From 40342a9e3690cb5b627a433d4d5cbf30e3c57698 Mon Sep 17 00:00:00 2001 From: Travis Bradshaw Date: Tue, 31 Jan 2012 15:06:54 -0600 Subject: [PATCH] The Wolfenstein: Enemy Territory sources as originally released under the GPL license on August 20, 2010. --- COPYING.txt | 643 + README.txt | 160 + etmain/ui/menudef.h | 473 + etmain/ui/menumacros.h | 1034 + src/SConscript.bspc | 144 + src/SConscript.cgame | 85 + src/SConscript.core | 332 + src/SConscript.curl | 49 + src/SConscript.game | 97 + src/SConscript.ui | 39 + src/SConstruct | 408 + src/SConstruct.sdk | 380 + src/botai/ai_cmd.c | 1540 + src/botai/ai_cmd.h | 40 + src/botai/ai_distances.h | 262 + src/botai/ai_dmgoal_mp.c | 3079 ++ src/botai/ai_dmgoal_mp.h | 46 + src/botai/ai_dmnet_mp.c | 6696 ++++ src/botai/ai_dmnet_mp.h | 102 + src/botai/ai_dmq3.c | 7572 ++++ src/botai/ai_dmq3.h | 313 + src/botai/ai_func_decs.h | 474 + src/botai/ai_funcs.h | 475 + src/botai/ai_main.c | 2670 ++ src/botai/ai_main.h | 941 + src/botai/ai_matrix.h | 60 + src/botai/ai_script.c | 1171 + src/botai/ai_script_actions.c | 2209 + src/botai/ai_team.c | 3183 ++ src/botai/ai_team.h | 134 + src/botai/botai.h | 110 + src/botai/chars.h | 159 + src/botai/inv.h | 138 + src/botai/match.h | 134 + src/botai/syn.h | 46 + src/botlib/aasfile.h | 285 + src/botlib/be_aas_bsp.h | 95 + src/botlib/be_aas_bspq3.c | 529 + src/botlib/be_aas_cluster.c | 1611 + src/botlib/be_aas_cluster.h | 42 + src/botlib/be_aas_debug.c | 688 + src/botlib/be_aas_debug.h | 68 + src/botlib/be_aas_def.h | 303 + src/botlib/be_aas_entity.c | 530 + src/botlib/be_aas_entity.h | 68 + src/botlib/be_aas_file.c | 681 + src/botlib/be_aas_file.h | 48 + src/botlib/be_aas_funcs.h | 56 + src/botlib/be_aas_main.c | 491 + src/botlib/be_aas_main.h | 69 + src/botlib/be_aas_move.c | 1026 + src/botlib/be_aas_move.h | 69 + src/botlib/be_aas_optimize.c | 657 + src/botlib/be_aas_optimize.h | 40 + src/botlib/be_aas_reach.c | 4634 +++ src/botlib/be_aas_reach.h | 80 + src/botlib/be_aas_route.c | 3704 ++ src/botlib/be_aas_route.h | 68 + src/botlib/be_aas_routealt.c | 294 + src/botlib/be_aas_routealt.h | 46 + src/botlib/be_aas_routetable.c | 1166 + src/botlib/be_aas_routetable.h | 171 + src/botlib/be_aas_sample.c | 1482 + src/botlib/be_aas_sample.h | 72 + src/botlib/be_ai_char.c | 767 + src/botlib/be_ai_chat.c | 2890 ++ src/botlib/be_ai_gen.c | 151 + src/botlib/be_ai_goal.c | 1634 + src/botlib/be_ai_move.c | 3962 ++ src/botlib/be_ai_weap.c | 555 + src/botlib/be_ai_weight.c | 972 + src/botlib/be_ai_weight.h | 89 + src/botlib/be_ea.c | 493 + src/botlib/be_interface.c | 878 + src/botlib/be_interface.h | 94 + src/botlib/botlib.vcproj | 444 + src/botlib/botlib_stub.c | 271 + src/botlib/l_common.h | 34 + src/botlib/l_crc.c | 155 + src/botlib/l_crc.h | 44 + src/botlib/l_libvar.c | 282 + src/botlib/l_libvar.h | 69 + src/botlib/l_log.c | 186 + src/botlib/l_log.h | 54 + src/botlib/l_memory.c | 446 + src/botlib/l_memory.h | 82 + src/botlib/l_precomp.c | 3262 ++ src/botlib/l_precomp.h | 167 + src/botlib/l_script.c | 1444 + src/botlib/l_script.h | 268 + src/botlib/l_struct.c | 507 + src/botlib/l_struct.h | 81 + src/botlib/l_utils.h | 43 + src/bspc/_files.c | 91 + src/bspc/aas_areamerging.c | 447 + src/bspc/aas_areamerging.h | 40 + src/bspc/aas_cfg.c | 310 + src/bspc/aas_cfg.h | 89 + src/bspc/aas_create.c | 1370 + src/bspc/aas_create.h | 153 + src/bspc/aas_edgemelting.c | 125 + src/bspc/aas_edgemelting.h | 40 + src/bspc/aas_facemerging.c | 312 + src/bspc/aas_facemerging.h | 39 + src/bspc/aas_file.c | 603 + src/bspc/aas_file.h | 40 + src/bspc/aas_gsubdiv.c | 687 + src/bspc/aas_gsubdiv.h | 40 + src/bspc/aas_map.c | 886 + src/bspc/aas_map.h | 38 + src/bspc/aas_prunenodes.c | 103 + src/bspc/aas_prunenodes.h | 39 + src/bspc/aas_store.c | 1124 + src/bspc/aas_store.h | 125 + src/bspc/be_aas_bspc.c | 315 + src/bspc/be_aas_bspc.h | 43 + src/bspc/brushbsp.c | 2619 ++ src/bspc/bspc.c | 1208 + src/bspc/bspc.vcproj | 630 + src/bspc/csg.c | 1061 + src/bspc/faces.c | 1002 + src/bspc/glfile.c | 157 + src/bspc/l_bsp_ent.c | 195 + src/bspc/l_bsp_ent.h | 65 + src/bspc/l_bsp_q1.c | 608 + src/bspc/l_bsp_q1.h | 282 + src/bspc/l_bsp_q2.c | 1138 + src/bspc/l_bsp_q2.h | 104 + src/bspc/l_bsp_q3.c | 873 + src/bspc/l_bsp_q3.h | 88 + src/bspc/l_bsp_sin.c | 1186 + src/bspc/l_bsp_sin.h | 113 + src/bspc/l_cmd.c | 1170 + src/bspc/l_cmd.h | 164 + src/bspc/l_log.c | 219 + src/bspc/l_log.h | 57 + src/bspc/l_math.c | 302 + src/bspc/l_math.h | 100 + src/bspc/l_mem.c | 468 + src/bspc/l_mem.h | 58 + src/bspc/l_poly.c | 1449 + src/bspc/l_poly.h | 136 + src/bspc/l_qfiles.c | 701 + src/bspc/l_qfiles.h | 106 + src/bspc/l_threads.c | 1528 + src/bspc/l_threads.h | 52 + src/bspc/l_utils.c | 262 + src/bspc/l_utils.h | 94 + src/bspc/leakfile.c | 108 + src/bspc/map.c | 1505 + src/bspc/map_q1.c | 1205 + src/bspc/map_q2.c | 1149 + src/bspc/map_q3.c | 679 + src/bspc/map_sin.c | 1201 + src/bspc/nodraw.c | 50 + src/bspc/portals.c | 1308 + src/bspc/prtfile.c | 287 + src/bspc/q2files.h | 497 + src/bspc/q3files.h | 382 + src/bspc/qbsp.h | 465 + src/bspc/qfiles.h | 495 + src/bspc/sinfiles.h | 372 + src/bspc/textures.c | 249 + src/bspc/tree.c | 298 + src/bspc/writebsp.c | 632 + src/cgame/cg_atmospheric.c | 952 + src/cgame/cg_character.c | 538 + src/cgame/cg_commandmap.c | 1593 + src/cgame/cg_consolecmds.c | 1176 + src/cgame/cg_debriefing.c | 2900 ++ src/cgame/cg_draw.c | 4641 +++ src/cgame/cg_drawtools.c | 1357 + src/cgame/cg_effects.c | 1671 + src/cgame/cg_ents.c | 2828 ++ src/cgame/cg_event.c | 2968 ++ src/cgame/cg_fireteamoverlay.c | 559 + src/cgame/cg_fireteams.c | 1237 + src/cgame/cg_flamethrower.c | 1294 + src/cgame/cg_info.c | 1044 + src/cgame/cg_limbopanel.c | 2903 ++ src/cgame/cg_loadpanel.c | 635 + src/cgame/cg_local.h | 3501 ++ src/cgame/cg_localents.c | 1383 + src/cgame/cg_main.c | 2893 ++ src/cgame/cg_marks.c | 386 + src/cgame/cg_missionbriefing.c | 401 + src/cgame/cg_multiview.c | 883 + src/cgame/cg_newDraw.c | 941 + src/cgame/cg_panelhandling.c | 310 + src/cgame/cg_particles.c | 2318 ++ src/cgame/cg_players.c | 3007 ++ src/cgame/cg_playerstate.c | 577 + src/cgame/cg_polybus.c | 93 + src/cgame/cg_popupmessages.c | 619 + src/cgame/cg_predict.c | 1140 + src/cgame/cg_public.h | 333 + src/cgame/cg_scoreboard.c | 768 + src/cgame/cg_servercmds.c | 2468 ++ src/cgame/cg_snapshot.c | 600 + src/cgame/cg_sound.c | 2079 + src/cgame/cg_spawn.c | 509 + src/cgame/cg_statsranksmedals.c | 85 + src/cgame/cg_syscalls.c | 891 + src/cgame/cg_trails.c | 821 + src/cgame/cg_view.c | 1992 + src/cgame/cg_weapons.c | 6164 +++ src/cgame/cg_window.c | 967 + src/cgame/cgame.def | 3 + src/cgame/cgame.vcproj | 506 + src/cgame/tr_types.h | 352 + src/client/cl_cgame.c | 1473 + src/client/cl_cin.c | 1816 + src/client/cl_console.c | 831 + src/client/cl_input.c | 1080 + src/client/cl_keys.c | 2040 + src/client/cl_main.c | 5224 +++ src/client/cl_net_chan.c | 204 + src/client/cl_parse.c | 923 + src/client/cl_scrn.c | 562 + src/client/cl_ui.c | 1310 + src/client/client.h | 706 + src/client/keys.h | 67 + src/client/snd_adpcm.c | 141 + src/client/snd_dma.c | 2824 ++ src/client/snd_local.h | 323 + src/client/snd_mem.c | 555 + src/client/snd_mix.c | 963 + src/client/snd_public.h | 115 + src/client/snd_wavelet.c | 276 + src/curl-7.12.2/CHANGES | 1634 + src/curl-7.12.2/COPYING | 21 + src/curl-7.12.2/Makefile.am | 116 + src/curl-7.12.2/Makefile.in | 760 + src/curl-7.12.2/README | 79 + src/curl-7.12.2/RELEASE-NOTES | 84 + src/curl-7.12.2/acinclude.m4 | 765 + src/curl-7.12.2/aclocal.m4 | 6876 ++++ src/curl-7.12.2/buildconf | 187 + src/curl-7.12.2/buildconf.bat | 14 + src/curl-7.12.2/config.guess | 1459 + src/curl-7.12.2/config.sub | 1549 + src/curl-7.12.2/configure | 33355 ++++++++++++++++ src/curl-7.12.2/configure.ac | 1479 + src/curl-7.12.2/curl-config.in | 133 + src/curl-7.12.2/curl-style.el | 51 + src/curl-7.12.2/depcomp | 520 + src/curl-7.12.2/docs/BINDINGS | 141 + src/curl-7.12.2/docs/BUGS | 81 + src/curl-7.12.2/docs/CONTRIBUTE | 159 + src/curl-7.12.2/docs/FAQ | 920 + src/curl-7.12.2/docs/FEATURES | 126 + src/curl-7.12.2/docs/HISTORY | 141 + src/curl-7.12.2/docs/INSTALL | 567 + src/curl-7.12.2/docs/INTERNALS | 381 + src/curl-7.12.2/docs/KNOWN_BUGS | 63 + src/curl-7.12.2/docs/LICENSE-MIXING | 86 + src/curl-7.12.2/docs/MANUAL | 882 + src/curl-7.12.2/docs/Makefile.am | 41 + src/curl-7.12.2/docs/Makefile.in | 580 + src/curl-7.12.2/docs/README.netware | 27 + src/curl-7.12.2/docs/README.win32 | 22 + src/curl-7.12.2/docs/RESOURCES | 72 + src/curl-7.12.2/docs/SSLCERTS | 92 + src/curl-7.12.2/docs/THANKS | 100 + src/curl-7.12.2/docs/TODO | 252 + src/curl-7.12.2/docs/TheArtOfHttpScripting | 399 + src/curl-7.12.2/docs/VERSIONS | 64 + src/curl-7.12.2/docs/curl-config.1 | 64 + src/curl-7.12.2/docs/curl-config.html | 82 + src/curl-7.12.2/docs/curl-config.pdf | Bin 0 -> 8389 bytes src/curl-7.12.2/docs/curl.1 | 1190 + src/curl-7.12.2/docs/curl.html | 613 + src/curl-7.12.2/docs/curl.pdf | Bin 0 -> 111968 bytes src/curl-7.12.2/docs/examples/Makefile.am | 15 + .../docs/examples/Makefile.example | 42 + src/curl-7.12.2/docs/examples/Makefile.in | 369 + src/curl-7.12.2/docs/examples/README | 25 + src/curl-7.12.2/docs/examples/curlgtk.c | 101 + src/curl-7.12.2/docs/examples/curlx.c | 510 + src/curl-7.12.2/docs/examples/debug.c | 127 + src/curl-7.12.2/docs/examples/fileupload.c | 64 + src/curl-7.12.2/docs/examples/fopen.c | 546 + src/curl-7.12.2/docs/examples/ftp3rdparty.c | 103 + src/curl-7.12.2/docs/examples/ftpget.c | 87 + src/curl-7.12.2/docs/examples/ftpgetresp.c | 59 + src/curl-7.12.2/docs/examples/ftpupload.c | 90 + src/curl-7.12.2/docs/examples/getinfo.c | 37 + src/curl-7.12.2/docs/examples/getinmemory.c | 85 + src/curl-7.12.2/docs/examples/http-post.c | 34 + src/curl-7.12.2/docs/examples/httpput.c | 100 + src/curl-7.12.2/docs/examples/https.c | 52 + src/curl-7.12.2/docs/examples/makefile.dj | 33 + src/curl-7.12.2/docs/examples/multi-app.c | 127 + .../docs/examples/multi-debugcallback.c | 177 + src/curl-7.12.2/docs/examples/multi-double.c | 94 + src/curl-7.12.2/docs/examples/multi-post.c | 125 + src/curl-7.12.2/docs/examples/multi-single.c | 89 + src/curl-7.12.2/docs/examples/multithread.c | 69 + src/curl-7.12.2/docs/examples/persistant.c | 40 + src/curl-7.12.2/docs/examples/post-callback.c | 116 + src/curl-7.12.2/docs/examples/postit2.c | 87 + src/curl-7.12.2/docs/examples/sepheaders.c | 77 + src/curl-7.12.2/docs/examples/simple.c | 27 + src/curl-7.12.2/docs/examples/simplepost.c | 35 + src/curl-7.12.2/docs/examples/simplessl.c | 121 + src/curl-7.12.2/docs/index.html | 20 + src/curl-7.12.2/docs/libcurl/Makefile.am | 73 + src/curl-7.12.2/docs/libcurl/Makefile.in | 475 + .../docs/libcurl/curl_easy_cleanup.3 | 28 + .../docs/libcurl/curl_easy_cleanup.html | 58 + .../docs/libcurl/curl_easy_cleanup.pdf | Bin 0 -> 5590 bytes .../docs/libcurl/curl_easy_duphandle.3 | 34 + .../docs/libcurl/curl_easy_duphandle.html | 59 + .../docs/libcurl/curl_easy_duphandle.pdf | Bin 0 -> 5834 bytes .../docs/libcurl/curl_easy_getinfo.3 | 120 + .../docs/libcurl/curl_easy_getinfo.html | 105 + .../docs/libcurl/curl_easy_getinfo.pdf | Bin 0 -> 16544 bytes src/curl-7.12.2/docs/libcurl/curl_easy_init.3 | 21 + .../docs/libcurl/curl_easy_init.html | 56 + .../docs/libcurl/curl_easy_init.pdf | Bin 0 -> 5371 bytes .../docs/libcurl/curl_easy_perform.3 | 40 + .../docs/libcurl/curl_easy_perform.html | 58 + .../docs/libcurl/curl_easy_perform.pdf | Bin 0 -> 6200 bytes .../docs/libcurl/curl_easy_reset.3 | 24 + .../docs/libcurl/curl_easy_reset.html | 58 + .../docs/libcurl/curl_easy_reset.pdf | 146 + .../docs/libcurl/curl_easy_setopt.3 | 1042 + .../docs/libcurl/curl_easy_setopt.html | 436 + .../docs/libcurl/curl_easy_setopt.pdf | Bin 0 -> 120552 bytes .../docs/libcurl/curl_easy_strerror.3 | 20 + .../docs/libcurl/curl_easy_strerror.html | 58 + .../docs/libcurl/curl_easy_strerror.pdf | Bin 0 -> 5212 bytes src/curl-7.12.2/docs/libcurl/curl_escape.3 | 26 + src/curl-7.12.2/docs/libcurl/curl_escape.html | 57 + src/curl-7.12.2/docs/libcurl/curl_escape.pdf | Bin 0 -> 5643 bytes src/curl-7.12.2/docs/libcurl/curl_formadd.3 | 211 + .../docs/libcurl/curl_formadd.html | 142 + src/curl-7.12.2/docs/libcurl/curl_formadd.pdf | Bin 0 -> 20585 bytes src/curl-7.12.2/docs/libcurl/curl_formfree.3 | 20 + .../docs/libcurl/curl_formfree.html | 55 + .../docs/libcurl/curl_formfree.pdf | Bin 0 -> 5238 bytes src/curl-7.12.2/docs/libcurl/curl_free.3 | 18 + src/curl-7.12.2/docs/libcurl/curl_free.html | 54 + src/curl-7.12.2/docs/libcurl/curl_free.pdf | Bin 0 -> 5112 bytes src/curl-7.12.2/docs/libcurl/curl_getdate.3 | 92 + .../docs/libcurl/curl_getdate.html | 90 + src/curl-7.12.2/docs/libcurl/curl_getdate.pdf | Bin 0 -> 10933 bytes src/curl-7.12.2/docs/libcurl/curl_getenv.3 | 31 + src/curl-7.12.2/docs/libcurl/curl_getenv.html | 57 + src/curl-7.12.2/docs/libcurl/curl_getenv.pdf | Bin 0 -> 6101 bytes .../docs/libcurl/curl_global_cleanup.3 | 23 + .../docs/libcurl/curl_global_cleanup.html | 57 + .../docs/libcurl/curl_global_cleanup.pdf | Bin 0 -> 5192 bytes .../docs/libcurl/curl_global_init.3 | 46 + .../docs/libcurl/curl_global_init.html | 65 + .../docs/libcurl/curl_global_init.pdf | Bin 0 -> 6106 bytes .../docs/libcurl/curl_global_init_mem.3 | 42 + .../docs/libcurl/curl_global_init_mem.html | 74 + .../docs/libcurl/curl_global_init_mem.pdf | Bin 0 -> 5877 bytes src/curl-7.12.2/docs/libcurl/curl_mprintf.3 | 90 + .../docs/libcurl/curl_mprintf.html | 70 + src/curl-7.12.2/docs/libcurl/curl_mprintf.pdf | Bin 0 -> 10232 bytes .../docs/libcurl/curl_multi_add_handle.3 | 31 + .../docs/libcurl/curl_multi_add_handle.html | 60 + .../docs/libcurl/curl_multi_add_handle.pdf | Bin 0 -> 5658 bytes .../docs/libcurl/curl_multi_cleanup.3 | 27 + .../docs/libcurl/curl_multi_cleanup.html | 58 + .../docs/libcurl/curl_multi_cleanup.pdf | Bin 0 -> 5591 bytes .../docs/libcurl/curl_multi_fdset.3 | 35 + .../docs/libcurl/curl_multi_fdset.html | 63 + .../docs/libcurl/curl_multi_fdset.pdf | Bin 0 -> 5970 bytes .../docs/libcurl/curl_multi_info_read.3 | 45 + .../docs/libcurl/curl_multi_info_read.html | 69 + .../docs/libcurl/curl_multi_info_read.pdf | Bin 0 -> 6214 bytes .../docs/libcurl/curl_multi_init.3 | 21 + .../docs/libcurl/curl_multi_init.html | 56 + .../docs/libcurl/curl_multi_init.pdf | Bin 0 -> 5250 bytes .../docs/libcurl/curl_multi_perform.3 | 45 + .../docs/libcurl/curl_multi_perform.html | 59 + .../docs/libcurl/curl_multi_perform.pdf | Bin 0 -> 6697 bytes .../docs/libcurl/curl_multi_remove_handle.3 | 23 + .../libcurl/curl_multi_remove_handle.html | 57 + .../docs/libcurl/curl_multi_remove_handle.pdf | Bin 0 -> 5615 bytes .../docs/libcurl/curl_multi_strerror.3 | 20 + .../docs/libcurl/curl_multi_strerror.html | 58 + .../docs/libcurl/curl_multi_strerror.pdf | 143 + .../docs/libcurl/curl_share_cleanup.3 | 21 + .../docs/libcurl/curl_share_cleanup.html | 56 + .../docs/libcurl/curl_share_cleanup.pdf | Bin 0 -> 5442 bytes .../docs/libcurl/curl_share_init.3 | 25 + .../docs/libcurl/curl_share_init.html | 57 + .../docs/libcurl/curl_share_init.pdf | Bin 0 -> 5625 bytes .../docs/libcurl/curl_share_setopt.3 | 59 + .../docs/libcurl/curl_share_setopt.html | 79 + .../docs/libcurl/curl_share_setopt.pdf | Bin 0 -> 6400 bytes .../docs/libcurl/curl_share_strerror.3 | 20 + .../docs/libcurl/curl_share_strerror.html | 58 + .../docs/libcurl/curl_share_strerror.pdf | Bin 0 -> 5216 bytes .../docs/libcurl/curl_slist_append.3 | 37 + .../docs/libcurl/curl_slist_append.html | 60 + .../docs/libcurl/curl_slist_append.pdf | Bin 0 -> 5626 bytes .../docs/libcurl/curl_slist_free_all.3 | 20 + .../docs/libcurl/curl_slist_free_all.html | 56 + .../docs/libcurl/curl_slist_free_all.pdf | Bin 0 -> 5087 bytes src/curl-7.12.2/docs/libcurl/curl_strequal.3 | 32 + .../docs/libcurl/curl_strequal.html | 58 + .../docs/libcurl/curl_strequal.pdf | Bin 0 -> 5934 bytes src/curl-7.12.2/docs/libcurl/curl_unescape.3 | 26 + .../docs/libcurl/curl_unescape.html | 57 + .../docs/libcurl/curl_unescape.pdf | 144 + src/curl-7.12.2/docs/libcurl/curl_version.3 | 19 + .../docs/libcurl/curl_version.html | 55 + src/curl-7.12.2/docs/libcurl/curl_version.pdf | 116 + .../docs/libcurl/curl_version_info.3 | 116 + .../docs/libcurl/curl_version_info.html | 112 + .../docs/libcurl/curl_version_info.pdf | Bin 0 -> 12546 bytes src/curl-7.12.2/docs/libcurl/index.html | 59 + src/curl-7.12.2/docs/libcurl/libcurl-easy.3 | 28 + .../docs/libcurl/libcurl-easy.html | 54 + src/curl-7.12.2/docs/libcurl/libcurl-easy.pdf | Bin 0 -> 6034 bytes src/curl-7.12.2/docs/libcurl/libcurl-errors.3 | 220 + .../docs/libcurl/libcurl-errors.html | 207 + .../docs/libcurl/libcurl-errors.pdf | Bin 0 -> 27311 bytes src/curl-7.12.2/docs/libcurl/libcurl-multi.3 | 95 + .../docs/libcurl/libcurl-multi.html | 66 + .../docs/libcurl/libcurl-multi.pdf | Bin 0 -> 13625 bytes src/curl-7.12.2/docs/libcurl/libcurl-share.3 | 46 + .../docs/libcurl/libcurl-share.html | 61 + .../docs/libcurl/libcurl-share.pdf | Bin 0 -> 6599 bytes .../docs/libcurl/libcurl-tutorial.3 | 1170 + .../docs/libcurl/libcurl-tutorial.html | 495 + .../docs/libcurl/libcurl-tutorial.pdf | Bin 0 -> 97815 bytes src/curl-7.12.2/docs/libcurl/libcurl.3 | 105 + src/curl-7.12.2/docs/libcurl/libcurl.html | 87 + src/curl-7.12.2/docs/libcurl/libcurl.pdf | Bin 0 -> 13637 bytes src/curl-7.12.2/include/Makefile.am | 5 + src/curl-7.12.2/include/Makefile.in | 496 + src/curl-7.12.2/include/README | 38 + src/curl-7.12.2/include/curl/Makefile.am | 5 + src/curl-7.12.2/include/curl/Makefile.in | 427 + src/curl-7.12.2/include/curl/curl.h | 1362 + src/curl-7.12.2/include/curl/curlver.h | 55 + src/curl-7.12.2/include/curl/easy.h | 81 + src/curl-7.12.2/include/curl/mprintf.h | 54 + src/curl-7.12.2/include/curl/multi.h | 225 + src/curl-7.12.2/include/curl/stdcheaders.h | 34 + src/curl-7.12.2/include/curl/types.h | 1 + src/curl-7.12.2/install-sh | 250 + src/curl-7.12.2/lib/Makefile.am | 142 + src/curl-7.12.2/lib/Makefile.b32 | 85 + src/curl-7.12.2/lib/Makefile.in | 750 + src/curl-7.12.2/lib/Makefile.inc | 19 + src/curl-7.12.2/lib/Makefile.m32 | 86 + src/curl-7.12.2/lib/Makefile.netware | 383 + src/curl-7.12.2/lib/Makefile.riscos | 131 + src/curl-7.12.2/lib/Makefile.vc6 | 309 + src/curl-7.12.2/lib/README.ares | 73 + src/curl-7.12.2/lib/README.curlx | 62 + src/curl-7.12.2/lib/README.encoding | 60 + src/curl-7.12.2/lib/README.memoryleak | 56 + src/curl-7.12.2/lib/amigaos.c | 49 + src/curl-7.12.2/lib/amigaos.h | 52 + src/curl-7.12.2/lib/arpa_telnet.h | 101 + src/curl-7.12.2/lib/base64.c | 287 + src/curl-7.12.2/lib/base64.h | 27 + src/curl-7.12.2/lib/ca-bundle.crt | 4371 ++ src/curl-7.12.2/lib/ca-bundle.h | 2 + src/curl-7.12.2/lib/config-amigaos.h | 84 + src/curl-7.12.2/lib/config-mac.h | 45 + src/curl-7.12.2/lib/config-riscos.h | 393 + src/curl-7.12.2/lib/config-vms.h | 382 + src/curl-7.12.2/lib/config-win32.h | 244 + src/curl-7.12.2/lib/config.dj | 111 + src/curl-7.12.2/lib/config.h.in | 530 + src/curl-7.12.2/lib/connect.c | 794 + src/curl-7.12.2/lib/connect.h | 44 + src/curl-7.12.2/lib/content_encoding.c | 371 + src/curl-7.12.2/lib/content_encoding.h | 41 + src/curl-7.12.2/lib/cookie.c | 900 + src/curl-7.12.2/lib/cookie.h | 95 + src/curl-7.12.2/lib/curlx.h | 95 + src/curl-7.12.2/lib/dict.c | 224 + src/curl-7.12.2/lib/dict.h | 30 + src/curl-7.12.2/lib/easy.c | 592 + src/curl-7.12.2/lib/escape.c | 133 + src/curl-7.12.2/lib/escape.h | 32 + src/curl-7.12.2/lib/file.c | 409 + src/curl-7.12.2/lib/file.h | 31 + src/curl-7.12.2/lib/formdata.c | 1602 + src/curl-7.12.2/lib/formdata.h | 92 + src/curl-7.12.2/lib/ftp.c | 2886 ++ src/curl-7.12.2/lib/ftp.h | 37 + src/curl-7.12.2/lib/getenv.c | 70 + src/curl-7.12.2/lib/getinfo.c | 179 + src/curl-7.12.2/lib/getinfo.h | 28 + src/curl-7.12.2/lib/hash.c | 255 + src/curl-7.12.2/lib/hash.h | 60 + src/curl-7.12.2/lib/hostares.c | 300 + src/curl-7.12.2/lib/hostasyn.c | 174 + src/curl-7.12.2/lib/hostip.c | 530 + src/curl-7.12.2/lib/hostip.h | 252 + src/curl-7.12.2/lib/hostip4.c | 450 + src/curl-7.12.2/lib/hostip6.c | 269 + src/curl-7.12.2/lib/hostsyn.c | 146 + src/curl-7.12.2/lib/hostthre.c | 649 + src/curl-7.12.2/lib/http.c | 2068 + src/curl-7.12.2/lib/http.h | 61 + src/curl-7.12.2/lib/http_chunks.c | 264 + src/curl-7.12.2/lib/http_chunks.h | 88 + src/curl-7.12.2/lib/http_digest.c | 497 + src/curl-7.12.2/lib/http_digest.h | 53 + src/curl-7.12.2/lib/http_negotiate.c | 335 + src/curl-7.12.2/lib/http_negotiate.h | 39 + src/curl-7.12.2/lib/http_ntlm.c | 585 + src/curl-7.12.2/lib/http_ntlm.h | 143 + src/curl-7.12.2/lib/if2ip.c | 135 + src/curl-7.12.2/lib/if2ip.h | 69 + src/curl-7.12.2/lib/inet_ntoa_r.h | 9 + src/curl-7.12.2/lib/inet_ntop.c | 194 + src/curl-7.12.2/lib/inet_ntop.h | 37 + src/curl-7.12.2/lib/inet_pton.c | 251 + src/curl-7.12.2/lib/inet_pton.h | 37 + src/curl-7.12.2/lib/krb4.c | 413 + src/curl-7.12.2/lib/krb4.h | 27 + src/curl-7.12.2/lib/ldap.c | 647 + src/curl-7.12.2/lib/ldap.h | 29 + src/curl-7.12.2/lib/libcurl.def | 52 + src/curl-7.12.2/lib/libcurl.framework.make | 108 + src/curl-7.12.2/lib/libcurl.imp | 47 + src/curl-7.12.2/lib/libcurl.plist | 35 + src/curl-7.12.2/lib/libcurl.rc | 41 + src/curl-7.12.2/lib/llist.c | 128 + src/curl-7.12.2/lib/llist.h | 56 + src/curl-7.12.2/lib/makefile.amiga | 27 + src/curl-7.12.2/lib/makefile.dj | 228 + src/curl-7.12.2/lib/md5.c | 342 + src/curl-7.12.2/lib/md5.h | 29 + src/curl-7.12.2/lib/memdebug.c | 301 + src/curl-7.12.2/lib/memdebug.h | 108 + src/curl-7.12.2/lib/memory.h | 50 + src/curl-7.12.2/lib/mprintf.c | 1236 + src/curl-7.12.2/lib/msvcproj.foot | 7 + src/curl-7.12.2/lib/msvcproj.head | 92 + src/curl-7.12.2/lib/multi.c | 660 + src/curl-7.12.2/lib/netrc.c | 247 + src/curl-7.12.2/lib/netrc.h | 34 + src/curl-7.12.2/lib/nwlib.c | 299 + src/curl-7.12.2/lib/parsedate.c | 401 + src/curl-7.12.2/lib/progress.c | 419 + src/curl-7.12.2/lib/progress.h | 70 + src/curl-7.12.2/lib/security.c | 491 + src/curl-7.12.2/lib/security.h | 72 + src/curl-7.12.2/lib/sendf.c | 500 + src/curl-7.12.2/lib/sendf.h | 56 + src/curl-7.12.2/lib/setup.h | 284 + src/curl-7.12.2/lib/share.c | 227 + src/curl-7.12.2/lib/share.h | 55 + src/curl-7.12.2/lib/speedcheck.c | 64 + src/curl-7.12.2/lib/speedcheck.h | 34 + src/curl-7.12.2/lib/ssluse.c | 1494 + src/curl-7.12.2/lib/ssluse.h | 38 + src/curl-7.12.2/lib/strequal.c | 139 + src/curl-7.12.2/lib/strequal.h | 47 + src/curl-7.12.2/lib/strerror.c | 629 + src/curl-7.12.2/lib/strerror.h | 34 + src/curl-7.12.2/lib/strtok.c | 68 + src/curl-7.12.2/lib/strtok.h | 38 + src/curl-7.12.2/lib/strtoofft.c | 151 + src/curl-7.12.2/lib/strtoofft.h | 73 + src/curl-7.12.2/lib/telnet.c | 1357 + src/curl-7.12.2/lib/telnet.h | 30 + src/curl-7.12.2/lib/timeval.c | 110 + src/curl-7.12.2/lib/timeval.h | 74 + src/curl-7.12.2/lib/transfer.c | 2251 ++ src/curl-7.12.2/lib/transfer.h | 52 + src/curl-7.12.2/lib/url.c | 3801 ++ src/curl-7.12.2/lib/url.h | 47 + src/curl-7.12.2/lib/urldata.h | 959 + src/curl-7.12.2/lib/version.c | 257 + src/curl-7.12.2/ltmain.sh | 6422 +++ src/curl-7.12.2/maketgz | 137 + src/curl-7.12.2/missing | 283 + src/curl-7.12.2/mkinstalldirs | 40 + src/curl-7.12.2/packages/DOS/Makefile.am | 1 + src/curl-7.12.2/packages/DOS/Makefile.in | 353 + src/curl-7.12.2/packages/DOS/README | 11 + src/curl-7.12.2/packages/DOS/common.dj | 92 + src/curl-7.12.2/packages/EPM/Makefile.am | 3 + src/curl-7.12.2/packages/EPM/Makefile.in | 357 + src/curl-7.12.2/packages/EPM/README | 12 + src/curl-7.12.2/packages/EPM/curl.list.in | 58 + src/curl-7.12.2/packages/Linux/Makefile.am | 1 + src/curl-7.12.2/packages/Linux/Makefile.in | 492 + .../packages/Linux/RPM/Makefile.am | 2 + .../packages/Linux/RPM/Makefile.in | 358 + src/curl-7.12.2/packages/Linux/RPM/README | 5 + .../packages/Linux/RPM/curl-ssl.spec.in | 84 + .../packages/Linux/RPM/curl.spec.in | 84 + .../packages/Linux/RPM/make_curl_rpm | 62 + src/curl-7.12.2/packages/Makefile.am | 3 + src/curl-7.12.2/packages/Makefile.in | 494 + src/curl-7.12.2/packages/NetWare/get_ver.awk | 68 + src/curl-7.12.2/packages/README | 27 + src/curl-7.12.2/packages/Solaris/Makefile.am | 39 + src/curl-7.12.2/packages/Solaris/Makefile.in | 387 + src/curl-7.12.2/packages/Win32/Makefile.am | 3 + src/curl-7.12.2/packages/Win32/Makefile.in | 493 + src/curl-7.12.2/packages/Win32/README | 53 + .../packages/Win32/cygwin/Makefile.am | 62 + .../packages/Win32/cygwin/Makefile.in | 414 + src/curl-7.12.2/packages/Win32/cygwin/README | 114 + src/curl-7.12.2/packages/vms/Makefile.am | 5 + src/curl-7.12.2/packages/vms/Makefile.in | 359 + src/curl-7.12.2/packages/vms/axp/README | 1 + .../packages/vms/batch_compile.com | 7 + src/curl-7.12.2/packages/vms/build_vms.com | 227 + .../packages/vms/config-vms.h_with_ssl | 264 + .../packages/vms/config-vms.h_without_ssl | 264 + src/curl-7.12.2/packages/vms/curlmsg.h | 92 + src/curl-7.12.2/packages/vms/curlmsg.msg | 87 + src/curl-7.12.2/packages/vms/curlmsg.sdl | 90 + src/curl-7.12.2/packages/vms/curlmsg_vms.h | 91 + src/curl-7.12.2/packages/vms/defines.com | 65 + src/curl-7.12.2/packages/vms/hpssl_alpha.opt | 2 + src/curl-7.12.2/packages/vms/hpssl_ia64.opt | 2 + src/curl-7.12.2/packages/vms/hpssl_vax.opt | 2 + src/curl-7.12.2/packages/vms/ia64/README | 1 + src/curl-7.12.2/packages/vms/readme | 66 + src/curl-7.12.2/packages/vms/vax/README | 1 + src/curl-7.12.2/reconf | 15 + src/curl-7.12.2/sample.emacs | 46 + src/curl-7.12.2/src/Makefile.am | 78 + src/curl-7.12.2/src/Makefile.b32 | 92 + src/curl-7.12.2/src/Makefile.in | 654 + src/curl-7.12.2/src/Makefile.inc | 16 + src/curl-7.12.2/src/Makefile.m32 | 85 + src/curl-7.12.2/src/Makefile.netware | 354 + src/curl-7.12.2/src/Makefile.riscos | 48 + src/curl-7.12.2/src/Makefile.vc6 | 173 + src/curl-7.12.2/src/config-amigaos.h | 34 + src/curl-7.12.2/src/config-mac.h | 15 + src/curl-7.12.2/src/config-riscos.h | 393 + src/curl-7.12.2/src/config-vms.h | 24 + src/curl-7.12.2/src/config-win32.h | 40 + src/curl-7.12.2/src/config.h.in | 105 + src/curl-7.12.2/src/curl.rc | 63 + src/curl-7.12.2/src/getpass.c | 244 + src/curl-7.12.2/src/getpass.h | 35 + src/curl-7.12.2/src/homedir.c | 116 + src/curl-7.12.2/src/homedir.h | 28 + src/curl-7.12.2/src/hugehelp.h | 26 + src/curl-7.12.2/src/macos/MACINSTALL.TXT | 30 + .../src/macos/curl.mcp.xml.sit.hqx | 1 + .../src/macos/src/curl_GUSIConfig.cpp | 62 + src/curl-7.12.2/src/macos/src/macos_main.cpp | 33 + src/curl-7.12.2/src/main.c | 3935 ++ src/curl-7.12.2/src/makefile.amiga | 29 + src/curl-7.12.2/src/makefile.dj | 71 + src/curl-7.12.2/src/mkhelp.pl | 226 + src/curl-7.12.2/src/setup.h | 121 + src/curl-7.12.2/src/urlglob.c | 509 + src/curl-7.12.2/src/urlglob.h | 66 + src/curl-7.12.2/src/version.h | 35 + src/curl-7.12.2/src/writeenv.c | 115 + src/curl-7.12.2/src/writeenv.h | 28 + src/curl-7.12.2/src/writeout.c | 236 + src/curl-7.12.2/src/writeout.h | 28 + src/curl-7.12.2/tests/FILEFORMAT | 175 + src/curl-7.12.2/tests/Makefile.am | 58 + src/curl-7.12.2/tests/Makefile.in | 545 + src/curl-7.12.2/tests/README | 88 + src/curl-7.12.2/tests/data/Makefile.am | 33 + src/curl-7.12.2/tests/data/Makefile.in | 384 + src/curl-7.12.2/tests/data/test1 | 47 + src/curl-7.12.2/tests/data/test10 | 60 + src/curl-7.12.2/tests/data/test100 | 48 + src/curl-7.12.2/tests/data/test101 | 48 + src/curl-7.12.2/tests/data/test102 | 43 + src/curl-7.12.2/tests/data/test103 | 45 + src/curl-7.12.2/tests/data/test104 | 35 + src/curl-7.12.2/tests/data/test105 | 43 + src/curl-7.12.2/tests/data/test106 | 40 + src/curl-7.12.2/tests/data/test107 | 42 + src/curl-7.12.2/tests/data/test108 | 47 + src/curl-7.12.2/tests/data/test109 | 41 + src/curl-7.12.2/tests/data/test11 | 67 + src/curl-7.12.2/tests/data/test110 | 44 + src/curl-7.12.2/tests/data/test111 | 37 + src/curl-7.12.2/tests/data/test112 | 41 + src/curl-7.12.2/tests/data/test113 | 29 + src/curl-7.12.2/tests/data/test114 | 30 + src/curl-7.12.2/tests/data/test115 | 35 + src/curl-7.12.2/tests/data/test116 | 37 + src/curl-7.12.2/tests/data/test117 | 36 + src/curl-7.12.2/tests/data/test118 | 38 + src/curl-7.12.2/tests/data/test119 | 40 + src/curl-7.12.2/tests/data/test12 | 47 + src/curl-7.12.2/tests/data/test120 | 43 + src/curl-7.12.2/tests/data/test121 | 40 + src/curl-7.12.2/tests/data/test122 | 36 + src/curl-7.12.2/tests/data/test123 | 31 + src/curl-7.12.2/tests/data/test124 | 38 + src/curl-7.12.2/tests/data/test125 | 33 + src/curl-7.12.2/tests/data/test126 | 38 + src/curl-7.12.2/tests/data/test127 | 36 + src/curl-7.12.2/tests/data/test128 | 47 + src/curl-7.12.2/tests/data/test13 | 37 + src/curl-7.12.2/tests/data/test130 | 56 + src/curl-7.12.2/tests/data/test131 | 56 + src/curl-7.12.2/tests/data/test132 | 56 + src/curl-7.12.2/tests/data/test133 | 56 + src/curl-7.12.2/tests/data/test134 | 56 + src/curl-7.12.2/tests/data/test135 | 43 + src/curl-7.12.2/tests/data/test136 | 33 + src/curl-7.12.2/tests/data/test137 | 38 + src/curl-7.12.2/tests/data/test138 | 41 + src/curl-7.12.2/tests/data/test139 | 38 + src/curl-7.12.2/tests/data/test14 | 37 + src/curl-7.12.2/tests/data/test140 | 33 + src/curl-7.12.2/tests/data/test141 | 39 + src/curl-7.12.2/tests/data/test142 | 183 + src/curl-7.12.2/tests/data/test143 | 35 + src/curl-7.12.2/tests/data/test144 | 40 + src/curl-7.12.2/tests/data/test145 | 43 + src/curl-7.12.2/tests/data/test146 | 47 + src/curl-7.12.2/tests/data/test147 | 49 + src/curl-7.12.2/tests/data/test148 | 42 + src/curl-7.12.2/tests/data/test149 | 47 + src/curl-7.12.2/tests/data/test15 | 49 + src/curl-7.12.2/tests/data/test150 | 82 + src/curl-7.12.2/tests/data/test151 | 40 + src/curl-7.12.2/tests/data/test152 | 43 + src/curl-7.12.2/tests/data/test153 | 122 + src/curl-7.12.2/tests/data/test154 | 94 + src/curl-7.12.2/tests/data/test155 | 118 + src/curl-7.12.2/tests/data/test156 | 79 + src/curl-7.12.2/tests/data/test157 | 38 + src/curl-7.12.2/tests/data/test158 | 49 + src/curl-7.12.2/tests/data/test159 | 82 + src/curl-7.12.2/tests/data/test16 | 41 + src/curl-7.12.2/tests/data/test160 | 66 + src/curl-7.12.2/tests/data/test161 | 39 + src/curl-7.12.2/tests/data/test162 | 49 + src/curl-7.12.2/tests/data/test163 | 71 + src/curl-7.12.2/tests/data/test164 | 59 + src/curl-7.12.2/tests/data/test165 | 47 + src/curl-7.12.2/tests/data/test166 | 52 + src/curl-7.12.2/tests/data/test167 | 62 + src/curl-7.12.2/tests/data/test168 | 82 + src/curl-7.12.2/tests/data/test169 | 107 + src/curl-7.12.2/tests/data/test17 | 46 + src/curl-7.12.2/tests/data/test170 | 39 + src/curl-7.12.2/tests/data/test171 | 47 + src/curl-7.12.2/tests/data/test172 | 47 + src/curl-7.12.2/tests/data/test173 | 71 + src/curl-7.12.2/tests/data/test174 | 71 + src/curl-7.12.2/tests/data/test175 | 71 + src/curl-7.12.2/tests/data/test176 | 76 + src/curl-7.12.2/tests/data/test177 | 39 + src/curl-7.12.2/tests/data/test178 | 42 + src/curl-7.12.2/tests/data/test179 | 46 + src/curl-7.12.2/tests/data/test18 | 84 + src/curl-7.12.2/tests/data/test180 | 58 + src/curl-7.12.2/tests/data/test181 | 59 + src/curl-7.12.2/tests/data/test182 | 35 + src/curl-7.12.2/tests/data/test183 | 48 + src/curl-7.12.2/tests/data/test184 | 67 + src/curl-7.12.2/tests/data/test185 | 67 + src/curl-7.12.2/tests/data/test186 | 54 + src/curl-7.12.2/tests/data/test187 | 67 + src/curl-7.12.2/tests/data/test188 | 68 + src/curl-7.12.2/tests/data/test189 | 69 + src/curl-7.12.2/tests/data/test19 | 25 + src/curl-7.12.2/tests/data/test190 | 36 + src/curl-7.12.2/tests/data/test191 | 33 + src/curl-7.12.2/tests/data/test2 | 45 + src/curl-7.12.2/tests/data/test20 | 25 + src/curl-7.12.2/tests/data/test200 | 34 + src/curl-7.12.2/tests/data/test201 | 25 + src/curl-7.12.2/tests/data/test202 | 29 + src/curl-7.12.2/tests/data/test203 | 34 + src/curl-7.12.2/tests/data/test204 | 32 + src/curl-7.12.2/tests/data/test205 | 29 + src/curl-7.12.2/tests/data/test21 | 25 + src/curl-7.12.2/tests/data/test22 | 38 + src/curl-7.12.2/tests/data/test23 | 25 + src/curl-7.12.2/tests/data/test24 | 40 + src/curl-7.12.2/tests/data/test25 | 112 + src/curl-7.12.2/tests/data/test26 | 38 + src/curl-7.12.2/tests/data/test27 | 49 + src/curl-7.12.2/tests/data/test28 | 67 + src/curl-7.12.2/tests/data/test29 | 43 + src/curl-7.12.2/tests/data/test3 | 52 + src/curl-7.12.2/tests/data/test30 | 35 + src/curl-7.12.2/tests/data/test300 | 41 + src/curl-7.12.2/tests/data/test301 | 45 + src/curl-7.12.2/tests/data/test302 | 35 + src/curl-7.12.2/tests/data/test303 | 43 + src/curl-7.12.2/tests/data/test304 | 61 + src/curl-7.12.2/tests/data/test305 | 30 + src/curl-7.12.2/tests/data/test306 | 54 + src/curl-7.12.2/tests/data/test31 | 59 + src/curl-7.12.2/tests/data/test32 | 48 + src/curl-7.12.2/tests/data/test33 | 55 + src/curl-7.12.2/tests/data/test34 | 58 + src/curl-7.12.2/tests/data/test36 | 57 + src/curl-7.12.2/tests/data/test37 | 39 + src/curl-7.12.2/tests/data/test38 | 52 + src/curl-7.12.2/tests/data/test39 | 76 + src/curl-7.12.2/tests/data/test4 | 42 + src/curl-7.12.2/tests/data/test40 | 67 + src/curl-7.12.2/tests/data/test41 | 23 + src/curl-7.12.2/tests/data/test42 | 67 + src/curl-7.12.2/tests/data/test43 | 67 + src/curl-7.12.2/tests/data/test44 | 64 + src/curl-7.12.2/tests/data/test45 | 67 + src/curl-7.12.2/tests/data/test46 | 71 + src/curl-7.12.2/tests/data/test47 | 41 + src/curl-7.12.2/tests/data/test48 | 39 + src/curl-7.12.2/tests/data/test49 | 67 + src/curl-7.12.2/tests/data/test5 | 41 + src/curl-7.12.2/tests/data/test50 | 67 + src/curl-7.12.2/tests/data/test500 | 48 + src/curl-7.12.2/tests/data/test501 | 30 + src/curl-7.12.2/tests/data/test502 | 40 + src/curl-7.12.2/tests/data/test503 | 61 + src/curl-7.12.2/tests/data/test504 | 28 + src/curl-7.12.2/tests/data/test505 | 58 + src/curl-7.12.2/tests/data/test506 | 159 + src/curl-7.12.2/tests/data/test507 | 26 + src/curl-7.12.2/tests/data/test508 | 46 + src/curl-7.12.2/tests/data/test509 | 50 + src/curl-7.12.2/tests/data/test51 | 67 + src/curl-7.12.2/tests/data/test510 | 55 + src/curl-7.12.2/tests/data/test511 | 35 + src/curl-7.12.2/tests/data/test512 | 45 + src/curl-7.12.2/tests/data/test513 | 40 + src/curl-7.12.2/tests/data/test514 | 49 + src/curl-7.12.2/tests/data/test52 | 67 + src/curl-7.12.2/tests/data/test53 | 46 + src/curl-7.12.2/tests/data/test54 | 37 + src/curl-7.12.2/tests/data/test55 | 59 + src/curl-7.12.2/tests/data/test56 | 53 + src/curl-7.12.2/tests/data/test57 | 39 + src/curl-7.12.2/tests/data/test58 | 44 + src/curl-7.12.2/tests/data/test59 | 40 + src/curl-7.12.2/tests/data/test6 | 40 + src/curl-7.12.2/tests/data/test60 | 49 + src/curl-7.12.2/tests/data/test61 | 54 + src/curl-7.12.2/tests/data/test62 | 49 + src/curl-7.12.2/tests/data/test63 | 41 + src/curl-7.12.2/tests/data/test64 | 69 + src/curl-7.12.2/tests/data/test65 | 73 + src/curl-7.12.2/tests/data/test66 | 34 + src/curl-7.12.2/tests/data/test67 | 82 + src/curl-7.12.2/tests/data/test68 | 84 + src/curl-7.12.2/tests/data/test69 | 103 + src/curl-7.12.2/tests/data/test7 | 52 + src/curl-7.12.2/tests/data/test70 | 80 + src/curl-7.12.2/tests/data/test71 | 70 + src/curl-7.12.2/tests/data/test72 | 76 + src/curl-7.12.2/tests/data/test73 | 45 + src/curl-7.12.2/tests/data/test74 | 67 + src/curl-7.12.2/tests/data/test75 | 33 + src/curl-7.12.2/tests/data/test76 | 29 + src/curl-7.12.2/tests/data/test77 | 47 + src/curl-7.12.2/tests/data/test78 | 59 + src/curl-7.12.2/tests/data/test79 | 42 + src/curl-7.12.2/tests/data/test8 | 50 + src/curl-7.12.2/tests/data/test80 | 59 + src/curl-7.12.2/tests/data/test81 | 80 + src/curl-7.12.2/tests/data/test82 | 45 + src/curl-7.12.2/tests/data/test83 | 58 + src/curl-7.12.2/tests/data/test84 | 44 + src/curl-7.12.2/tests/data/test85 | 45 + src/curl-7.12.2/tests/data/test86 | 90 + src/curl-7.12.2/tests/data/test87 | 25 + src/curl-7.12.2/tests/data/test88 | 88 + src/curl-7.12.2/tests/data/test89 | 128 + src/curl-7.12.2/tests/data/test9 | 65 + src/curl-7.12.2/tests/data/test90 | 172 + src/curl-7.12.2/tests/data/test91 | 106 + src/curl-7.12.2/tests/data/test92 | 44 + src/curl-7.12.2/tests/data/test93 | 41 + src/curl-7.12.2/tests/data/test94 | 45 + src/curl-7.12.2/tests/data/test95 | 60 + src/curl-7.12.2/tests/data/test97 | 44 + src/curl-7.12.2/tests/data/test98 | 47 + src/curl-7.12.2/tests/data/test99 | 59 + src/curl-7.12.2/tests/ftpserver.pl | 656 + src/curl-7.12.2/tests/ftpsserver.pl | 85 + src/curl-7.12.2/tests/getpart.pm | 216 + src/curl-7.12.2/tests/httpserver.pl | 22 + src/curl-7.12.2/tests/httpsserver.pl | 87 + src/curl-7.12.2/tests/libtest/Makefile.am | 102 + src/curl-7.12.2/tests/libtest/Makefile.in | 652 + src/curl-7.12.2/tests/libtest/first.c | 44 + src/curl-7.12.2/tests/libtest/lib500.c | 12 + src/curl-7.12.2/tests/libtest/lib501.c | 13 + src/curl-7.12.2/tests/libtest/lib502.c | 32 + src/curl-7.12.2/tests/libtest/lib503.c | 84 + src/curl-7.12.2/tests/libtest/lib504.c | 94 + src/curl-7.12.2/tests/libtest/lib505.c | 116 + src/curl-7.12.2/tests/libtest/lib506.c | 223 + src/curl-7.12.2/tests/libtest/lib507.c | 51 + src/curl-7.12.2/tests/libtest/lib508.c | 66 + src/curl-7.12.2/tests/libtest/lib509.c | 273 + src/curl-7.12.2/tests/libtest/lib510.c | 82 + src/curl-7.12.2/tests/libtest/lib511.c | 14 + src/curl-7.12.2/tests/libtest/lib512.c | 52 + src/curl-7.12.2/tests/libtest/lib513.c | 45 + src/curl-7.12.2/tests/libtest/lib514.c | 42 + src/curl-7.12.2/tests/libtest/test.h | 36 + src/curl-7.12.2/tests/memanalyze.pl | 352 + src/curl-7.12.2/tests/runtests.pl | 1781 + src/curl-7.12.2/tests/server/Makefile.am | 37 + src/curl-7.12.2/tests/server/Makefile.in | 522 + src/curl-7.12.2/tests/server/getpart.c | 183 + src/curl-7.12.2/tests/server/getpart.h | 27 + src/curl-7.12.2/tests/server/sws.c | 820 + src/curl-7.12.2/tests/stunnel.pem | 138 + src/curl-7.12.2/tests/testcurl.pl | 494 + src/docs/BotScriptCommands | 1786 + src/docs/Changelog | 7733 ++++ src/docs/Notes | 86 + src/docs/QA-Changelog | 286 + src/docs/Todo | 250 + src/extractfuncs/ChangeLog | 17 + src/extractfuncs/Conscript | 9 + src/extractfuncs/Construct | 9 + src/extractfuncs/extractfuncs.bat | 5 + src/extractfuncs/extractfuncs.c | 707 + src/extractfuncs/extractfuncs.vcproj | 330 + src/extractfuncs/l_log.c | 199 + src/extractfuncs/l_log.h | 59 + src/extractfuncs/l_memory.c | 444 + src/extractfuncs/l_memory.h | 80 + src/extractfuncs/l_precomp.c | 3177 ++ src/extractfuncs/l_precomp.h | 158 + src/extractfuncs/l_script.c | 1459 + src/extractfuncs/l_script.h | 266 + src/ft2/LICENSE.txt | 28 + src/ft2/ahangles.c | 128 + src/ft2/ahangles.h | 55 + src/ft2/ahglobal.c | 397 + src/ft2/ahglobal.h | 42 + src/ft2/ahglyph.c | 1275 + src/ft2/ahglyph.h | 86 + src/ft2/ahhint.c | 1362 + src/ft2/ahhint.h | 65 + src/ft2/ahloader.h | 123 + src/ft2/ahmodule.c | 114 + src/ft2/ahmodule.h | 32 + src/ft2/ahoptim.c | 875 + src/ft2/ahoptim.h | 127 + src/ft2/ahtypes.h | 484 + src/ft2/autohint.h | 195 + src/ft2/freetype.h | 2286 ++ src/ft2/ftcalc.c | 777 + src/ft2/ftcalc.h | 123 + src/ft2/ftconfig.h | 187 + src/ft2/ftdebug.c | 124 + src/ft2/ftdebug.h | 225 + src/ft2/ftdriver.h | 182 + src/ft2/fterrors.h | 166 + src/ft2/ftextend.c | 332 + src/ft2/ftextend.h | 178 + src/ft2/ftglyph.c | 1185 + src/ft2/ftglyph.h | 422 + src/ft2/ftgrays.c | 1996 + src/ft2/ftgrays.h | 48 + src/ft2/ftimage.h | 1003 + src/ft2/ftinit.c | 161 + src/ft2/ftlist.c | 310 + src/ft2/ftlist.h | 113 + src/ft2/ftmemory.h | 127 + src/ft2/ftmm.c | 179 + src/ft2/ftmm.h | 175 + src/ft2/ftmodule.h | 274 + src/ft2/ftnames.c | 68 + src/ft2/ftnames.h | 52 + src/ft2/ftobjs.c | 3280 ++ src/ft2/ftobjs.h | 532 + src/ft2/ftoption.h | 395 + src/ft2/ftoutln.c | 861 + src/ft2/ftoutln.h | 344 + src/ft2/ftraster.c | 3251 ++ src/ft2/ftraster.h | 50 + src/ft2/ftrend1.c | 264 + src/ft2/ftrend1.h | 37 + src/ft2/ftrender.h | 191 + src/ft2/ftsmooth.c | 219 + src/ft2/ftsmooth.h | 35 + src/ft2/ftstream.c | 807 + src/ft2/ftstream.h | 361 + src/ft2/ftsystem.c | 293 + src/ft2/ftsystem.h | 101 + src/ft2/fttypes.h | 400 + src/ft2/psnames.h | 220 + src/ft2/sfdriver.c | 211 + src/ft2/sfdriver.h | 29 + src/ft2/sfnt.h | 492 + src/ft2/sfobjs.c | 555 + src/ft2/sfobjs.h | 57 + src/ft2/t1tables.h | 235 + src/ft2/ttcmap.c | 550 + src/ft2/ttcmap.h | 45 + src/ft2/ttdriver.c | 501 + src/ft2/ttdriver.h | 31 + src/ft2/tterrors.h | 121 + src/ft2/ttgload.c | 1432 + src/ft2/ttgload.h | 57 + src/ft2/ttinterp.c | 7349 ++++ src/ft2/ttinterp.h | 270 + src/ft2/ttload.c | 1673 + src/ft2/ttload.h | 132 + src/ft2/ttnameid.h | 698 + src/ft2/ttobjs.c | 727 + src/ft2/ttobjs.h | 412 + src/ft2/ttpload.c | 266 + src/ft2/ttpload.h | 51 + src/ft2/ttpost.c | 528 + src/ft2/ttpost.h | 52 + src/ft2/ttsbit.c | 1449 + src/ft2/ttsbit.h | 57 + src/ft2/tttables.h | 583 + src/ft2/tttags.h | 66 + src/ft2/tttypes.h | 1582 + src/game/be_aas.h | 245 + src/game/be_ai_char.h | 53 + src/game/be_ai_chat.h | 118 + src/game/be_ai_gen.h | 38 + src/game/be_ai_goal.h | 141 + src/game/be_ai_move.h | 135 + src/game/be_ai_weap.h | 109 + src/game/be_ea.h | 73 + src/game/bg_animation.c | 1834 + src/game/bg_animgroup.c | 271 + src/game/bg_campaign.c | 144 + src/game/bg_character.c | 209 + src/game/bg_classes.c | 287 + src/game/bg_local.h | 105 + src/game/bg_misc.c | 5342 +++ src/game/bg_pmove.c | 5709 +++ src/game/bg_public.h | 2421 ++ src/game/bg_slidemove.c | 403 + src/game/bg_sscript.c | 240 + src/game/bg_stats.c | 119 + src/game/bg_tracemap.c | 741 + src/game/botlib.h | 536 + src/game/g_active.c | 1806 + src/game/g_alarm.c | 224 + src/game/g_antilag.c | 280 + src/game/g_bot.c | 1771 + src/game/g_buddy_list.c | 132 + src/game/g_character.c | 228 + src/game/g_client.c | 2417 ++ src/game/g_cmds.c | 3556 ++ src/game/g_cmds_ext.c | 812 + src/game/g_combat.c | 2094 + src/game/g_config.c | 131 + src/game/g_fireteams.c | 791 + src/game/g_func_decs.h | 1340 + src/game/g_funcs.h | 1341 + src/game/g_items.c | 1332 + src/game/g_local.h | 2531 ++ src/game/g_main.c | 3846 ++ src/game/g_match.c | 825 + src/game/g_mem.c | 68 + src/game/g_misc.c | 2634 ++ src/game/g_missile.c | 2188 + src/game/g_mover.c | 5328 +++ src/game/g_multiview.c | 379 + src/game/g_props.c | 4110 ++ src/game/g_public.h | 507 + src/game/g_referee.c | 614 + src/game/g_save.c | 2075 + src/game/g_script.c | 1310 + src/game/g_script_actions.c | 4301 ++ src/game/g_session.c | 530 + src/game/g_spawn.c | 1072 + src/game/g_stats.c | 716 + src/game/g_sv_entities.c | 238 + src/game/g_svcmds.c | 1384 + src/game/g_syscalls.c | 984 + src/game/g_systemmsg.c | 237 + src/game/g_target.c | 1271 + src/game/g_team.c | 1950 + src/game/g_team.h | 119 + src/game/g_teammapdata.c | 1208 + src/game/g_trigger.c | 1553 + src/game/g_utils.c | 1409 + src/game/g_vote.c | 1035 + src/game/g_weapon.c | 4285 ++ src/game/game.def | 3 + src/game/game.vcproj | 636 + src/game/q_math.c | 1605 + src/game/q_shared.c | 1482 + src/game/q_shared.h | 1740 + src/game/surfaceflags.h | 104 + src/jpeg-6/README | 385 + src/jpeg-6/jcapimin.c | 229 + src/jpeg-6/jccoefct.c | 449 + src/jpeg-6/jccolor.c | 467 + src/jpeg-6/jcdctmgr.c | 383 + src/jpeg-6/jchuff.c | 860 + src/jpeg-6/jchuff.h | 34 + src/jpeg-6/jcinit.c | 72 + src/jpeg-6/jcmainct.c | 299 + src/jpeg-6/jcmarker.c | 637 + src/jpeg-6/jcmaster.c | 604 + src/jpeg-6/jcomapi.c | 91 + src/jpeg-6/jconfig.h | 41 + src/jpeg-6/jcparam.c | 582 + src/jpeg-6/jcphuff.c | 844 + src/jpeg-6/jcprepct.c | 370 + src/jpeg-6/jcsample.c | 512 + src/jpeg-6/jctrans.c | 373 + src/jpeg-6/jdapimin.c | 395 + src/jpeg-6/jdapistd.c | 282 + src/jpeg-6/jdatadst.c | 150 + src/jpeg-6/jdatasrc.c | 199 + src/jpeg-6/jdcoefct.c | 747 + src/jpeg-6/jdcolor.c | 369 + src/jpeg-6/jdct.h | 176 + src/jpeg-6/jddctmgr.c | 270 + src/jpeg-6/jdhuff.c | 581 + src/jpeg-6/jdhuff.h | 202 + src/jpeg-6/jdinput.c | 392 + src/jpeg-6/jdmainct.c | 521 + src/jpeg-6/jdmarker.c | 1080 + src/jpeg-6/jdmaster.c | 564 + src/jpeg-6/jdpostct.c | 290 + src/jpeg-6/jdsample.c | 476 + src/jpeg-6/jdtrans.c | 125 + src/jpeg-6/jerror.c | 231 + src/jpeg-6/jerror.h | 273 + src/jpeg-6/jfdctflt.c | 167 + src/jpeg-6/jidctflt.c | 240 + src/jpeg-6/jinclude.h | 116 + src/jpeg-6/jmemmgr.c | 1143 + src/jpeg-6/jmemnobs.c | 97 + src/jpeg-6/jmemsys.h | 182 + src/jpeg-6/jmorecfg.h | 354 + src/jpeg-6/jpegint.h | 388 + src/jpeg-6/jpeglib.h | 1051 + src/jpeg-6/jutils.c | 170 + src/jpeg-6/jversion.h | 14 + src/mac/AGLUtils.cpp | 851 + src/mac/AGLUtils.h | 59 + src/mac/CDrawSprocket.cpp | 1740 + src/mac/CDrawSprocket.h | 139 + src/mac/CGameLibMP.mcp | Bin 0 -> 200936 bytes src/mac/CGameLib_Debug.h | 31 + src/mac/CGameLib_Release.h | 32 + src/mac/CarbonMouse.cpp | 349 + src/mac/CarbonMouse.h | 38 + src/mac/Icon.icns | Bin 0 -> 45914 bytes src/mac/Info.plc | 73 + src/mac/Info.plist | 82 + src/mac/MacPrefix.h | 34 + src/mac/MacPrefixCommon.h | 128 + src/mac/MacPrefixDebug.h | 34 + src/mac/MacPrefixDebugMach.h | 35 + src/mac/MacPrefixDedicatedDebug.h | 38 + src/mac/MacPrefixDedicatedRelease.h | 38 + src/mac/MacPrefixMach.h | 35 + src/mac/MacPrefix_GameSpecific.h | 88 + src/mac/MacPrefs.cpp | 283 + src/mac/MacPrefs.h | 67 + src/mac/QAGameLibMP.mcp | Bin 0 -> 206063 bytes src/mac/QAGameLib_Debug.h | 31 + src/mac/QAGameLib_Release.h | 32 + src/mac/UILibMP.mcp | Bin 0 -> 301748 bytes src/mac/UILib_Debug.h | 31 + src/mac/UILib_Release.h | 31 + src/mac/Wolf ET.mcp | Bin 0 -> 234588 bytes src/mac/WolfET.xcodeproj/project.pbxproj | 1170 + src/mac/Wolfenstein ET.rsrc | Bin 0 -> 6588 bytes src/mac/cgame.xcodeproj/project.pbxproj | 409 + src/mac/deployment.sh | 27 + src/mac/exports.def | 2 + src/mac/game.xcodeproj/project.pbxproj | 441 + src/mac/glext.h | 3051 ++ src/mac/mac_console.c | 995 + src/mac/mac_dedicated_main.c | 1441 + src/mac/mac_event.cpp | 999 + src/mac/mac_files.cpp | 778 + src/mac/mac_glimp.cpp | 1311 + src/mac/mac_glimp.h | 83 + src/mac/mac_input.cpp | 346 + src/mac/mac_input.h | 83 + src/mac/mac_local.h | 232 + src/mac/mac_main.cpp | 2473 ++ src/mac/mac_net.cpp | 636 + src/mac/mac_qgl.c | 3547 ++ src/mac/mac_snddma.c | 192 + src/mac/mac_specific.cpp | 161 + src/mac/pk3.icns | Bin 0 -> 61029 bytes src/mac/setup/ReadMe.txt | 13 + src/mac/setup/build-setup.py | 92 + src/mac/setup/mycmds.py | 29 + src/mac/setup/pbsetup.pmsp | Bin 0 -> 1387 bytes src/mac/setup/setup.pmsp | Bin 0 -> 1291 bytes src/mac/ui.xcodeproj/project.pbxproj | 251 + src/makebundle.sh | 87 + src/null/null_client.c | 106 + src/null/null_input.c | 51 + src/null/null_snddma.c | 77 + src/qcommon/cm_load.c | 884 + src/qcommon/cm_local.h | 216 + src/qcommon/cm_patch.c | 1944 + src/qcommon/cm_patch.h | 110 + src/qcommon/cm_polylib.c | 738 + src/qcommon/cm_polylib.h | 75 + src/qcommon/cm_public.h | 85 + src/qcommon/cm_test.c | 486 + src/qcommon/cm_trace.c | 1675 + src/qcommon/cmd.c | 720 + src/qcommon/common.c | 3827 ++ src/qcommon/cvar.c | 1053 + src/qcommon/dl_local.h | 58 + src/qcommon/dl_main.c | 356 + src/qcommon/dl_main_curl.c | 205 + src/qcommon/dl_main_stubs.c | 58 + src/qcommon/dl_public.h | 51 + src/qcommon/files.c | 4346 ++ src/qcommon/huffman.c | 456 + src/qcommon/md4.c | 317 + src/qcommon/msg.c | 2219 + src/qcommon/net_chan.c | 830 + src/qcommon/qcommon.h | 1317 + src/qcommon/qfiles.h | 956 + src/qcommon/unzip.c | 4329 ++ src/qcommon/unzip.h | 341 + src/qcommon/vm.c | 843 + src/qcommon/vm_interpreted.c | 896 + src/qcommon/vm_local.h | 187 + src/qcommon/vm_x86.c | 833 + src/renderer/anorms256.h | 284 + src/renderer/glext.h | 5948 +++ src/renderer/qgl.h | 632 + src/renderer/qgl_linked.h | 364 + src/renderer/ref_trin.def | 2 + src/renderer/renderer.vcproj | 909 + src/renderer/tr_animation_mdm.c | 1828 + src/renderer/tr_animation_mds.c | 1506 + src/renderer/tr_backend.c | 1531 + src/renderer/tr_bsp.c | 2656 ++ src/renderer/tr_cmds.c | 670 + src/renderer/tr_cmesh.c | 438 + src/renderer/tr_curve.c | 634 + src/renderer/tr_decals.c | 955 + src/renderer/tr_flares.c | 537 + src/renderer/tr_font.c | 550 + src/renderer/tr_image.c | 3417 ++ src/renderer/tr_init.c | 1408 + src/renderer/tr_light.c | 536 + src/renderer/tr_local.h | 2153 + src/renderer/tr_main.c | 1934 + src/renderer/tr_marks.c | 1041 + src/renderer/tr_mesh.c | 445 + src/renderer/tr_model.c | 2415 ++ src/renderer/tr_noise.c | 99 + src/renderer/tr_public.h | 214 + src/renderer/tr_scene.c | 670 + src/renderer/tr_shade.c | 1737 + src/renderer/tr_shade_calc.c | 1443 + src/renderer/tr_shader.c | 3904 ++ src/renderer/tr_shadows.c | 348 + src/renderer/tr_sky.c | 1047 + src/renderer/tr_surface.c | 1766 + src/renderer/tr_world.c | 1065 + src/scons_utils.py | 116 + src/server/server.h | 538 + src/server/sv_bot.c | 784 + src/server/sv_ccmds.c | 1186 + src/server/sv_client.c | 1848 + src/server/sv_game.c | 1173 + src/server/sv_init.c | 951 + src/server/sv_main.c | 1235 + src/server/sv_net_chan.c | 255 + src/server/sv_snapshot.c | 928 + src/server/sv_world.c | 756 + src/splines/Splines.vcproj | 245 + src/splines/math_angles.cpp | 157 + src/splines/math_angles.h | 203 + src/splines/math_matrix.cpp | 141 + src/splines/math_matrix.h | 230 + src/splines/math_quaternion.cpp | 85 + src/splines/math_quaternion.h | 197 + src/splines/math_vector.cpp | 151 + src/splines/math_vector.h | 586 + src/splines/q_parse.cpp | 541 + src/splines/q_shared.c | 923 + src/splines/q_splineshared.h | 764 + src/splines/splines.cpp | 1438 + src/splines/splines.h | 1134 + src/splines/util_list.h | 353 + src/splines/util_str.cpp | 583 + src/splines/util_str.h | 728 + src/ui/keycodes.h | 169 + src/ui/ui.def | 3 + src/ui/ui.vcproj | 597 + src/ui/ui_atoms.c | 405 + src/ui/ui_gameinfo.c | 981 + src/ui/ui_loadpanel.c | 460 + src/ui/ui_local.h | 1210 + src/ui/ui_main.c | 8779 ++++ src/ui/ui_players.c | 1709 + src/ui/ui_public.h | 224 + src/ui/ui_shared.c | 7801 ++++ src/ui/ui_shared.h | 633 + src/ui/ui_syscalls.c | 532 + src/ui/ui_util.c | 36 + src/unix/ChangeLog | 1429 + src/unix/ftol.nasm | 131 + src/unix/linux_common.c | 350 + src/unix/linux_glimp.c | 1890 + src/unix/linux_joystick.c | 213 + src/unix/linux_local.h | 53 + src/unix/linux_qgl.c | 3816 ++ src/unix/linux_signals.c | 74 + src/unix/linux_snd.c | 316 + src/unix/matha.spp | 404 + src/unix/qasm.h | 488 + src/unix/snapvector.asm | 75 + src/unix/snd_mixa.spp | 197 + src/unix/staticlinkcpp.sh | 2 + src/unix/symbolcheck.pl | 117 + src/unix/unix_glw.h | 45 + src/unix/unix_main.c | 1467 + src/unix/unix_net.c | 734 + src/unix/unix_shared.c | 461 + src/unix/wolf.vpw | 36 + src/win32/background.bmp | Bin 0 -> 197688 bytes src/win32/clear.bmp | Bin 0 -> 5174 bytes src/win32/glw_win.h | 58 + src/win32/qe3.ico | Bin 0 -> 2238 bytes src/win32/resource.h | 49 + src/win32/win_eh.cpp | 261 + src/win32/win_gamma.c | 208 + src/win32/win_glimp.c | 1686 + src/win32/win_input.c | 1119 + src/win32/win_local.h | 138 + src/win32/win_main.c | 1334 + src/win32/win_net.c | 1145 + src/win32/win_qgl.c | 4037 ++ src/win32/win_shared.c | 586 + src/win32/win_snd.c | 390 + src/win32/win_syscon.c | 885 + src/win32/win_wndproc.c | 521 + src/win32/winquake.rc | 71 + src/win32/wolfet.ico | Bin 0 -> 2238 bytes src/wolf.sln | 133 + src/wolf.vcproj | 1627 + uncrustify.cfg | 929 + 1357 files changed, 699137 insertions(+) create mode 100644 COPYING.txt create mode 100644 README.txt create mode 100644 etmain/ui/menudef.h create mode 100644 etmain/ui/menumacros.h create mode 100644 src/SConscript.bspc create mode 100644 src/SConscript.cgame create mode 100644 src/SConscript.core create mode 100644 src/SConscript.curl create mode 100644 src/SConscript.game create mode 100644 src/SConscript.ui create mode 100644 src/SConstruct create mode 100644 src/SConstruct.sdk create mode 100644 src/botai/ai_cmd.c create mode 100644 src/botai/ai_cmd.h create mode 100644 src/botai/ai_distances.h create mode 100644 src/botai/ai_dmgoal_mp.c create mode 100644 src/botai/ai_dmgoal_mp.h create mode 100644 src/botai/ai_dmnet_mp.c create mode 100644 src/botai/ai_dmnet_mp.h create mode 100644 src/botai/ai_dmq3.c create mode 100644 src/botai/ai_dmq3.h create mode 100644 src/botai/ai_func_decs.h create mode 100644 src/botai/ai_funcs.h create mode 100644 src/botai/ai_main.c create mode 100644 src/botai/ai_main.h create mode 100644 src/botai/ai_matrix.h create mode 100644 src/botai/ai_script.c create mode 100644 src/botai/ai_script_actions.c create mode 100644 src/botai/ai_team.c create mode 100644 src/botai/ai_team.h create mode 100644 src/botai/botai.h create mode 100644 src/botai/chars.h create mode 100644 src/botai/inv.h create mode 100644 src/botai/match.h create mode 100644 src/botai/syn.h create mode 100644 src/botlib/aasfile.h create mode 100644 src/botlib/be_aas_bsp.h create mode 100644 src/botlib/be_aas_bspq3.c create mode 100644 src/botlib/be_aas_cluster.c create mode 100644 src/botlib/be_aas_cluster.h create mode 100644 src/botlib/be_aas_debug.c create mode 100644 src/botlib/be_aas_debug.h create mode 100644 src/botlib/be_aas_def.h create mode 100644 src/botlib/be_aas_entity.c create mode 100644 src/botlib/be_aas_entity.h create mode 100644 src/botlib/be_aas_file.c create mode 100644 src/botlib/be_aas_file.h create mode 100644 src/botlib/be_aas_funcs.h create mode 100644 src/botlib/be_aas_main.c create mode 100644 src/botlib/be_aas_main.h create mode 100644 src/botlib/be_aas_move.c create mode 100644 src/botlib/be_aas_move.h create mode 100644 src/botlib/be_aas_optimize.c create mode 100644 src/botlib/be_aas_optimize.h create mode 100644 src/botlib/be_aas_reach.c create mode 100644 src/botlib/be_aas_reach.h create mode 100644 src/botlib/be_aas_route.c create mode 100644 src/botlib/be_aas_route.h create mode 100644 src/botlib/be_aas_routealt.c create mode 100644 src/botlib/be_aas_routealt.h create mode 100644 src/botlib/be_aas_routetable.c create mode 100644 src/botlib/be_aas_routetable.h create mode 100644 src/botlib/be_aas_sample.c create mode 100644 src/botlib/be_aas_sample.h create mode 100644 src/botlib/be_ai_char.c create mode 100644 src/botlib/be_ai_chat.c create mode 100644 src/botlib/be_ai_gen.c create mode 100644 src/botlib/be_ai_goal.c create mode 100644 src/botlib/be_ai_move.c create mode 100644 src/botlib/be_ai_weap.c create mode 100644 src/botlib/be_ai_weight.c create mode 100644 src/botlib/be_ai_weight.h create mode 100644 src/botlib/be_ea.c create mode 100644 src/botlib/be_interface.c create mode 100644 src/botlib/be_interface.h create mode 100644 src/botlib/botlib.vcproj create mode 100644 src/botlib/botlib_stub.c create mode 100644 src/botlib/l_common.h create mode 100644 src/botlib/l_crc.c create mode 100644 src/botlib/l_crc.h create mode 100644 src/botlib/l_libvar.c create mode 100644 src/botlib/l_libvar.h create mode 100644 src/botlib/l_log.c create mode 100644 src/botlib/l_log.h create mode 100644 src/botlib/l_memory.c create mode 100644 src/botlib/l_memory.h create mode 100644 src/botlib/l_precomp.c create mode 100644 src/botlib/l_precomp.h create mode 100644 src/botlib/l_script.c create mode 100644 src/botlib/l_script.h create mode 100644 src/botlib/l_struct.c create mode 100644 src/botlib/l_struct.h create mode 100644 src/botlib/l_utils.h create mode 100644 src/bspc/_files.c create mode 100644 src/bspc/aas_areamerging.c create mode 100644 src/bspc/aas_areamerging.h create mode 100644 src/bspc/aas_cfg.c create mode 100644 src/bspc/aas_cfg.h create mode 100644 src/bspc/aas_create.c create mode 100644 src/bspc/aas_create.h create mode 100644 src/bspc/aas_edgemelting.c create mode 100644 src/bspc/aas_edgemelting.h create mode 100644 src/bspc/aas_facemerging.c create mode 100644 src/bspc/aas_facemerging.h create mode 100644 src/bspc/aas_file.c create mode 100644 src/bspc/aas_file.h create mode 100644 src/bspc/aas_gsubdiv.c create mode 100644 src/bspc/aas_gsubdiv.h create mode 100644 src/bspc/aas_map.c create mode 100644 src/bspc/aas_map.h create mode 100644 src/bspc/aas_prunenodes.c create mode 100644 src/bspc/aas_prunenodes.h create mode 100644 src/bspc/aas_store.c create mode 100644 src/bspc/aas_store.h create mode 100644 src/bspc/be_aas_bspc.c create mode 100644 src/bspc/be_aas_bspc.h create mode 100644 src/bspc/brushbsp.c create mode 100644 src/bspc/bspc.c create mode 100644 src/bspc/bspc.vcproj create mode 100644 src/bspc/csg.c create mode 100644 src/bspc/faces.c create mode 100644 src/bspc/glfile.c create mode 100644 src/bspc/l_bsp_ent.c create mode 100644 src/bspc/l_bsp_ent.h create mode 100644 src/bspc/l_bsp_q1.c create mode 100644 src/bspc/l_bsp_q1.h create mode 100644 src/bspc/l_bsp_q2.c create mode 100644 src/bspc/l_bsp_q2.h create mode 100644 src/bspc/l_bsp_q3.c create mode 100644 src/bspc/l_bsp_q3.h create mode 100644 src/bspc/l_bsp_sin.c create mode 100644 src/bspc/l_bsp_sin.h create mode 100644 src/bspc/l_cmd.c create mode 100644 src/bspc/l_cmd.h create mode 100644 src/bspc/l_log.c create mode 100644 src/bspc/l_log.h create mode 100644 src/bspc/l_math.c create mode 100644 src/bspc/l_math.h create mode 100644 src/bspc/l_mem.c create mode 100644 src/bspc/l_mem.h create mode 100644 src/bspc/l_poly.c create mode 100644 src/bspc/l_poly.h create mode 100644 src/bspc/l_qfiles.c create mode 100644 src/bspc/l_qfiles.h create mode 100644 src/bspc/l_threads.c create mode 100644 src/bspc/l_threads.h create mode 100644 src/bspc/l_utils.c create mode 100644 src/bspc/l_utils.h create mode 100644 src/bspc/leakfile.c create mode 100644 src/bspc/map.c create mode 100644 src/bspc/map_q1.c create mode 100644 src/bspc/map_q2.c create mode 100644 src/bspc/map_q3.c create mode 100644 src/bspc/map_sin.c create mode 100644 src/bspc/nodraw.c create mode 100644 src/bspc/portals.c create mode 100644 src/bspc/prtfile.c create mode 100644 src/bspc/q2files.h create mode 100644 src/bspc/q3files.h create mode 100644 src/bspc/qbsp.h create mode 100644 src/bspc/qfiles.h create mode 100644 src/bspc/sinfiles.h create mode 100644 src/bspc/textures.c create mode 100644 src/bspc/tree.c create mode 100644 src/bspc/writebsp.c create mode 100644 src/cgame/cg_atmospheric.c create mode 100644 src/cgame/cg_character.c create mode 100644 src/cgame/cg_commandmap.c create mode 100644 src/cgame/cg_consolecmds.c create mode 100644 src/cgame/cg_debriefing.c create mode 100644 src/cgame/cg_draw.c create mode 100644 src/cgame/cg_drawtools.c create mode 100644 src/cgame/cg_effects.c create mode 100644 src/cgame/cg_ents.c create mode 100644 src/cgame/cg_event.c create mode 100644 src/cgame/cg_fireteamoverlay.c create mode 100644 src/cgame/cg_fireteams.c create mode 100644 src/cgame/cg_flamethrower.c create mode 100644 src/cgame/cg_info.c create mode 100644 src/cgame/cg_limbopanel.c create mode 100644 src/cgame/cg_loadpanel.c create mode 100644 src/cgame/cg_local.h create mode 100644 src/cgame/cg_localents.c create mode 100644 src/cgame/cg_main.c create mode 100644 src/cgame/cg_marks.c create mode 100644 src/cgame/cg_missionbriefing.c create mode 100644 src/cgame/cg_multiview.c create mode 100644 src/cgame/cg_newDraw.c create mode 100644 src/cgame/cg_panelhandling.c create mode 100644 src/cgame/cg_particles.c create mode 100644 src/cgame/cg_players.c create mode 100644 src/cgame/cg_playerstate.c create mode 100644 src/cgame/cg_polybus.c create mode 100644 src/cgame/cg_popupmessages.c create mode 100644 src/cgame/cg_predict.c create mode 100644 src/cgame/cg_public.h create mode 100644 src/cgame/cg_scoreboard.c create mode 100644 src/cgame/cg_servercmds.c create mode 100644 src/cgame/cg_snapshot.c create mode 100644 src/cgame/cg_sound.c create mode 100644 src/cgame/cg_spawn.c create mode 100644 src/cgame/cg_statsranksmedals.c create mode 100644 src/cgame/cg_syscalls.c create mode 100644 src/cgame/cg_trails.c create mode 100644 src/cgame/cg_view.c create mode 100644 src/cgame/cg_weapons.c create mode 100644 src/cgame/cg_window.c create mode 100644 src/cgame/cgame.def create mode 100644 src/cgame/cgame.vcproj create mode 100644 src/cgame/tr_types.h create mode 100644 src/client/cl_cgame.c create mode 100644 src/client/cl_cin.c create mode 100644 src/client/cl_console.c create mode 100644 src/client/cl_input.c create mode 100644 src/client/cl_keys.c create mode 100644 src/client/cl_main.c create mode 100644 src/client/cl_net_chan.c create mode 100644 src/client/cl_parse.c create mode 100644 src/client/cl_scrn.c create mode 100644 src/client/cl_ui.c create mode 100644 src/client/client.h create mode 100644 src/client/keys.h create mode 100644 src/client/snd_adpcm.c create mode 100644 src/client/snd_dma.c create mode 100644 src/client/snd_local.h create mode 100644 src/client/snd_mem.c create mode 100644 src/client/snd_mix.c create mode 100644 src/client/snd_public.h create mode 100644 src/client/snd_wavelet.c create mode 100644 src/curl-7.12.2/CHANGES create mode 100644 src/curl-7.12.2/COPYING create mode 100644 src/curl-7.12.2/Makefile.am create mode 100644 src/curl-7.12.2/Makefile.in create mode 100644 src/curl-7.12.2/README create mode 100644 src/curl-7.12.2/RELEASE-NOTES create mode 100644 src/curl-7.12.2/acinclude.m4 create mode 100644 src/curl-7.12.2/aclocal.m4 create mode 100644 src/curl-7.12.2/buildconf create mode 100644 src/curl-7.12.2/buildconf.bat create mode 100644 src/curl-7.12.2/config.guess create mode 100644 src/curl-7.12.2/config.sub create mode 100644 src/curl-7.12.2/configure create mode 100644 src/curl-7.12.2/configure.ac create mode 100644 src/curl-7.12.2/curl-config.in create mode 100644 src/curl-7.12.2/curl-style.el create mode 100644 src/curl-7.12.2/depcomp create mode 100644 src/curl-7.12.2/docs/BINDINGS create mode 100644 src/curl-7.12.2/docs/BUGS create mode 100644 src/curl-7.12.2/docs/CONTRIBUTE create mode 100644 src/curl-7.12.2/docs/FAQ create mode 100644 src/curl-7.12.2/docs/FEATURES create mode 100644 src/curl-7.12.2/docs/HISTORY create mode 100644 src/curl-7.12.2/docs/INSTALL create mode 100644 src/curl-7.12.2/docs/INTERNALS create mode 100644 src/curl-7.12.2/docs/KNOWN_BUGS create mode 100644 src/curl-7.12.2/docs/LICENSE-MIXING create mode 100644 src/curl-7.12.2/docs/MANUAL create mode 100644 src/curl-7.12.2/docs/Makefile.am create mode 100644 src/curl-7.12.2/docs/Makefile.in create mode 100644 src/curl-7.12.2/docs/README.netware create mode 100644 src/curl-7.12.2/docs/README.win32 create mode 100644 src/curl-7.12.2/docs/RESOURCES create mode 100644 src/curl-7.12.2/docs/SSLCERTS create mode 100644 src/curl-7.12.2/docs/THANKS create mode 100644 src/curl-7.12.2/docs/TODO create mode 100644 src/curl-7.12.2/docs/TheArtOfHttpScripting create mode 100644 src/curl-7.12.2/docs/VERSIONS create mode 100644 src/curl-7.12.2/docs/curl-config.1 create mode 100644 src/curl-7.12.2/docs/curl-config.html create mode 100644 src/curl-7.12.2/docs/curl-config.pdf create mode 100644 src/curl-7.12.2/docs/curl.1 create mode 100644 src/curl-7.12.2/docs/curl.html create mode 100644 src/curl-7.12.2/docs/curl.pdf create mode 100644 src/curl-7.12.2/docs/examples/Makefile.am create mode 100644 src/curl-7.12.2/docs/examples/Makefile.example create mode 100644 src/curl-7.12.2/docs/examples/Makefile.in create mode 100644 src/curl-7.12.2/docs/examples/README create mode 100644 src/curl-7.12.2/docs/examples/curlgtk.c create mode 100644 src/curl-7.12.2/docs/examples/curlx.c create mode 100644 src/curl-7.12.2/docs/examples/debug.c create mode 100644 src/curl-7.12.2/docs/examples/fileupload.c create mode 100644 src/curl-7.12.2/docs/examples/fopen.c create mode 100644 src/curl-7.12.2/docs/examples/ftp3rdparty.c create mode 100644 src/curl-7.12.2/docs/examples/ftpget.c create mode 100644 src/curl-7.12.2/docs/examples/ftpgetresp.c create mode 100644 src/curl-7.12.2/docs/examples/ftpupload.c create mode 100644 src/curl-7.12.2/docs/examples/getinfo.c create mode 100644 src/curl-7.12.2/docs/examples/getinmemory.c create mode 100644 src/curl-7.12.2/docs/examples/http-post.c create mode 100644 src/curl-7.12.2/docs/examples/httpput.c create mode 100644 src/curl-7.12.2/docs/examples/https.c create mode 100644 src/curl-7.12.2/docs/examples/makefile.dj create mode 100644 src/curl-7.12.2/docs/examples/multi-app.c create mode 100644 src/curl-7.12.2/docs/examples/multi-debugcallback.c create mode 100644 src/curl-7.12.2/docs/examples/multi-double.c create mode 100644 src/curl-7.12.2/docs/examples/multi-post.c create mode 100644 src/curl-7.12.2/docs/examples/multi-single.c create mode 100644 src/curl-7.12.2/docs/examples/multithread.c create mode 100644 src/curl-7.12.2/docs/examples/persistant.c create mode 100644 src/curl-7.12.2/docs/examples/post-callback.c create mode 100644 src/curl-7.12.2/docs/examples/postit2.c create mode 100644 src/curl-7.12.2/docs/examples/sepheaders.c create mode 100644 src/curl-7.12.2/docs/examples/simple.c create mode 100644 src/curl-7.12.2/docs/examples/simplepost.c create mode 100644 src/curl-7.12.2/docs/examples/simplessl.c create mode 100644 src/curl-7.12.2/docs/index.html create mode 100644 src/curl-7.12.2/docs/libcurl/Makefile.am create mode 100644 src/curl-7.12.2/docs/libcurl/Makefile.in create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_cleanup.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_cleanup.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_cleanup.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_duphandle.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_duphandle.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_duphandle.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_getinfo.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_getinfo.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_getinfo.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_init.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_init.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_init.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_perform.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_perform.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_perform.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_reset.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_reset.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_reset.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_setopt.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_setopt.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_setopt.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_strerror.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_strerror.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_easy_strerror.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_escape.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_escape.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_escape.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formadd.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formadd.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formadd.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formfree.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formfree.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_formfree.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_free.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_free.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_free.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getdate.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getdate.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getdate.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getenv.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getenv.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_getenv.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_cleanup.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_cleanup.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_cleanup.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init_mem.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init_mem.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_global_init_mem.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_mprintf.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_mprintf.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_mprintf.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_add_handle.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_add_handle.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_add_handle.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_cleanup.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_cleanup.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_cleanup.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_fdset.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_fdset.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_fdset.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_info_read.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_info_read.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_info_read.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_init.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_init.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_init.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_perform.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_perform.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_perform.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_remove_handle.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_remove_handle.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_remove_handle.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_strerror.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_strerror.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_multi_strerror.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_cleanup.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_cleanup.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_cleanup.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_init.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_init.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_init.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_setopt.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_setopt.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_setopt.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_strerror.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_strerror.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_share_strerror.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_append.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_append.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_append.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_free_all.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_free_all.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_slist_free_all.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_strequal.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_strequal.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_strequal.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_unescape.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_unescape.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_unescape.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version_info.3 create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version_info.html create mode 100644 src/curl-7.12.2/docs/libcurl/curl_version_info.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/index.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-easy.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-easy.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-easy.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-errors.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-errors.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-errors.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-multi.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-multi.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-multi.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-share.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-share.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-share.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-tutorial.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-tutorial.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl-tutorial.pdf create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl.3 create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl.html create mode 100644 src/curl-7.12.2/docs/libcurl/libcurl.pdf create mode 100644 src/curl-7.12.2/include/Makefile.am create mode 100644 src/curl-7.12.2/include/Makefile.in create mode 100644 src/curl-7.12.2/include/README create mode 100644 src/curl-7.12.2/include/curl/Makefile.am create mode 100644 src/curl-7.12.2/include/curl/Makefile.in create mode 100644 src/curl-7.12.2/include/curl/curl.h create mode 100644 src/curl-7.12.2/include/curl/curlver.h create mode 100644 src/curl-7.12.2/include/curl/easy.h create mode 100644 src/curl-7.12.2/include/curl/mprintf.h create mode 100644 src/curl-7.12.2/include/curl/multi.h create mode 100644 src/curl-7.12.2/include/curl/stdcheaders.h create mode 100644 src/curl-7.12.2/include/curl/types.h create mode 100644 src/curl-7.12.2/install-sh create mode 100644 src/curl-7.12.2/lib/Makefile.am create mode 100644 src/curl-7.12.2/lib/Makefile.b32 create mode 100644 src/curl-7.12.2/lib/Makefile.in create mode 100644 src/curl-7.12.2/lib/Makefile.inc create mode 100644 src/curl-7.12.2/lib/Makefile.m32 create mode 100644 src/curl-7.12.2/lib/Makefile.netware create mode 100644 src/curl-7.12.2/lib/Makefile.riscos create mode 100644 src/curl-7.12.2/lib/Makefile.vc6 create mode 100644 src/curl-7.12.2/lib/README.ares create mode 100644 src/curl-7.12.2/lib/README.curlx create mode 100644 src/curl-7.12.2/lib/README.encoding create mode 100644 src/curl-7.12.2/lib/README.memoryleak create mode 100644 src/curl-7.12.2/lib/amigaos.c create mode 100644 src/curl-7.12.2/lib/amigaos.h create mode 100644 src/curl-7.12.2/lib/arpa_telnet.h create mode 100644 src/curl-7.12.2/lib/base64.c create mode 100644 src/curl-7.12.2/lib/base64.h create mode 100644 src/curl-7.12.2/lib/ca-bundle.crt create mode 100644 src/curl-7.12.2/lib/ca-bundle.h create mode 100644 src/curl-7.12.2/lib/config-amigaos.h create mode 100644 src/curl-7.12.2/lib/config-mac.h create mode 100644 src/curl-7.12.2/lib/config-riscos.h create mode 100644 src/curl-7.12.2/lib/config-vms.h create mode 100644 src/curl-7.12.2/lib/config-win32.h create mode 100644 src/curl-7.12.2/lib/config.dj create mode 100644 src/curl-7.12.2/lib/config.h.in create mode 100644 src/curl-7.12.2/lib/connect.c create mode 100644 src/curl-7.12.2/lib/connect.h create mode 100644 src/curl-7.12.2/lib/content_encoding.c create mode 100644 src/curl-7.12.2/lib/content_encoding.h create mode 100644 src/curl-7.12.2/lib/cookie.c create mode 100644 src/curl-7.12.2/lib/cookie.h create mode 100644 src/curl-7.12.2/lib/curlx.h create mode 100644 src/curl-7.12.2/lib/dict.c create mode 100644 src/curl-7.12.2/lib/dict.h create mode 100644 src/curl-7.12.2/lib/easy.c create mode 100644 src/curl-7.12.2/lib/escape.c create mode 100644 src/curl-7.12.2/lib/escape.h create mode 100644 src/curl-7.12.2/lib/file.c create mode 100644 src/curl-7.12.2/lib/file.h create mode 100644 src/curl-7.12.2/lib/formdata.c create mode 100644 src/curl-7.12.2/lib/formdata.h create mode 100644 src/curl-7.12.2/lib/ftp.c create mode 100644 src/curl-7.12.2/lib/ftp.h create mode 100644 src/curl-7.12.2/lib/getenv.c create mode 100644 src/curl-7.12.2/lib/getinfo.c create mode 100644 src/curl-7.12.2/lib/getinfo.h create mode 100644 src/curl-7.12.2/lib/hash.c create mode 100644 src/curl-7.12.2/lib/hash.h create mode 100644 src/curl-7.12.2/lib/hostares.c create mode 100644 src/curl-7.12.2/lib/hostasyn.c create mode 100644 src/curl-7.12.2/lib/hostip.c create mode 100644 src/curl-7.12.2/lib/hostip.h create mode 100644 src/curl-7.12.2/lib/hostip4.c create mode 100644 src/curl-7.12.2/lib/hostip6.c create mode 100644 src/curl-7.12.2/lib/hostsyn.c create mode 100644 src/curl-7.12.2/lib/hostthre.c create mode 100644 src/curl-7.12.2/lib/http.c create mode 100644 src/curl-7.12.2/lib/http.h create mode 100644 src/curl-7.12.2/lib/http_chunks.c create mode 100644 src/curl-7.12.2/lib/http_chunks.h create mode 100644 src/curl-7.12.2/lib/http_digest.c create mode 100644 src/curl-7.12.2/lib/http_digest.h create mode 100644 src/curl-7.12.2/lib/http_negotiate.c create mode 100644 src/curl-7.12.2/lib/http_negotiate.h create mode 100644 src/curl-7.12.2/lib/http_ntlm.c create mode 100644 src/curl-7.12.2/lib/http_ntlm.h create mode 100644 src/curl-7.12.2/lib/if2ip.c create mode 100644 src/curl-7.12.2/lib/if2ip.h create mode 100644 src/curl-7.12.2/lib/inet_ntoa_r.h create mode 100644 src/curl-7.12.2/lib/inet_ntop.c create mode 100644 src/curl-7.12.2/lib/inet_ntop.h create mode 100644 src/curl-7.12.2/lib/inet_pton.c create mode 100644 src/curl-7.12.2/lib/inet_pton.h create mode 100644 src/curl-7.12.2/lib/krb4.c create mode 100644 src/curl-7.12.2/lib/krb4.h create mode 100644 src/curl-7.12.2/lib/ldap.c create mode 100644 src/curl-7.12.2/lib/ldap.h create mode 100644 src/curl-7.12.2/lib/libcurl.def create mode 100644 src/curl-7.12.2/lib/libcurl.framework.make create mode 100644 src/curl-7.12.2/lib/libcurl.imp create mode 100644 src/curl-7.12.2/lib/libcurl.plist create mode 100644 src/curl-7.12.2/lib/libcurl.rc create mode 100644 src/curl-7.12.2/lib/llist.c create mode 100644 src/curl-7.12.2/lib/llist.h create mode 100644 src/curl-7.12.2/lib/makefile.amiga create mode 100644 src/curl-7.12.2/lib/makefile.dj create mode 100644 src/curl-7.12.2/lib/md5.c create mode 100644 src/curl-7.12.2/lib/md5.h create mode 100644 src/curl-7.12.2/lib/memdebug.c create mode 100644 src/curl-7.12.2/lib/memdebug.h create mode 100644 src/curl-7.12.2/lib/memory.h create mode 100644 src/curl-7.12.2/lib/mprintf.c create mode 100644 src/curl-7.12.2/lib/msvcproj.foot create mode 100644 src/curl-7.12.2/lib/msvcproj.head create mode 100644 src/curl-7.12.2/lib/multi.c create mode 100644 src/curl-7.12.2/lib/netrc.c create mode 100644 src/curl-7.12.2/lib/netrc.h create mode 100644 src/curl-7.12.2/lib/nwlib.c create mode 100644 src/curl-7.12.2/lib/parsedate.c create mode 100644 src/curl-7.12.2/lib/progress.c create mode 100644 src/curl-7.12.2/lib/progress.h create mode 100644 src/curl-7.12.2/lib/security.c create mode 100644 src/curl-7.12.2/lib/security.h create mode 100644 src/curl-7.12.2/lib/sendf.c create mode 100644 src/curl-7.12.2/lib/sendf.h create mode 100644 src/curl-7.12.2/lib/setup.h create mode 100644 src/curl-7.12.2/lib/share.c create mode 100644 src/curl-7.12.2/lib/share.h create mode 100644 src/curl-7.12.2/lib/speedcheck.c create mode 100644 src/curl-7.12.2/lib/speedcheck.h create mode 100644 src/curl-7.12.2/lib/ssluse.c create mode 100644 src/curl-7.12.2/lib/ssluse.h create mode 100644 src/curl-7.12.2/lib/strequal.c create mode 100644 src/curl-7.12.2/lib/strequal.h create mode 100644 src/curl-7.12.2/lib/strerror.c create mode 100644 src/curl-7.12.2/lib/strerror.h create mode 100644 src/curl-7.12.2/lib/strtok.c create mode 100644 src/curl-7.12.2/lib/strtok.h create mode 100644 src/curl-7.12.2/lib/strtoofft.c create mode 100644 src/curl-7.12.2/lib/strtoofft.h create mode 100644 src/curl-7.12.2/lib/telnet.c create mode 100644 src/curl-7.12.2/lib/telnet.h create mode 100644 src/curl-7.12.2/lib/timeval.c create mode 100644 src/curl-7.12.2/lib/timeval.h create mode 100644 src/curl-7.12.2/lib/transfer.c create mode 100644 src/curl-7.12.2/lib/transfer.h create mode 100644 src/curl-7.12.2/lib/url.c create mode 100644 src/curl-7.12.2/lib/url.h create mode 100644 src/curl-7.12.2/lib/urldata.h create mode 100644 src/curl-7.12.2/lib/version.c create mode 100644 src/curl-7.12.2/ltmain.sh create mode 100644 src/curl-7.12.2/maketgz create mode 100644 src/curl-7.12.2/missing create mode 100644 src/curl-7.12.2/mkinstalldirs create mode 100644 src/curl-7.12.2/packages/DOS/Makefile.am create mode 100644 src/curl-7.12.2/packages/DOS/Makefile.in create mode 100644 src/curl-7.12.2/packages/DOS/README create mode 100644 src/curl-7.12.2/packages/DOS/common.dj create mode 100644 src/curl-7.12.2/packages/EPM/Makefile.am create mode 100644 src/curl-7.12.2/packages/EPM/Makefile.in create mode 100644 src/curl-7.12.2/packages/EPM/README create mode 100644 src/curl-7.12.2/packages/EPM/curl.list.in create mode 100644 src/curl-7.12.2/packages/Linux/Makefile.am create mode 100644 src/curl-7.12.2/packages/Linux/Makefile.in create mode 100644 src/curl-7.12.2/packages/Linux/RPM/Makefile.am create mode 100644 src/curl-7.12.2/packages/Linux/RPM/Makefile.in create mode 100644 src/curl-7.12.2/packages/Linux/RPM/README create mode 100644 src/curl-7.12.2/packages/Linux/RPM/curl-ssl.spec.in create mode 100644 src/curl-7.12.2/packages/Linux/RPM/curl.spec.in create mode 100644 src/curl-7.12.2/packages/Linux/RPM/make_curl_rpm create mode 100644 src/curl-7.12.2/packages/Makefile.am create mode 100644 src/curl-7.12.2/packages/Makefile.in create mode 100644 src/curl-7.12.2/packages/NetWare/get_ver.awk create mode 100644 src/curl-7.12.2/packages/README create mode 100644 src/curl-7.12.2/packages/Solaris/Makefile.am create mode 100644 src/curl-7.12.2/packages/Solaris/Makefile.in create mode 100644 src/curl-7.12.2/packages/Win32/Makefile.am create mode 100644 src/curl-7.12.2/packages/Win32/Makefile.in create mode 100644 src/curl-7.12.2/packages/Win32/README create mode 100644 src/curl-7.12.2/packages/Win32/cygwin/Makefile.am create mode 100644 src/curl-7.12.2/packages/Win32/cygwin/Makefile.in create mode 100644 src/curl-7.12.2/packages/Win32/cygwin/README create mode 100644 src/curl-7.12.2/packages/vms/Makefile.am create mode 100644 src/curl-7.12.2/packages/vms/Makefile.in create mode 100644 src/curl-7.12.2/packages/vms/axp/README create mode 100644 src/curl-7.12.2/packages/vms/batch_compile.com create mode 100644 src/curl-7.12.2/packages/vms/build_vms.com create mode 100644 src/curl-7.12.2/packages/vms/config-vms.h_with_ssl create mode 100644 src/curl-7.12.2/packages/vms/config-vms.h_without_ssl create mode 100644 src/curl-7.12.2/packages/vms/curlmsg.h create mode 100644 src/curl-7.12.2/packages/vms/curlmsg.msg create mode 100644 src/curl-7.12.2/packages/vms/curlmsg.sdl create mode 100644 src/curl-7.12.2/packages/vms/curlmsg_vms.h create mode 100644 src/curl-7.12.2/packages/vms/defines.com create mode 100644 src/curl-7.12.2/packages/vms/hpssl_alpha.opt create mode 100644 src/curl-7.12.2/packages/vms/hpssl_ia64.opt create mode 100644 src/curl-7.12.2/packages/vms/hpssl_vax.opt create mode 100644 src/curl-7.12.2/packages/vms/ia64/README create mode 100644 src/curl-7.12.2/packages/vms/readme create mode 100644 src/curl-7.12.2/packages/vms/vax/README create mode 100644 src/curl-7.12.2/reconf create mode 100644 src/curl-7.12.2/sample.emacs create mode 100644 src/curl-7.12.2/src/Makefile.am create mode 100644 src/curl-7.12.2/src/Makefile.b32 create mode 100644 src/curl-7.12.2/src/Makefile.in create mode 100644 src/curl-7.12.2/src/Makefile.inc create mode 100644 src/curl-7.12.2/src/Makefile.m32 create mode 100644 src/curl-7.12.2/src/Makefile.netware create mode 100644 src/curl-7.12.2/src/Makefile.riscos create mode 100644 src/curl-7.12.2/src/Makefile.vc6 create mode 100644 src/curl-7.12.2/src/config-amigaos.h create mode 100644 src/curl-7.12.2/src/config-mac.h create mode 100644 src/curl-7.12.2/src/config-riscos.h create mode 100644 src/curl-7.12.2/src/config-vms.h create mode 100644 src/curl-7.12.2/src/config-win32.h create mode 100644 src/curl-7.12.2/src/config.h.in create mode 100644 src/curl-7.12.2/src/curl.rc create mode 100644 src/curl-7.12.2/src/getpass.c create mode 100644 src/curl-7.12.2/src/getpass.h create mode 100644 src/curl-7.12.2/src/homedir.c create mode 100644 src/curl-7.12.2/src/homedir.h create mode 100644 src/curl-7.12.2/src/hugehelp.h create mode 100644 src/curl-7.12.2/src/macos/MACINSTALL.TXT create mode 100644 src/curl-7.12.2/src/macos/curl.mcp.xml.sit.hqx create mode 100644 src/curl-7.12.2/src/macos/src/curl_GUSIConfig.cpp create mode 100644 src/curl-7.12.2/src/macos/src/macos_main.cpp create mode 100644 src/curl-7.12.2/src/main.c create mode 100644 src/curl-7.12.2/src/makefile.amiga create mode 100644 src/curl-7.12.2/src/makefile.dj create mode 100644 src/curl-7.12.2/src/mkhelp.pl create mode 100644 src/curl-7.12.2/src/setup.h create mode 100644 src/curl-7.12.2/src/urlglob.c create mode 100644 src/curl-7.12.2/src/urlglob.h create mode 100644 src/curl-7.12.2/src/version.h create mode 100644 src/curl-7.12.2/src/writeenv.c create mode 100644 src/curl-7.12.2/src/writeenv.h create mode 100644 src/curl-7.12.2/src/writeout.c create mode 100644 src/curl-7.12.2/src/writeout.h create mode 100644 src/curl-7.12.2/tests/FILEFORMAT create mode 100644 src/curl-7.12.2/tests/Makefile.am create mode 100644 src/curl-7.12.2/tests/Makefile.in create mode 100644 src/curl-7.12.2/tests/README create mode 100644 src/curl-7.12.2/tests/data/Makefile.am create mode 100644 src/curl-7.12.2/tests/data/Makefile.in create mode 100644 src/curl-7.12.2/tests/data/test1 create mode 100644 src/curl-7.12.2/tests/data/test10 create mode 100644 src/curl-7.12.2/tests/data/test100 create mode 100644 src/curl-7.12.2/tests/data/test101 create mode 100644 src/curl-7.12.2/tests/data/test102 create mode 100644 src/curl-7.12.2/tests/data/test103 create mode 100644 src/curl-7.12.2/tests/data/test104 create mode 100644 src/curl-7.12.2/tests/data/test105 create mode 100644 src/curl-7.12.2/tests/data/test106 create mode 100644 src/curl-7.12.2/tests/data/test107 create mode 100644 src/curl-7.12.2/tests/data/test108 create mode 100644 src/curl-7.12.2/tests/data/test109 create mode 100644 src/curl-7.12.2/tests/data/test11 create mode 100644 src/curl-7.12.2/tests/data/test110 create mode 100644 src/curl-7.12.2/tests/data/test111 create mode 100644 src/curl-7.12.2/tests/data/test112 create mode 100644 src/curl-7.12.2/tests/data/test113 create mode 100644 src/curl-7.12.2/tests/data/test114 create mode 100644 src/curl-7.12.2/tests/data/test115 create mode 100644 src/curl-7.12.2/tests/data/test116 create mode 100644 src/curl-7.12.2/tests/data/test117 create mode 100644 src/curl-7.12.2/tests/data/test118 create mode 100644 src/curl-7.12.2/tests/data/test119 create mode 100644 src/curl-7.12.2/tests/data/test12 create mode 100644 src/curl-7.12.2/tests/data/test120 create mode 100644 src/curl-7.12.2/tests/data/test121 create mode 100644 src/curl-7.12.2/tests/data/test122 create mode 100644 src/curl-7.12.2/tests/data/test123 create mode 100644 src/curl-7.12.2/tests/data/test124 create mode 100644 src/curl-7.12.2/tests/data/test125 create mode 100644 src/curl-7.12.2/tests/data/test126 create mode 100644 src/curl-7.12.2/tests/data/test127 create mode 100644 src/curl-7.12.2/tests/data/test128 create mode 100644 src/curl-7.12.2/tests/data/test13 create mode 100644 src/curl-7.12.2/tests/data/test130 create mode 100644 src/curl-7.12.2/tests/data/test131 create mode 100644 src/curl-7.12.2/tests/data/test132 create mode 100644 src/curl-7.12.2/tests/data/test133 create mode 100644 src/curl-7.12.2/tests/data/test134 create mode 100644 src/curl-7.12.2/tests/data/test135 create mode 100644 src/curl-7.12.2/tests/data/test136 create mode 100644 src/curl-7.12.2/tests/data/test137 create mode 100644 src/curl-7.12.2/tests/data/test138 create mode 100644 src/curl-7.12.2/tests/data/test139 create mode 100644 src/curl-7.12.2/tests/data/test14 create mode 100644 src/curl-7.12.2/tests/data/test140 create mode 100644 src/curl-7.12.2/tests/data/test141 create mode 100644 src/curl-7.12.2/tests/data/test142 create mode 100644 src/curl-7.12.2/tests/data/test143 create mode 100644 src/curl-7.12.2/tests/data/test144 create mode 100644 src/curl-7.12.2/tests/data/test145 create mode 100644 src/curl-7.12.2/tests/data/test146 create mode 100644 src/curl-7.12.2/tests/data/test147 create mode 100644 src/curl-7.12.2/tests/data/test148 create mode 100644 src/curl-7.12.2/tests/data/test149 create mode 100644 src/curl-7.12.2/tests/data/test15 create mode 100644 src/curl-7.12.2/tests/data/test150 create mode 100644 src/curl-7.12.2/tests/data/test151 create mode 100644 src/curl-7.12.2/tests/data/test152 create mode 100644 src/curl-7.12.2/tests/data/test153 create mode 100644 src/curl-7.12.2/tests/data/test154 create mode 100644 src/curl-7.12.2/tests/data/test155 create mode 100644 src/curl-7.12.2/tests/data/test156 create mode 100644 src/curl-7.12.2/tests/data/test157 create mode 100644 src/curl-7.12.2/tests/data/test158 create mode 100644 src/curl-7.12.2/tests/data/test159 create mode 100644 src/curl-7.12.2/tests/data/test16 create mode 100644 src/curl-7.12.2/tests/data/test160 create mode 100644 src/curl-7.12.2/tests/data/test161 create mode 100644 src/curl-7.12.2/tests/data/test162 create mode 100644 src/curl-7.12.2/tests/data/test163 create mode 100644 src/curl-7.12.2/tests/data/test164 create mode 100644 src/curl-7.12.2/tests/data/test165 create mode 100644 src/curl-7.12.2/tests/data/test166 create mode 100644 src/curl-7.12.2/tests/data/test167 create mode 100644 src/curl-7.12.2/tests/data/test168 create mode 100644 src/curl-7.12.2/tests/data/test169 create mode 100644 src/curl-7.12.2/tests/data/test17 create mode 100644 src/curl-7.12.2/tests/data/test170 create mode 100644 src/curl-7.12.2/tests/data/test171 create mode 100644 src/curl-7.12.2/tests/data/test172 create mode 100644 src/curl-7.12.2/tests/data/test173 create mode 100644 src/curl-7.12.2/tests/data/test174 create mode 100644 src/curl-7.12.2/tests/data/test175 create mode 100644 src/curl-7.12.2/tests/data/test176 create mode 100644 src/curl-7.12.2/tests/data/test177 create mode 100644 src/curl-7.12.2/tests/data/test178 create mode 100644 src/curl-7.12.2/tests/data/test179 create mode 100644 src/curl-7.12.2/tests/data/test18 create mode 100644 src/curl-7.12.2/tests/data/test180 create mode 100644 src/curl-7.12.2/tests/data/test181 create mode 100644 src/curl-7.12.2/tests/data/test182 create mode 100644 src/curl-7.12.2/tests/data/test183 create mode 100644 src/curl-7.12.2/tests/data/test184 create mode 100644 src/curl-7.12.2/tests/data/test185 create mode 100644 src/curl-7.12.2/tests/data/test186 create mode 100644 src/curl-7.12.2/tests/data/test187 create mode 100644 src/curl-7.12.2/tests/data/test188 create mode 100644 src/curl-7.12.2/tests/data/test189 create mode 100644 src/curl-7.12.2/tests/data/test19 create mode 100644 src/curl-7.12.2/tests/data/test190 create mode 100644 src/curl-7.12.2/tests/data/test191 create mode 100644 src/curl-7.12.2/tests/data/test2 create mode 100644 src/curl-7.12.2/tests/data/test20 create mode 100644 src/curl-7.12.2/tests/data/test200 create mode 100644 src/curl-7.12.2/tests/data/test201 create mode 100644 src/curl-7.12.2/tests/data/test202 create mode 100644 src/curl-7.12.2/tests/data/test203 create mode 100644 src/curl-7.12.2/tests/data/test204 create mode 100644 src/curl-7.12.2/tests/data/test205 create mode 100644 src/curl-7.12.2/tests/data/test21 create mode 100644 src/curl-7.12.2/tests/data/test22 create mode 100644 src/curl-7.12.2/tests/data/test23 create mode 100644 src/curl-7.12.2/tests/data/test24 create mode 100644 src/curl-7.12.2/tests/data/test25 create mode 100644 src/curl-7.12.2/tests/data/test26 create mode 100644 src/curl-7.12.2/tests/data/test27 create mode 100644 src/curl-7.12.2/tests/data/test28 create mode 100644 src/curl-7.12.2/tests/data/test29 create mode 100644 src/curl-7.12.2/tests/data/test3 create mode 100644 src/curl-7.12.2/tests/data/test30 create mode 100644 src/curl-7.12.2/tests/data/test300 create mode 100644 src/curl-7.12.2/tests/data/test301 create mode 100644 src/curl-7.12.2/tests/data/test302 create mode 100644 src/curl-7.12.2/tests/data/test303 create mode 100644 src/curl-7.12.2/tests/data/test304 create mode 100644 src/curl-7.12.2/tests/data/test305 create mode 100644 src/curl-7.12.2/tests/data/test306 create mode 100644 src/curl-7.12.2/tests/data/test31 create mode 100644 src/curl-7.12.2/tests/data/test32 create mode 100644 src/curl-7.12.2/tests/data/test33 create mode 100644 src/curl-7.12.2/tests/data/test34 create mode 100644 src/curl-7.12.2/tests/data/test36 create mode 100644 src/curl-7.12.2/tests/data/test37 create mode 100644 src/curl-7.12.2/tests/data/test38 create mode 100644 src/curl-7.12.2/tests/data/test39 create mode 100644 src/curl-7.12.2/tests/data/test4 create mode 100644 src/curl-7.12.2/tests/data/test40 create mode 100644 src/curl-7.12.2/tests/data/test41 create mode 100644 src/curl-7.12.2/tests/data/test42 create mode 100644 src/curl-7.12.2/tests/data/test43 create mode 100644 src/curl-7.12.2/tests/data/test44 create mode 100644 src/curl-7.12.2/tests/data/test45 create mode 100644 src/curl-7.12.2/tests/data/test46 create mode 100644 src/curl-7.12.2/tests/data/test47 create mode 100644 src/curl-7.12.2/tests/data/test48 create mode 100644 src/curl-7.12.2/tests/data/test49 create mode 100644 src/curl-7.12.2/tests/data/test5 create mode 100644 src/curl-7.12.2/tests/data/test50 create mode 100644 src/curl-7.12.2/tests/data/test500 create mode 100644 src/curl-7.12.2/tests/data/test501 create mode 100644 src/curl-7.12.2/tests/data/test502 create mode 100644 src/curl-7.12.2/tests/data/test503 create mode 100644 src/curl-7.12.2/tests/data/test504 create mode 100644 src/curl-7.12.2/tests/data/test505 create mode 100644 src/curl-7.12.2/tests/data/test506 create mode 100644 src/curl-7.12.2/tests/data/test507 create mode 100644 src/curl-7.12.2/tests/data/test508 create mode 100644 src/curl-7.12.2/tests/data/test509 create mode 100644 src/curl-7.12.2/tests/data/test51 create mode 100644 src/curl-7.12.2/tests/data/test510 create mode 100644 src/curl-7.12.2/tests/data/test511 create mode 100644 src/curl-7.12.2/tests/data/test512 create mode 100644 src/curl-7.12.2/tests/data/test513 create mode 100644 src/curl-7.12.2/tests/data/test514 create mode 100644 src/curl-7.12.2/tests/data/test52 create mode 100644 src/curl-7.12.2/tests/data/test53 create mode 100644 src/curl-7.12.2/tests/data/test54 create mode 100644 src/curl-7.12.2/tests/data/test55 create mode 100644 src/curl-7.12.2/tests/data/test56 create mode 100644 src/curl-7.12.2/tests/data/test57 create mode 100644 src/curl-7.12.2/tests/data/test58 create mode 100644 src/curl-7.12.2/tests/data/test59 create mode 100644 src/curl-7.12.2/tests/data/test6 create mode 100644 src/curl-7.12.2/tests/data/test60 create mode 100644 src/curl-7.12.2/tests/data/test61 create mode 100644 src/curl-7.12.2/tests/data/test62 create mode 100644 src/curl-7.12.2/tests/data/test63 create mode 100644 src/curl-7.12.2/tests/data/test64 create mode 100644 src/curl-7.12.2/tests/data/test65 create mode 100644 src/curl-7.12.2/tests/data/test66 create mode 100644 src/curl-7.12.2/tests/data/test67 create mode 100644 src/curl-7.12.2/tests/data/test68 create mode 100644 src/curl-7.12.2/tests/data/test69 create mode 100644 src/curl-7.12.2/tests/data/test7 create mode 100644 src/curl-7.12.2/tests/data/test70 create mode 100644 src/curl-7.12.2/tests/data/test71 create mode 100644 src/curl-7.12.2/tests/data/test72 create mode 100644 src/curl-7.12.2/tests/data/test73 create mode 100644 src/curl-7.12.2/tests/data/test74 create mode 100644 src/curl-7.12.2/tests/data/test75 create mode 100644 src/curl-7.12.2/tests/data/test76 create mode 100644 src/curl-7.12.2/tests/data/test77 create mode 100644 src/curl-7.12.2/tests/data/test78 create mode 100644 src/curl-7.12.2/tests/data/test79 create mode 100644 src/curl-7.12.2/tests/data/test8 create mode 100644 src/curl-7.12.2/tests/data/test80 create mode 100644 src/curl-7.12.2/tests/data/test81 create mode 100644 src/curl-7.12.2/tests/data/test82 create mode 100644 src/curl-7.12.2/tests/data/test83 create mode 100644 src/curl-7.12.2/tests/data/test84 create mode 100644 src/curl-7.12.2/tests/data/test85 create mode 100644 src/curl-7.12.2/tests/data/test86 create mode 100644 src/curl-7.12.2/tests/data/test87 create mode 100644 src/curl-7.12.2/tests/data/test88 create mode 100644 src/curl-7.12.2/tests/data/test89 create mode 100644 src/curl-7.12.2/tests/data/test9 create mode 100644 src/curl-7.12.2/tests/data/test90 create mode 100644 src/curl-7.12.2/tests/data/test91 create mode 100644 src/curl-7.12.2/tests/data/test92 create mode 100644 src/curl-7.12.2/tests/data/test93 create mode 100644 src/curl-7.12.2/tests/data/test94 create mode 100644 src/curl-7.12.2/tests/data/test95 create mode 100644 src/curl-7.12.2/tests/data/test97 create mode 100644 src/curl-7.12.2/tests/data/test98 create mode 100644 src/curl-7.12.2/tests/data/test99 create mode 100644 src/curl-7.12.2/tests/ftpserver.pl create mode 100644 src/curl-7.12.2/tests/ftpsserver.pl create mode 100644 src/curl-7.12.2/tests/getpart.pm create mode 100644 src/curl-7.12.2/tests/httpserver.pl create mode 100644 src/curl-7.12.2/tests/httpsserver.pl create mode 100644 src/curl-7.12.2/tests/libtest/Makefile.am create mode 100644 src/curl-7.12.2/tests/libtest/Makefile.in create mode 100644 src/curl-7.12.2/tests/libtest/first.c create mode 100644 src/curl-7.12.2/tests/libtest/lib500.c create mode 100644 src/curl-7.12.2/tests/libtest/lib501.c create mode 100644 src/curl-7.12.2/tests/libtest/lib502.c create mode 100644 src/curl-7.12.2/tests/libtest/lib503.c create mode 100644 src/curl-7.12.2/tests/libtest/lib504.c create mode 100644 src/curl-7.12.2/tests/libtest/lib505.c create mode 100644 src/curl-7.12.2/tests/libtest/lib506.c create mode 100644 src/curl-7.12.2/tests/libtest/lib507.c create mode 100644 src/curl-7.12.2/tests/libtest/lib508.c create mode 100644 src/curl-7.12.2/tests/libtest/lib509.c create mode 100644 src/curl-7.12.2/tests/libtest/lib510.c create mode 100644 src/curl-7.12.2/tests/libtest/lib511.c create mode 100644 src/curl-7.12.2/tests/libtest/lib512.c create mode 100644 src/curl-7.12.2/tests/libtest/lib513.c create mode 100644 src/curl-7.12.2/tests/libtest/lib514.c create mode 100644 src/curl-7.12.2/tests/libtest/test.h create mode 100644 src/curl-7.12.2/tests/memanalyze.pl create mode 100644 src/curl-7.12.2/tests/runtests.pl create mode 100644 src/curl-7.12.2/tests/server/Makefile.am create mode 100644 src/curl-7.12.2/tests/server/Makefile.in create mode 100644 src/curl-7.12.2/tests/server/getpart.c create mode 100644 src/curl-7.12.2/tests/server/getpart.h create mode 100644 src/curl-7.12.2/tests/server/sws.c create mode 100644 src/curl-7.12.2/tests/stunnel.pem create mode 100644 src/curl-7.12.2/tests/testcurl.pl create mode 100644 src/docs/BotScriptCommands create mode 100644 src/docs/Changelog create mode 100644 src/docs/Notes create mode 100644 src/docs/QA-Changelog create mode 100644 src/docs/Todo create mode 100644 src/extractfuncs/ChangeLog create mode 100644 src/extractfuncs/Conscript create mode 100644 src/extractfuncs/Construct create mode 100644 src/extractfuncs/extractfuncs.bat create mode 100644 src/extractfuncs/extractfuncs.c create mode 100644 src/extractfuncs/extractfuncs.vcproj create mode 100644 src/extractfuncs/l_log.c create mode 100644 src/extractfuncs/l_log.h create mode 100644 src/extractfuncs/l_memory.c create mode 100644 src/extractfuncs/l_memory.h create mode 100644 src/extractfuncs/l_precomp.c create mode 100644 src/extractfuncs/l_precomp.h create mode 100644 src/extractfuncs/l_script.c create mode 100644 src/extractfuncs/l_script.h create mode 100644 src/ft2/LICENSE.txt create mode 100644 src/ft2/ahangles.c create mode 100644 src/ft2/ahangles.h create mode 100644 src/ft2/ahglobal.c create mode 100644 src/ft2/ahglobal.h create mode 100644 src/ft2/ahglyph.c create mode 100644 src/ft2/ahglyph.h create mode 100644 src/ft2/ahhint.c create mode 100644 src/ft2/ahhint.h create mode 100644 src/ft2/ahloader.h create mode 100644 src/ft2/ahmodule.c create mode 100644 src/ft2/ahmodule.h create mode 100644 src/ft2/ahoptim.c create mode 100644 src/ft2/ahoptim.h create mode 100644 src/ft2/ahtypes.h create mode 100644 src/ft2/autohint.h create mode 100644 src/ft2/freetype.h create mode 100644 src/ft2/ftcalc.c create mode 100644 src/ft2/ftcalc.h create mode 100644 src/ft2/ftconfig.h create mode 100644 src/ft2/ftdebug.c create mode 100644 src/ft2/ftdebug.h create mode 100644 src/ft2/ftdriver.h create mode 100644 src/ft2/fterrors.h create mode 100644 src/ft2/ftextend.c create mode 100644 src/ft2/ftextend.h create mode 100644 src/ft2/ftglyph.c create mode 100644 src/ft2/ftglyph.h create mode 100644 src/ft2/ftgrays.c create mode 100644 src/ft2/ftgrays.h create mode 100644 src/ft2/ftimage.h create mode 100644 src/ft2/ftinit.c create mode 100644 src/ft2/ftlist.c create mode 100644 src/ft2/ftlist.h create mode 100644 src/ft2/ftmemory.h create mode 100644 src/ft2/ftmm.c create mode 100644 src/ft2/ftmm.h create mode 100644 src/ft2/ftmodule.h create mode 100644 src/ft2/ftnames.c create mode 100644 src/ft2/ftnames.h create mode 100644 src/ft2/ftobjs.c create mode 100644 src/ft2/ftobjs.h create mode 100644 src/ft2/ftoption.h create mode 100644 src/ft2/ftoutln.c create mode 100644 src/ft2/ftoutln.h create mode 100644 src/ft2/ftraster.c create mode 100644 src/ft2/ftraster.h create mode 100644 src/ft2/ftrend1.c create mode 100644 src/ft2/ftrend1.h create mode 100644 src/ft2/ftrender.h create mode 100644 src/ft2/ftsmooth.c create mode 100644 src/ft2/ftsmooth.h create mode 100644 src/ft2/ftstream.c create mode 100644 src/ft2/ftstream.h create mode 100644 src/ft2/ftsystem.c create mode 100644 src/ft2/ftsystem.h create mode 100644 src/ft2/fttypes.h create mode 100644 src/ft2/psnames.h create mode 100644 src/ft2/sfdriver.c create mode 100644 src/ft2/sfdriver.h create mode 100644 src/ft2/sfnt.h create mode 100644 src/ft2/sfobjs.c create mode 100644 src/ft2/sfobjs.h create mode 100644 src/ft2/t1tables.h create mode 100644 src/ft2/ttcmap.c create mode 100644 src/ft2/ttcmap.h create mode 100644 src/ft2/ttdriver.c create mode 100644 src/ft2/ttdriver.h create mode 100644 src/ft2/tterrors.h create mode 100644 src/ft2/ttgload.c create mode 100644 src/ft2/ttgload.h create mode 100644 src/ft2/ttinterp.c create mode 100644 src/ft2/ttinterp.h create mode 100644 src/ft2/ttload.c create mode 100644 src/ft2/ttload.h create mode 100644 src/ft2/ttnameid.h create mode 100644 src/ft2/ttobjs.c create mode 100644 src/ft2/ttobjs.h create mode 100644 src/ft2/ttpload.c create mode 100644 src/ft2/ttpload.h create mode 100644 src/ft2/ttpost.c create mode 100644 src/ft2/ttpost.h create mode 100644 src/ft2/ttsbit.c create mode 100644 src/ft2/ttsbit.h create mode 100644 src/ft2/tttables.h create mode 100644 src/ft2/tttags.h create mode 100644 src/ft2/tttypes.h create mode 100644 src/game/be_aas.h create mode 100644 src/game/be_ai_char.h create mode 100644 src/game/be_ai_chat.h create mode 100644 src/game/be_ai_gen.h create mode 100644 src/game/be_ai_goal.h create mode 100644 src/game/be_ai_move.h create mode 100644 src/game/be_ai_weap.h create mode 100644 src/game/be_ea.h create mode 100644 src/game/bg_animation.c create mode 100644 src/game/bg_animgroup.c create mode 100644 src/game/bg_campaign.c create mode 100644 src/game/bg_character.c create mode 100644 src/game/bg_classes.c create mode 100644 src/game/bg_local.h create mode 100644 src/game/bg_misc.c create mode 100644 src/game/bg_pmove.c create mode 100644 src/game/bg_public.h create mode 100644 src/game/bg_slidemove.c create mode 100644 src/game/bg_sscript.c create mode 100644 src/game/bg_stats.c create mode 100644 src/game/bg_tracemap.c create mode 100644 src/game/botlib.h create mode 100644 src/game/g_active.c create mode 100644 src/game/g_alarm.c create mode 100644 src/game/g_antilag.c create mode 100644 src/game/g_bot.c create mode 100644 src/game/g_buddy_list.c create mode 100644 src/game/g_character.c create mode 100644 src/game/g_client.c create mode 100644 src/game/g_cmds.c create mode 100644 src/game/g_cmds_ext.c create mode 100644 src/game/g_combat.c create mode 100644 src/game/g_config.c create mode 100644 src/game/g_fireteams.c create mode 100644 src/game/g_func_decs.h create mode 100644 src/game/g_funcs.h create mode 100644 src/game/g_items.c create mode 100644 src/game/g_local.h create mode 100644 src/game/g_main.c create mode 100644 src/game/g_match.c create mode 100644 src/game/g_mem.c create mode 100644 src/game/g_misc.c create mode 100644 src/game/g_missile.c create mode 100644 src/game/g_mover.c create mode 100644 src/game/g_multiview.c create mode 100644 src/game/g_props.c create mode 100644 src/game/g_public.h create mode 100644 src/game/g_referee.c create mode 100644 src/game/g_save.c create mode 100644 src/game/g_script.c create mode 100644 src/game/g_script_actions.c create mode 100644 src/game/g_session.c create mode 100644 src/game/g_spawn.c create mode 100644 src/game/g_stats.c create mode 100644 src/game/g_sv_entities.c create mode 100644 src/game/g_svcmds.c create mode 100644 src/game/g_syscalls.c create mode 100644 src/game/g_systemmsg.c create mode 100644 src/game/g_target.c create mode 100644 src/game/g_team.c create mode 100644 src/game/g_team.h create mode 100644 src/game/g_teammapdata.c create mode 100644 src/game/g_trigger.c create mode 100644 src/game/g_utils.c create mode 100644 src/game/g_vote.c create mode 100644 src/game/g_weapon.c create mode 100644 src/game/game.def create mode 100644 src/game/game.vcproj create mode 100644 src/game/q_math.c create mode 100644 src/game/q_shared.c create mode 100644 src/game/q_shared.h create mode 100644 src/game/surfaceflags.h create mode 100644 src/jpeg-6/README create mode 100644 src/jpeg-6/jcapimin.c create mode 100644 src/jpeg-6/jccoefct.c create mode 100644 src/jpeg-6/jccolor.c create mode 100644 src/jpeg-6/jcdctmgr.c create mode 100644 src/jpeg-6/jchuff.c create mode 100644 src/jpeg-6/jchuff.h create mode 100644 src/jpeg-6/jcinit.c create mode 100644 src/jpeg-6/jcmainct.c create mode 100644 src/jpeg-6/jcmarker.c create mode 100644 src/jpeg-6/jcmaster.c create mode 100644 src/jpeg-6/jcomapi.c create mode 100644 src/jpeg-6/jconfig.h create mode 100644 src/jpeg-6/jcparam.c create mode 100644 src/jpeg-6/jcphuff.c create mode 100644 src/jpeg-6/jcprepct.c create mode 100644 src/jpeg-6/jcsample.c create mode 100644 src/jpeg-6/jctrans.c create mode 100644 src/jpeg-6/jdapimin.c create mode 100644 src/jpeg-6/jdapistd.c create mode 100644 src/jpeg-6/jdatadst.c create mode 100644 src/jpeg-6/jdatasrc.c create mode 100644 src/jpeg-6/jdcoefct.c create mode 100644 src/jpeg-6/jdcolor.c create mode 100644 src/jpeg-6/jdct.h create mode 100644 src/jpeg-6/jddctmgr.c create mode 100644 src/jpeg-6/jdhuff.c create mode 100644 src/jpeg-6/jdhuff.h create mode 100644 src/jpeg-6/jdinput.c create mode 100644 src/jpeg-6/jdmainct.c create mode 100644 src/jpeg-6/jdmarker.c create mode 100644 src/jpeg-6/jdmaster.c create mode 100644 src/jpeg-6/jdpostct.c create mode 100644 src/jpeg-6/jdsample.c create mode 100644 src/jpeg-6/jdtrans.c create mode 100644 src/jpeg-6/jerror.c create mode 100644 src/jpeg-6/jerror.h create mode 100644 src/jpeg-6/jfdctflt.c create mode 100644 src/jpeg-6/jidctflt.c create mode 100644 src/jpeg-6/jinclude.h create mode 100644 src/jpeg-6/jmemmgr.c create mode 100644 src/jpeg-6/jmemnobs.c create mode 100644 src/jpeg-6/jmemsys.h create mode 100644 src/jpeg-6/jmorecfg.h create mode 100644 src/jpeg-6/jpegint.h create mode 100644 src/jpeg-6/jpeglib.h create mode 100644 src/jpeg-6/jutils.c create mode 100644 src/jpeg-6/jversion.h create mode 100644 src/mac/AGLUtils.cpp create mode 100644 src/mac/AGLUtils.h create mode 100644 src/mac/CDrawSprocket.cpp create mode 100644 src/mac/CDrawSprocket.h create mode 100644 src/mac/CGameLibMP.mcp create mode 100644 src/mac/CGameLib_Debug.h create mode 100644 src/mac/CGameLib_Release.h create mode 100644 src/mac/CarbonMouse.cpp create mode 100644 src/mac/CarbonMouse.h create mode 100644 src/mac/Icon.icns create mode 100644 src/mac/Info.plc create mode 100644 src/mac/Info.plist create mode 100644 src/mac/MacPrefix.h create mode 100644 src/mac/MacPrefixCommon.h create mode 100644 src/mac/MacPrefixDebug.h create mode 100644 src/mac/MacPrefixDebugMach.h create mode 100644 src/mac/MacPrefixDedicatedDebug.h create mode 100644 src/mac/MacPrefixDedicatedRelease.h create mode 100644 src/mac/MacPrefixMach.h create mode 100644 src/mac/MacPrefix_GameSpecific.h create mode 100644 src/mac/MacPrefs.cpp create mode 100644 src/mac/MacPrefs.h create mode 100644 src/mac/QAGameLibMP.mcp create mode 100644 src/mac/QAGameLib_Debug.h create mode 100644 src/mac/QAGameLib_Release.h create mode 100644 src/mac/UILibMP.mcp create mode 100644 src/mac/UILib_Debug.h create mode 100644 src/mac/UILib_Release.h create mode 100644 src/mac/Wolf ET.mcp create mode 100644 src/mac/WolfET.xcodeproj/project.pbxproj create mode 100644 src/mac/Wolfenstein ET.rsrc create mode 100644 src/mac/cgame.xcodeproj/project.pbxproj create mode 100644 src/mac/deployment.sh create mode 100644 src/mac/exports.def create mode 100644 src/mac/game.xcodeproj/project.pbxproj create mode 100644 src/mac/glext.h create mode 100644 src/mac/mac_console.c create mode 100644 src/mac/mac_dedicated_main.c create mode 100644 src/mac/mac_event.cpp create mode 100644 src/mac/mac_files.cpp create mode 100644 src/mac/mac_glimp.cpp create mode 100644 src/mac/mac_glimp.h create mode 100644 src/mac/mac_input.cpp create mode 100644 src/mac/mac_input.h create mode 100644 src/mac/mac_local.h create mode 100644 src/mac/mac_main.cpp create mode 100644 src/mac/mac_net.cpp create mode 100644 src/mac/mac_qgl.c create mode 100644 src/mac/mac_snddma.c create mode 100644 src/mac/mac_specific.cpp create mode 100644 src/mac/pk3.icns create mode 100644 src/mac/setup/ReadMe.txt create mode 100644 src/mac/setup/build-setup.py create mode 100644 src/mac/setup/mycmds.py create mode 100644 src/mac/setup/pbsetup.pmsp create mode 100644 src/mac/setup/setup.pmsp create mode 100644 src/mac/ui.xcodeproj/project.pbxproj create mode 100644 src/makebundle.sh create mode 100644 src/null/null_client.c create mode 100644 src/null/null_input.c create mode 100644 src/null/null_snddma.c create mode 100644 src/qcommon/cm_load.c create mode 100644 src/qcommon/cm_local.h create mode 100644 src/qcommon/cm_patch.c create mode 100644 src/qcommon/cm_patch.h create mode 100644 src/qcommon/cm_polylib.c create mode 100644 src/qcommon/cm_polylib.h create mode 100644 src/qcommon/cm_public.h create mode 100644 src/qcommon/cm_test.c create mode 100644 src/qcommon/cm_trace.c create mode 100644 src/qcommon/cmd.c create mode 100644 src/qcommon/common.c create mode 100644 src/qcommon/cvar.c create mode 100644 src/qcommon/dl_local.h create mode 100644 src/qcommon/dl_main.c create mode 100644 src/qcommon/dl_main_curl.c create mode 100644 src/qcommon/dl_main_stubs.c create mode 100644 src/qcommon/dl_public.h create mode 100644 src/qcommon/files.c create mode 100644 src/qcommon/huffman.c create mode 100644 src/qcommon/md4.c create mode 100644 src/qcommon/msg.c create mode 100644 src/qcommon/net_chan.c create mode 100644 src/qcommon/qcommon.h create mode 100644 src/qcommon/qfiles.h create mode 100644 src/qcommon/unzip.c create mode 100644 src/qcommon/unzip.h create mode 100644 src/qcommon/vm.c create mode 100644 src/qcommon/vm_interpreted.c create mode 100644 src/qcommon/vm_local.h create mode 100644 src/qcommon/vm_x86.c create mode 100644 src/renderer/anorms256.h create mode 100644 src/renderer/glext.h create mode 100644 src/renderer/qgl.h create mode 100644 src/renderer/qgl_linked.h create mode 100644 src/renderer/ref_trin.def create mode 100644 src/renderer/renderer.vcproj create mode 100644 src/renderer/tr_animation_mdm.c create mode 100644 src/renderer/tr_animation_mds.c create mode 100644 src/renderer/tr_backend.c create mode 100644 src/renderer/tr_bsp.c create mode 100644 src/renderer/tr_cmds.c create mode 100644 src/renderer/tr_cmesh.c create mode 100644 src/renderer/tr_curve.c create mode 100644 src/renderer/tr_decals.c create mode 100644 src/renderer/tr_flares.c create mode 100644 src/renderer/tr_font.c create mode 100644 src/renderer/tr_image.c create mode 100644 src/renderer/tr_init.c create mode 100644 src/renderer/tr_light.c create mode 100644 src/renderer/tr_local.h create mode 100644 src/renderer/tr_main.c create mode 100644 src/renderer/tr_marks.c create mode 100644 src/renderer/tr_mesh.c create mode 100644 src/renderer/tr_model.c create mode 100644 src/renderer/tr_noise.c create mode 100644 src/renderer/tr_public.h create mode 100644 src/renderer/tr_scene.c create mode 100644 src/renderer/tr_shade.c create mode 100644 src/renderer/tr_shade_calc.c create mode 100644 src/renderer/tr_shader.c create mode 100644 src/renderer/tr_shadows.c create mode 100644 src/renderer/tr_sky.c create mode 100644 src/renderer/tr_surface.c create mode 100644 src/renderer/tr_world.c create mode 100644 src/scons_utils.py create mode 100644 src/server/server.h create mode 100644 src/server/sv_bot.c create mode 100644 src/server/sv_ccmds.c create mode 100644 src/server/sv_client.c create mode 100644 src/server/sv_game.c create mode 100644 src/server/sv_init.c create mode 100644 src/server/sv_main.c create mode 100644 src/server/sv_net_chan.c create mode 100644 src/server/sv_snapshot.c create mode 100644 src/server/sv_world.c create mode 100644 src/splines/Splines.vcproj create mode 100644 src/splines/math_angles.cpp create mode 100644 src/splines/math_angles.h create mode 100644 src/splines/math_matrix.cpp create mode 100644 src/splines/math_matrix.h create mode 100644 src/splines/math_quaternion.cpp create mode 100644 src/splines/math_quaternion.h create mode 100644 src/splines/math_vector.cpp create mode 100644 src/splines/math_vector.h create mode 100644 src/splines/q_parse.cpp create mode 100644 src/splines/q_shared.c create mode 100644 src/splines/q_splineshared.h create mode 100644 src/splines/splines.cpp create mode 100644 src/splines/splines.h create mode 100644 src/splines/util_list.h create mode 100644 src/splines/util_str.cpp create mode 100644 src/splines/util_str.h create mode 100644 src/ui/keycodes.h create mode 100644 src/ui/ui.def create mode 100644 src/ui/ui.vcproj create mode 100644 src/ui/ui_atoms.c create mode 100644 src/ui/ui_gameinfo.c create mode 100644 src/ui/ui_loadpanel.c create mode 100644 src/ui/ui_local.h create mode 100644 src/ui/ui_main.c create mode 100644 src/ui/ui_players.c create mode 100644 src/ui/ui_public.h create mode 100644 src/ui/ui_shared.c create mode 100644 src/ui/ui_shared.h create mode 100644 src/ui/ui_syscalls.c create mode 100644 src/ui/ui_util.c create mode 100644 src/unix/ChangeLog create mode 100644 src/unix/ftol.nasm create mode 100644 src/unix/linux_common.c create mode 100644 src/unix/linux_glimp.c create mode 100644 src/unix/linux_joystick.c create mode 100644 src/unix/linux_local.h create mode 100644 src/unix/linux_qgl.c create mode 100644 src/unix/linux_signals.c create mode 100644 src/unix/linux_snd.c create mode 100644 src/unix/matha.spp create mode 100644 src/unix/qasm.h create mode 100644 src/unix/snapvector.asm create mode 100644 src/unix/snd_mixa.spp create mode 100644 src/unix/staticlinkcpp.sh create mode 100644 src/unix/symbolcheck.pl create mode 100644 src/unix/unix_glw.h create mode 100644 src/unix/unix_main.c create mode 100644 src/unix/unix_net.c create mode 100644 src/unix/unix_shared.c create mode 100644 src/unix/wolf.vpw create mode 100644 src/win32/background.bmp create mode 100644 src/win32/clear.bmp create mode 100644 src/win32/glw_win.h create mode 100644 src/win32/qe3.ico create mode 100644 src/win32/resource.h create mode 100644 src/win32/win_eh.cpp create mode 100644 src/win32/win_gamma.c create mode 100644 src/win32/win_glimp.c create mode 100644 src/win32/win_input.c create mode 100644 src/win32/win_local.h create mode 100644 src/win32/win_main.c create mode 100644 src/win32/win_net.c create mode 100644 src/win32/win_qgl.c create mode 100644 src/win32/win_shared.c create mode 100644 src/win32/win_snd.c create mode 100644 src/win32/win_syscon.c create mode 100644 src/win32/win_wndproc.c create mode 100644 src/win32/winquake.rc create mode 100644 src/win32/wolfet.ico create mode 100644 src/wolf.sln create mode 100644 src/wolf.vcproj create mode 100644 uncrustify.cfg diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..f1579a5 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,643 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + + +ADDITIONAL TERMS APPLICABLE TO THE WOLFENSTEIN: ENEMY TERRITORY GPL SOURCE CODE. + + The following additional terms (“Additional Terms”) supplement and modify the GNU General Public License, Version 3 (“GPL”) applicable to the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). In addition to the terms and conditions of the GPL, the Wolf ET Source Code is subject to the further restrictions below. + +1. Replacement of Section 15. Section 15 of the GPL shall be deleted in its entirety and replaced with the following: + +“15. Disclaimer of Warranty. + +THE PROGRAM IS PROVIDED WITHOUT ANY WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE AND MERCHANTABILITY. THE PROGRAM IS BEING DELIVERED OR MADE AVAILABLE “AS IS”, “WITH ALL FAULTS” AND WITHOUT WARRANTY OR REPRESENTATION. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.” + +2. Replacement of Section 16. Section 16 of the GPL shall be deleted in its entirety and replaced with the following: + +“16. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES SHALL ANY COPYRIGHT HOLDER OR ITS AFFILIATES, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, FOR ANY DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES ARISING FROM, OUT OF OR IN CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM OR OTHER DEALINGS WITH THE PROGRAM(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), WHETHER OR NOT ANY COPYRIGHT HOLDER OR SUCH OTHER PARTY RECEIVES NOTICE OF ANY SUCH DAMAGES AND WHETHER OR NOT SUCH DAMAGES COULD HAVE BEEN FORESEEN.” + +3. LEGAL NOTICES; NO TRADEMARK LICENSE; ORIGIN. You must reproduce faithfully all trademark, copyright and other proprietary and legal notices on any copies of the Program or any other required author attributions. This license does not grant you rights to use any copyright holder or any other party’s name, logo, or trademarks. Neither the name of the copyright holder or its affiliates, or any other party who modifies and/or conveys the Program may be used to endorse or promote products derived from this software without specific prior written permission. The origin of the Program must not be misrepresented; you must not claim that you wrote the original Program. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original Program. + +4. INDEMNIFICATION. IF YOU CONVEY A COVERED WORK AND AGREE WITH ANY RECIPIENT OF THAT COVERED WORK THAT YOU WILL ASSUME ANY LIABILITY FOR THAT COVERED WORK, YOU HEREBY AGREE TO INDEMNIFY, DEFEND AND HOLD HARMLESS THE OTHER LICENSORS AND AUTHORS OF THAT COVERED WORK FOR ANY DAMAEGS, DEMANDS, CLAIMS, LOSSES, CAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION REASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM, RELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..c895626 --- /dev/null +++ b/README.txt @@ -0,0 +1,160 @@ +Wolfenstein: Enemy Territory GPL source release +=============================================== + +This file contains the following sections: + +GENERAL NOTES +LICENSE + +GENERAL NOTES +============= + +Game data and patching: +----------------------- + +Wolfenstein: Enemy Territory is a free release, and can be downloaded from +http://www.splashdamage.com/content/wolfenstein-enemy-territory-barracks + +This source release does not contain any game data, the game data is still +covered by the original EULA and must be obeyed as usual. + +Install the latest version of the game for your platform to get the game data. + +Compiling on win32: +------------------- + +A Visual C++ 2008 project is provided in src\wolf.sln. +The solution file is compatible with the Express release of Visual C++. + +In order to test your binaries, backup and remove Main\mp_bin.pk3, then replace +WolfMP.exe, Main\qagame_mp_x86.dll, Main\cgame_mp_x86.dll, Main\ui_mp_x86.dll +by your compiled versions. When starting the server make sure to specify +'Pure Server: No' in the advanced settings page. + +Compiling on GNU/Linux x86: +--------------------------- + +Get scons from http://scons.org/ if your favorite distribution doesn't +package it. + +run scons from the src/ directory. see scons --help for build options + +If any problems occur, consult the internet. + +Other platforms, updated source code, security issues: +------------------------------------------------------ + +If you have obtained this source code several weeks after the time of release +(August 2010), it is likely that you can find modified and improved +versions of the engine in various open source projects across the internet. +Depending what is your interest with the source code, those may be a better +starting point. + + +LICENSE +======= + +See COPYING.txt for the GNU GENERAL PUBLIC LICENSE + +ADDITIONAL TERMS: The Wolfenstein: Enemy Territory GPL Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU GPL which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +EXCLUDED CODE: The code described below and contained in the Wolfenstein: Enemy Territory GPL Source Code release is not part of the Program covered by the GPL and is expressly excluded from its terms. You are solely responsible for obtaining from the copyright holder a license for such code and complying with the applicable license terms. + +IO on .zip files using portions of zlib +--------------------------------------------------------------------------- +lines file(s) +4301 src/qcommon/unzip.c +Copyright (C) 1998 Gilles Vollant +zlib is Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +MD4 Message-Digest Algorithm +----------------------------------------------------------------------------- +lines file(s) +289 src/qcommon/md4.c +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. + +License to copy and use this software is granted provided that it is identified +as the <93>RSA Data Security, Inc. MD4 Message-Digest Algorithm<94> in all mater +ial mentioning or referencing this software or this function. +License is also granted to make and use derivative works provided that such work +s are identified as <93>derived from the RSA Data Security, Inc. MD4 Message-Dig +est Algorithm<94> in all material mentioning or referencing the derived work. +RSA Data Security, Inc. makes no representations concerning either the merchanta +bility of this software or the suitability of this software for any particular p +urpose. It is provided <93>as is<94> without express or implied warranty of any +kind. + +JPEG library +----------------------------------------------------------------------------- +src/jpeg-6 +Copyright (C) 1991-1995, Thomas G. Lane + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +NOTE: unfortunately the README that came with our copy of the library has +been lost, so the one from release 6b is included instead. There are a few +'glue type' modifications to the library to make it easier to use from +the engine, but otherwise the dependency can be easily cleaned up to a +better release of the library. + +CURL library +----------------------------------------------------------------------------- +src/curl-7.12.2 +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2004, Daniel Stenberg, . + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + +FT2 library +----------------------------------------------------------------------------- +src/ft2 +The FT2 library is being used under the GPL v2 as indicated by its LICENSE.txt diff --git a/etmain/ui/menudef.h b/etmain/ui/menudef.h new file mode 100644 index 0000000..af55980 --- /dev/null +++ b/etmain/ui/menudef.h @@ -0,0 +1,473 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#define ITEM_TYPE_TEXT 0 // simple text +#define ITEM_TYPE_BUTTON 1 // button, basically text with a border +#define ITEM_TYPE_RADIOBUTTON 2 // toggle button, may be grouped +#define ITEM_TYPE_CHECKBOX 3 // check box +#define ITEM_TYPE_EDITFIELD 4 // editable text, associated with a cvar +#define ITEM_TYPE_COMBO 5 // drop down list +#define ITEM_TYPE_LISTBOX 6 // scrollable list +#define ITEM_TYPE_MODEL 7 // model +#define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is +#define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a cvar +#define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. +#define ITEM_TYPE_YESNO 11 // yes no cvar setting +#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated +#define ITEM_TYPE_BIND 13 // multiple list setting, enumerated +#define ITEM_TYPE_MENUMODEL 14 // special menu model +#define ITEM_TYPE_TIMEOUT_COUNTER 15 // ydnar +#define ITEM_TYPE_TRICHECKBOX 16 // tri-state check box + +#define ITEM_ALIGN_LEFT 0 // left alignment +#define ITEM_ALIGN_CENTER 1 // center alignment +#define ITEM_ALIGN_RIGHT 2 // right alignment +#define ITEM_ALIGN_CENTER2 3 // center alignment + +#define ITEM_TEXTSTYLE_NORMAL 0 // normal text +#define ITEM_TEXTSTYLE_BLINK 1 // fast blinking +#define ITEM_TEXTSTYLE_PULSE 2 // slow pulsing +#define ITEM_TEXTSTYLE_SHADOWED 3 // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_OUTLINED 4 // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_OUTLINESHADOWED 5 // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_SHADOWEDMORE 6 // drop shadow ( need a color for this ) + +#define WINDOW_BORDER_NONE 0 // no border +#define WINDOW_BORDER_FULL 1 // full border based on border color ( single pixel ) +#define WINDOW_BORDER_HORZ 2 // horizontal borders only +#define WINDOW_BORDER_VERT 3 // vertical borders only +#define WINDOW_BORDER_KCGRADIENT 4 // horizontal border using the gradient bars + +#define WINDOW_STYLE_EMPTY 0 // no background +#define WINDOW_STYLE_FILLED 1 // filled with background color +#define WINDOW_STYLE_GRADIENT 2 // gradient bar based on background color +#define WINDOW_STYLE_SHADER 3 // gradient bar based on background color +#define WINDOW_STYLE_TEAMCOLOR 4 // team color +#define WINDOW_STYLE_CINEMATIC 5 // cinematic + +#define MENU_TRUE 1 // uh.. true +#define MENU_FALSE 0 // and false + +#define HUD_VERTICAL 0x00 +#define HUD_HORIZONTAL 0x01 + +#define RANGETYPE_ABSOLUTE 0 +#define RANGETYPE_RELATIVE 1 + +// list box element types +#define LISTBOX_TEXT 0x00 +#define LISTBOX_IMAGE 0x01 + +// list feeders +#define FEEDER_HEADS 0x00 // model heads +#define FEEDER_MAPS 0x01 // text maps based on game type +#define FEEDER_SERVERS 0x02 // servers +#define FEEDER_CLANS 0x03 // clan names +#define FEEDER_ALLMAPS 0x04 // all maps available, in graphic format +#define FEEDER_REDTEAM_LIST 0x05 // red team members +#define FEEDER_BLUETEAM_LIST 0x06 // blue team members +#define FEEDER_PLAYER_LIST 0x07 // players +#define FEEDER_TEAM_LIST 0x08 // team members for team voting +#define FEEDER_MODS 0x09 // team members for team voting +#define FEEDER_DEMOS 0x0a // team members for team voting +#define FEEDER_SCOREBOARD 0x0b // team members for team voting +#define FEEDER_Q3HEADS 0x0c // model heads +#define FEEDER_SERVERSTATUS 0x0d // server status +#define FEEDER_FINDPLAYER 0x0e // find player +#define FEEDER_CINEMATICS 0x0f // cinematics +#define FEEDER_SAVEGAMES 0x10 // savegames +#define FEEDER_CAMPAIGNS 0x1a // Arnout: all unlocked campaigns available +#define FEEDER_ALLCAMPAIGNS 0x1b // Arnout: all campaigns available +#define FEEDER_PROFILES 0x1c // Arnout: profiles +#define FEEDER_GLINFO 0x1d // Arnout: glinfo + +// display flags +#define CG_SHOW_BLUE_TEAM_HAS_REDFLAG 0x00000001 +#define CG_SHOW_RED_TEAM_HAS_BLUEFLAG 0x00000002 +#define CG_SHOW_ANYTEAMGAME 0x00000004 +#define CG_SHOW_HARVESTER 0x00000008 +#define CG_SHOW_ONEFLAG 0x00000010 +#define CG_SHOW_CTF 0x00000020 +#define CG_SHOW_OBELISK 0x00000040 +#define CG_SHOW_HEALTHCRITICAL 0x00000080 +#define CG_SHOW_SINGLEPLAYER 0x00000100 +#define CG_SHOW_TOURNAMENT 0x00000200 +#define CG_SHOW_DURINGINCOMINGVOICE 0x00000400 +#define CG_SHOW_IF_PLAYER_HAS_FLAG 0x00000800 +#define CG_SHOW_LANPLAYONLY 0x00001000 +#define CG_SHOW_MINED 0x00002000 +#define CG_SHOW_HEALTHOK 0x00004000 +#define CG_SHOW_TEAMINFO 0x00008000 +#define CG_SHOW_NOTEAMINFO 0x00010000 +#define CG_SHOW_OTHERTEAMHASFLAG 0x00020000 +#define CG_SHOW_YOURTEAMHASENEMYFLAG 0x00040000 +#define CG_SHOW_ANYNONTEAMGAME 0x00080000 +//(SA) +#define CG_SHOW_HIGHLIGHTED 0x00100000 + +#define CG_SHOW_NOT_V_BINOC 0x00400000 //----(SA) added // hide on binoc huds +#define CG_SHOW_NOT_V_SNOOPER 0x00800000 //----(SA) added // hide on snooper huds +#define CG_SHOW_NOT_V_FGSCOPE 0x01000000 //----(SA) added // hide on fg42 scope huds +#define CG_SHOW_NOT_V_CLEAR 0x02000000 //----(SA) added // hide on normal, full-view huds +#define CG_SHOW_NOT_V_GARANDSCOPE 0x04000000 // Arnout: hide on garand scope huds +#define CG_SHOW_NOT_V_K43SCOPE 0x08000000 // Arnout: hide on k43 scope huds + +#define CG_SHOW_2DONLY 0x10000000 + + +#define UI_SHOW_LEADER 0x00000001 +#define UI_SHOW_NOTLEADER 0x00000002 +#define UI_SHOW_FAVORITESERVERS 0x00000004 +#define UI_SHOW_ANYNONTEAMGAME 0x00000008 +#define UI_SHOW_ANYTEAMGAME 0x00000010 +#define UI_SHOW_NEWHIGHSCORE 0x00000020 +#define UI_SHOW_DEMOAVAILABLE 0x00000040 +#define UI_SHOW_NEWBESTTIME 0x00000080 +#define UI_SHOW_FFA 0x00000100 +#define UI_SHOW_NOTFFA 0x00000200 +#define UI_SHOW_NETANYNONTEAMGAME 0x00000400 +#define UI_SHOW_NETANYTEAMGAME 0x00000800 +#define UI_SHOW_NOTFAVORITESERVERS 0x00001000 + +#define UI_SHOW_CAMPAIGNMAP1EXISTS 0x00002000 +#define UI_SHOW_CAMPAIGNMAP2EXISTS 0x00004000 +#define UI_SHOW_CAMPAIGNMAP3EXISTS 0x00008000 +#define UI_SHOW_CAMPAIGNMAP4EXISTS 0x00010000 +#define UI_SHOW_CAMPAIGNMAP5EXISTS 0x00020000 +#define UI_SHOW_CAMPAIGNMAP6EXISTS 0x00040000 + +#define UI_SHOW_SELECTEDCAMPAIGNMAPPLAYABLE 0x00080000 +#define UI_SHOW_SELECTEDCAMPAIGNMAPNOTPLAYABLE 0x00100000 + +#define UI_SHOW_PLAYERMUTED 0x01000000 +#define UI_SHOW_PLAYERNOTMUTED 0x02000000 +#define UI_SHOW_PLAYERNOREFEREE 0x04000000 +#define UI_SHOW_PLAYERREFEREE 0x08000000 + +// owner draw types +// ideally these should be done outside of this file but +// this makes it much easier for the macro expansion to +// convert them for the designers ( from the .menu files ) +#define CG_OWNERDRAW_BASE 1 +#define CG_PLAYER_ARMOR_ICON 1 +#define CG_PLAYER_ARMOR_VALUE 2 +#define CG_PLAYER_HEAD 3 +#define CG_PLAYER_HEALTH 4 +#define CG_PLAYER_AMMO_ICON 5 +#define CG_PLAYER_AMMO_VALUE 6 +#define CG_SELECTEDPLAYER_HEAD 7 +#define CG_SELECTEDPLAYER_NAME 8 +#define CG_SELECTEDPLAYER_STATUS 10 +#define CG_SELECTEDPLAYER_WEAPON 11 +#define CG_SELECTEDPLAYER_POWERUP 12 + +#define CG_FLAGCARRIER_HEAD 13 +#define CG_FLAGCARRIER_NAME 14 +#define CG_FLAGCARRIER_LOCATION 15 +#define CG_FLAGCARRIER_STATUS 16 +#define CG_FLAGCARRIER_WEAPON 17 +#define CG_FLAGCARRIER_POWERUP 18 + +#define CG_PLAYER_ITEM 19 +#define CG_PLAYER_SCORE 20 + +/*#define CG_BLUE_FLAGHEAD 21 +#define CG_BLUE_FLAGSTATUS 22 +#define CG_BLUE_FLAGNAME 23 +#define CG_RED_FLAGHEAD 24 +#define CG_RED_FLAGSTATUS 25 +#define CG_RED_FLAGNAME 26*/ + +#define CG_BLUE_SCORE 27 +#define CG_RED_SCORE 28 +/*#define CG_RED_NAME 29 +#define CG_BLUE_NAME 30 +#define CG_HARVESTER_SKULLS 31 // only shows in harvester +#define CG_ONEFLAG_STATUS 32 // only shows in one flag*/ +#define CG_TEAM_COLOR 34 +//#define CG_CTF_POWERUP 35 + +#define CG_AREA_POWERUP 36 +#define CG_AREA_LAGOMETER 37 // painted with old system +#define CG_PLAYER_HASFLAG 38 +#define CG_GAME_TYPE 39 // not done + +#define CG_SELECTEDPLAYER_HEALTH 41 +#define CG_PLAYER_STATUS 42 +#define CG_FRAGGED_MSG 43 // painted with old system +#define CG_PROXMINED_MSG 44 // painted with old system +#define CG_AREA_FPSINFO 45 // painted with old system +#define CG_AREA_SYSTEMCHAT 46 // painted with old system +#define CG_AREA_TEAMCHAT 47 // painted with old system +#define CG_AREA_CHAT 48 // painted with old system +#define CG_GAME_STATUS 49 +#define CG_KILLER 50 +#define CG_PLAYER_ARMOR_ICON2D 51 +#define CG_PLAYER_AMMO_ICON2D 52 +#define CG_ACCURACY 53 +#define CG_ASSISTS 54 +#define CG_DEFEND 55 +#define CG_EXCELLENT 56 +#define CG_IMPRESSIVE 57 +#define CG_PERFECT 58 +#define CG_GAUNTLET 59 +#define CG_SPECTATORS 60 +#define CG_TEAMINFO 61 +#define CG_VOICE_HEAD 62 +#define CG_VOICE_NAME 63 +#define CG_PLAYER_HASFLAG2D 64 +#define CG_HARVESTER_SKULLS2D 65 // only shows in harvester +#define CG_CAPFRAGLIMIT 66 +#define CG_1STPLACE 67 +#define CG_2NDPLACE 68 +#define CG_CAPTURES 69 + +// (SA) adding +#define CG_PLAYER_AMMOCLIP_VALUE 70 +#define CG_PLAYER_WEAPON_ICON2D 71 +#define CG_CURSORHINT 72 +#define CG_STAMINA 73 +#define CG_PLAYER_WEAPON_HEAT 74 +#define CG_PLAYER_POWERUP 75 +#define CG_PLAYER_INVENTORY 77 +#define CG_AREA_WEAPON 78 // draw weapons here +#define CG_AREA_HOLDABLE 79 +#define CG_CURSORHINT_STATUS 80 // like 'health' bar when pointing at a func_explosive +#define CG_PLAYER_WEAPON_STABILITY 81 // shows aimSpreadScale value +#define CG_PLAYER_WEAPON_RECHARGE 82 // DHM - Nerve :: For various multiplayer weapons that have recharge times + +#define UI_OWNERDRAW_BASE 200 +#define UI_HANDICAP 200 +#define UI_EFFECTS 201 +#define UI_PLAYERMODEL 202 +#define UI_CLANNAME 203 +#define UI_CLANLOGO 204 +#define UI_GAMETYPE 205 +#define UI_MAPPREVIEW 206 +#define UI_NETMAPPREVIEW 207 +#define UI_BLUETEAMNAME 208 +#define UI_REDTEAMNAME 209 +#define UI_BLUETEAM1 210 +#define UI_BLUETEAM2 211 +#define UI_BLUETEAM3 212 +#define UI_BLUETEAM4 213 +#define UI_BLUETEAM5 214 +#define UI_REDTEAM1 215 +#define UI_REDTEAM2 216 +#define UI_REDTEAM3 217 +#define UI_REDTEAM4 218 +#define UI_REDTEAM5 219 +#define UI_NETSOURCE 220 +//#define UI_NETMAPPREVIEW 221 +#define UI_NETFILTER 222 +#define UI_TIER 223 +#define UI_OPPONENTMODEL 224 +#define UI_TIERMAP1 225 +#define UI_TIERMAP2 226 +#define UI_TIERMAP3 227 +#define UI_PLAYERLOGO 228 +#define UI_OPPONENTLOGO 229 +#define UI_PLAYERLOGO_METAL 230 +#define UI_OPPONENTLOGO_METAL 231 +#define UI_PLAYERLOGO_NAME 232 +#define UI_OPPONENTLOGO_NAME 233 +#define UI_TIER_MAPNAME 234 +#define UI_TIER_GAMETYPE 235 +#define UI_ALLMAPS_SELECTION 236 +#define UI_OPPONENT_NAME 237 +#define UI_VOTE_KICK 238 +#define UI_BOTNAME 239 +//#define UI_BOTSKILL 240 +#define UI_REDBLUE 241 +#define UI_CROSSHAIR 242 +#define UI_SELECTEDPLAYER 243 +#define UI_MAPCINEMATIC 244 +#define UI_NETGAMETYPE 245 +#define UI_NETMAPCINEMATIC 246 +#define UI_SERVERREFRESHDATE 247 +#define UI_SERVERMOTD 248 +#define UI_KEYBINDSTATUS 250 +#define UI_CLANCINEMATIC 251 +#define UI_MAP_TIMETOBEAT 252 +#define UI_JOINGAMETYPE 253 +#define UI_PREVIEWCINEMATIC 254 +#define UI_STARTMAPCINEMATIC 255 +#define UI_MAPS_SELECTION 256 +#define UI_LOADPANEL 257 + +//----(SA) added +#define UI_MENUMODEL 257 +#define UI_SAVEGAME_SHOT 258 +//----(SA) end + +// NERVE - SMF +#define UI_LIMBOCHAT 259 +// -NERVE - SMF + +// Arnout: Enemy Territory +#define UI_CAMPAIGNCINEMATIC 260 +#define UI_CAMPAIGNNAME 261 +#define UI_CAMPAIGNDESCRIPTION 262 +#define UI_CAMPAIGNMAP1_SHOT 263 +#define UI_CAMPAIGNMAP2_SHOT 264 +#define UI_CAMPAIGNMAP3_SHOT 265 +#define UI_CAMPAIGNMAP4_SHOT 266 +#define UI_CAMPAIGNMAP5_SHOT 267 +#define UI_CAMPAIGNMAP6_SHOT 268 + +#define UI_CAMPAIGNMAP1_TEXT 269 +#define UI_CAMPAIGNMAP2_TEXT 270 +#define UI_CAMPAIGNMAP3_TEXT 271 +#define UI_CAMPAIGNMAP4_TEXT 272 +#define UI_CAMPAIGNMAP5_TEXT 273 +#define UI_CAMPAIGNMAP6_TEXT 274 + +#define UI_GAMETYPEDESCRIPTION 280 + +// Gordon: Mission briefing +#define UI_MB_MAP 300 +#define UI_MB_TITLE 301 +#define UI_MB_OBJECTIVES 302 + +#define VOICECHAT_GETFLAG "getflag" // command someone to get the flag +#define VOICECHAT_OFFENSE "offense" // command someone to go on offense +#define VOICECHAT_DEFEND "defend" // command someone to go on defense +#define VOICECHAT_DEFENDFLAG "defendflag" // command someone to defend the flag +#define VOICECHAT_PATROL "patrol" // command someone to go on patrol (roam) +#define VOICECHAT_CAMP "camp" // command someone to camp (we don't have sounds for this one) +#define VOICECHAT_FOLLOWME "followme" // command someone to follow you +#define VOICECHAT_RETURNFLAG "returnflag" // command someone to return our flag +#define VOICECHAT_FOLLOWFLAGCARRIER "followflagcarrier" // command someone to follow the flag carrier +#define VOICECHAT_YES "yes" // yes, affirmative, etc. +#define VOICECHAT_NO "no" // no, negative, etc. +#define VOICECHAT_ONGETFLAG "ongetflag" // I'm getting the flag +#define VOICECHAT_ONOFFENSE "onoffense" // I'm on offense +#define VOICECHAT_ONDEFENSE "ondefense" // I'm on defense +#define VOICECHAT_ONPATROL "onpatrol" // I'm on patrol (roaming) +#define VOICECHAT_ONCAMPING "oncamp" // I'm camping somewhere +#define VOICECHAT_ONFOLLOW "onfollow" // I'm following +#define VOICECHAT_ONFOLLOWCARRIER "onfollowcarrier" // I'm following the flag carrier +#define VOICECHAT_ONRETURNFLAG "onreturnflag" // I'm returning our flag +#define VOICECHAT_INPOSITION "inposition" // I'm in position +#define VOICECHAT_IHAVEFLAG "ihaveflag" // I have the flag +#define VOICECHAT_BASEATTACK "baseattack" // the base is under attack +#define VOICECHAT_ENEMYHASFLAG "enemyhasflag" // the enemy has our flag (CTF) +#define VOICECHAT_STARTLEADER "startleader" // I'm the leader +#define VOICECHAT_STOPLEADER "stopleader" // I resign leadership +#define VOICECHAT_WHOISLEADER "whoisleader" // who is the team leader +#define VOICECHAT_WANTONDEFENSE "wantondefense" // I want to be on defense +#define VOICECHAT_WANTONOFFENSE "wantonoffense" // I want to be on offense +#define VOICECHAT_KILLINSULT "kill_insult" // I just killed you +#define VOICECHAT_TAUNT "taunt" // I want to taunt you +#define VOICECHAT_DEATHINSULT "death_insult" // you just killed me +#define VOICECHAT_KILLGAUNTLET "kill_gauntlet" // I just killed you with the gauntlet +#define VOICECHAT_PRAISE "praise" // you did something good + +// NERVE - SMF - wolf multiplayer class/item selection mechanism +#define WM_START_SELECT 0 + +#define WM_SELECT_TEAM 1 +#define WM_SELECT_CLASS 2 +#define WM_SELECT_WEAPON 3 +#define WM_SELECT_PISTOL 4 +#define WM_SELECT_GRENADE 5 +#define WM_SELECT_ITEM1 6 + +#define WM_AXIS 1 +#define WM_ALLIES 2 +#define WM_SPECTATOR 3 + +#define WM_SOLDIER 1 +#define WM_MEDIC 2 +#define WM_LIEUTENANT 3 +#define WM_ENGINEER 4 +#define WM_COVERTOPS 5 + +/*#define WM_PISTOL_1911 1 +#define WM_PISTOL_LUGER 2 + +#define WM_WEAPON_MP40 3 +#define WM_WEAPON_THOMPSON 4 +#define WM_WEAPON_STEN 5 +#define WM_WEAPON_MAUSER 6 +#define WM_WEAPON_PANZERFAUST 8 +#define WM_WEAPON_VENOM 9 +#define WM_WEAPON_FLAMETHROWER 10 + +#define WM_PINEAPPLE_GRENADE 11 +#define WM_STICK_GRENADE 12 + +#define WM_WEAPON_KAR98 13 +#define WM_WEAPON_CARBINE 14 +#define WM_WEAPON_GARAND 15 +#define WM_WEAPON_FG42 16*/ +// -NERVE - SMF + +// Arnout: UI fonts, supports up to 6 fonts +#define UI_FONT_ARIBLK_16 0 +#define UI_FONT_ARIBLK_27 1 +#define UI_FONT_COURBD_21 2 +#define UI_FONT_COURBD_30 3 + +// OSP - callvote server setting toggles +// CS_SERVERTOGGLES +#define CV_SVS_MUTESPECS 1 +#define CV_SVS_FRIENDLYFIRE 2 +// 2 bits for warmup damage setting +#define CV_SVS_WARMUPDMG 12 +#define CV_SVS_PAUSE 16 +#define CV_SVS_LOCKTEAMS 32 +#define CV_SVS_LOCKSPECS 64 +#define CV_SVS_ANTILAG 128 +#define CV_SVS_BALANCEDTEAMS 256 +#define CV_SVS_NEXTMAP 512 + +// "cg_ui_voteFlags" +#define CV_SVF_COMP 1 +#define CV_SVF_GAMETYPE 2 +#define CV_SVF_KICK 4 +#define CV_SVF_MAP 8 +#define CV_SVF_MATCHRESET 16 +#define CV_SVF_MUTESPECS 32 +#define CV_SVF_NEXTMAP 64 +#define CV_SVF_PUB 128 +#define CV_SVF_REFEREE 256 +#define CV_SVF_SHUFFLETEAMS 512 +#define CV_SVF_SWAPTEAMS 1024 +#define CV_SVF_FRIENDLYFIRE 2048 +#define CV_SVF_TIMELIMIT 4096 +#define CV_SVF_WARMUPDAMAGE 8192 +#define CV_SVF_ANTILAG 16384 +#define CV_SVF_BALANCEDTEAMS 32768 +#define CV_SVF_MUTING 65536 + +// referee level +#define RL_NONE 0 +#define RL_REFEREE 1 +#define RL_RCON 2 +// -OSP diff --git a/etmain/ui/menumacros.h b/etmain/ui/menumacros.h new file mode 100644 index 0000000..02eff51 --- /dev/null +++ b/etmain/ui/menumacros.h @@ -0,0 +1,1034 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#define WINDOW_FUI( WINDOW_TEXT, GRADIENT_START_OFFSET ) \ + itemDef { \ + name "window" \ + group GROUP_NAME \ + rect 0 0 WINDOW_WIDTH WINDOW_HEIGHT \ + style WINDOW_STYLE_FILLED \ + backcolor 0 0 0 .2 \ + border WINDOW_BORDER_FULL \ + bordercolor .5 .5 .5 .5 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "titlebar" \ + group GROUP_NAME \ + rect 2 2 GRADIENT_START_OFFSET 24 \ + style WINDOW_STYLE_FILLED \ + backcolor .16 .2 .17 .8 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "titlebargradient" \ + group GROUP_NAME \ + rect $evalint(GRADIENT_START_OFFSET+2) 2 $evalint(WINDOW_WIDTH-(GRADIENT_START_OFFSET+4)) 24 \ + style WINDOW_STYLE_GRADIENT \ + backcolor .16 .2 .17 .8 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "windowtitle" \ + group GROUP_NAME \ + rect 2 2 $evalint(WINDOW_WIDTH-4) 24 \ + text WINDOW_TEXT \ + textfont UI_FONT_ARIBLK_27 \ + textscale .4 \ + textalignx 3 \ + textaligny 20 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .2 \ + visible 1 \ + decoration \ + } + +#define WINDOW_INGAME( WINDOW_TEXT, GRADIENT_START_OFFSET ) \ + itemDef { \ + name "window" \ + group GROUP_NAME \ + rect 0 0 WINDOW_WIDTH WINDOW_HEIGHT \ + style WINDOW_STYLE_FILLED \ + backcolor 0 0 0 .6 \ + border WINDOW_BORDER_FULL \ + bordercolor .5 .5 .5 .5 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "titlebar" \ + group GROUP_NAME \ + rect 2 2 GRADIENT_START_OFFSET 24 \ + style WINDOW_STYLE_FILLED \ + backcolor .16 .2 .17 .8 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "titlebargradient" \ + group GROUP_NAME \ + rect $evalint(GRADIENT_START_OFFSET+2) 2 $evalint(WINDOW_WIDTH-(GRADIENT_START_OFFSET+4)) 24 \ + style WINDOW_STYLE_GRADIENT \ + backcolor .16 .2 .17 .8 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "windowtitle" \ + group GROUP_NAME \ + rect 2 2 $evalint(WINDOW_WIDTH-4) 24 \ + text WINDOW_TEXT \ + textfont UI_FONT_ARIBLK_27 \ + textscale .4 \ + textalignx 3 \ + textaligny 20 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .2 \ + visible 1 \ + decoration \ + } + +#ifdef FUI +#define WINDOW WINDOW_FUI +#else +#define WINDOW WINDOW_INGAME +#endif + +#define SUBWINDOW( SUBWINDOW_X, SUBWINDOW_Y, SUBWINDOW_W, SUBWINDOW_H, SUBWINDOW_TEXT ) \ + itemDef { \ + name "subwindow"##SUBWINDOW_TEXT \ + group GROUP_NAME \ + rect $evalfloat(SUBWINDOW_X) $evalfloat(SUBWINDOW_Y) $evalfloat(SUBWINDOW_W) $evalfloat(SUBWINDOW_H) \ + style WINDOW_STYLE_FILLED \ + backcolor 0 0 0 .2 \ + border WINDOW_BORDER_FULL \ + bordercolor .5 .5 .5 .5 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "subwindowtitle"##SUBWINDOW_TEXT \ + group GROUP_NAME \ + rect $evalfloat((SUBWINDOW_X)+2) $evalfloat((SUBWINDOW_Y)+2) $evalfloat((SUBWINDOW_W)-4) 12 \ + text SUBWINDOW_TEXT \ + textfont UI_FONT_ARIBLK_16 \ + textscale .19 \ + textalignx 3 \ + textaligny 10 \ + style WINDOW_STYLE_FILLED \ + backcolor .16 .2 .17 .8 \ + forecolor .6 .6 .6 1 \ + visible 1 \ + decoration \ + } + +#define SUBWINDOWBLACK( SUBWINDOWBLACK_X, SUBWINDOWBLACK_Y, SUBWINDOWBLACK_W, SUBWINDOWBLACK_H, SUBWINDOWBLACK_TEXT ) \ + itemDef { \ + name "subwindowblack"##SUBWINDOWBLACK_TEXT \ + group GROUP_NAME \ + rect $evalfloat(SUBWINDOWBLACK_X) $evalfloat(SUBWINDOWBLACK_Y) $evalfloat(SUBWINDOWBLACK_W) $evalfloat(SUBWINDOWBLACK_H) \ + style WINDOW_STYLE_FILLED \ + backcolor 0 0 0 .85 \ + border WINDOW_BORDER_FULL \ + bordercolor .5 .5 .5 .5 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "subwindowblacktitle"##SUBWINDOWBLACK_TEXT \ + group GROUP_NAME \ + rect $evalfloat((SUBWINDOWBLACK_X)+2) $evalfloat((SUBWINDOWBLACK_Y)+2) $evalfloat((SUBWINDOWBLACK_W)-4) 12 \ + text SUBWINDOWBLACK_TEXT \ + textfont UI_FONT_ARIBLK_16 \ + textscale .19 \ + textalignx 3 \ + textaligny 10 \ + style WINDOW_STYLE_FILLED \ + backcolor .16 .2 .17 .8 \ + forecolor .6 .6 .6 1 \ + visible 1 \ + decoration \ + } + +#define BUTTON( BUTTON_X, BUTTON_Y, BUTTON_W, BUTTON_H, BUTTON_TEXT, BUTTON_TEXT_SCALE, BUTTON_TEXT_ALIGN_Y, BUTTON_ACTION ) \ + itemDef { \ + name "bttn"##BUTTON_TEXT \ + group GROUP_NAME \ + rect $evalfloat(BUTTON_X) $evalfloat(BUTTON_Y) $evalfloat(BUTTON_W) $evalfloat(BUTTON_H) \ + type ITEM_TYPE_BUTTON \ + text BUTTON_TEXT \ + textfont UI_FONT_COURBD_30 \ + textscale BUTTON_TEXT_SCALE \ + textalign ITEM_ALIGN_CENTER \ + textalignx $evalfloat(0.5*(BUTTON_W)) \ + textaligny BUTTON_TEXT_ALIGN_Y \ + style WINDOW_STYLE_FILLED \ + backcolor .3 .3 .3 .4 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .5 \ + visible 1 \ + \ + mouseEnter { \ + setitemcolor "bttn"##BUTTON_TEXT forecolor .9 .9 .9 1 ; \ + setitemcolor "bttn"##BUTTON_TEXT backcolor .5 .5 .5 .4 \ + } \ + \ + mouseExit { \ + setitemcolor "bttn"##BUTTON_TEXT forecolor .6 .6 .6 1 ; \ + setitemcolor "bttn"##BUTTON_TEXT backcolor .3 .3 .3 .4 \ + } \ + \ + action { \ + setitemcolor "bttn"##BUTTON_TEXT forecolor .6 .6 .6 1 ; \ + setitemcolor "bttn"##BUTTON_TEXT backcolor .3 .3 .3 .4 ; \ + play "sound/menu/select.wav" ; \ + BUTTON_ACTION \ + } \ + } + +#define BUTTONEXT( BUTTONEXT_X, BUTTONEXT_Y, BUTTONEXT_W, BUTTONEXT_H, BUTTONEXT_TEXT, BUTTONEXT_TEXT_SCALE, BUTTONEXT_TEXT_ALIGN_Y, BUTTONEXT_ACTION, BUTTONEXT_EXT ) \ + itemDef { \ + name "bttnext"##BUTTONEXT_TEXT \ + group GROUP_NAME \ + rect $evalfloat(BUTTONEXT_X) $evalfloat(BUTTONEXT_Y) $evalfloat(BUTTONEXT_W) $evalfloat(BUTTONEXT_H) \ + type ITEM_TYPE_BUTTON \ + text BUTTONEXT_TEXT \ + textfont UI_FONT_COURBD_30 \ + textscale BUTTONEXT_TEXT_SCALE \ + textalign ITEM_ALIGN_CENTER \ + textalignx $evalfloat(0.5*(BUTTONEXT_W)) \ + textaligny BUTTONEXT_TEXT_ALIGN_Y \ + style WINDOW_STYLE_FILLED \ + backcolor .3 .3 .3 .4 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .5 \ + visible 1 \ + \ + mouseEnter { \ + setitemcolor "bttnext"##BUTTONEXT_TEXT forecolor .9 .9 .9 1 ; \ + setitemcolor "bttnext"##BUTTONEXT_TEXT backcolor .5 .5 .5 .4 \ + } \ + \ + mouseExit { \ + setitemcolor "bttnext"##BUTTONEXT_TEXT forecolor .6 .6 .6 1 ; \ + setitemcolor "bttnext"##BUTTONEXT_TEXT backcolor .3 .3 .3 .4 \ + } \ + \ + action { \ + setitemcolor "bttnext"##BUTTONEXT_TEXT forecolor .6 .6 .6 1 ; \ + setitemcolor "bttnext"##BUTTONEXT_TEXT backcolor .3 .3 .3 .4 ; \ + play "sound/menu/select.wav" ; \ + BUTTONEXT_ACTION \ + } \ + \ + BUTTONEXT_EXT \ + } + +#define NAMEDBUTTON( NAMEDBUTTON_NAME, NAMEDBUTTON_X, NAMEDBUTTON_Y, NAMEDBUTTON_W, NAMEDBUTTON_H, NAMEDBUTTON_TEXT, NAMEDBUTTON_TEXT_SCALE, NAMEDBUTTON_TEXT_ALIGN_Y, NAMEDBUTTON_ACTION ) \ + itemDef { \ + name NAMEDBUTTON_NAME \ + group GROUP_NAME \ + rect $evalfloat(NAMEDBUTTON_X) $evalfloat(NAMEDBUTTON_Y) $evalfloat(NAMEDBUTTON_W) $evalfloat(NAMEDBUTTON_H) \ + type ITEM_TYPE_BUTTON \ + text NAMEDBUTTON_TEXT \ + textfont UI_FONT_COURBD_30 \ + textscale NAMEDBUTTON_TEXT_SCALE \ + textalign ITEM_ALIGN_CENTER \ + textalignx $evalfloat(0.5*(NAMEDBUTTON_W)) \ + textaligny NAMEDBUTTON_TEXT_ALIGN_Y \ + style WINDOW_STYLE_FILLED \ + backcolor .3 .3 .3 .4 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .5 \ + visible 1 \ + \ + mouseEnter { \ + setitemcolor NAMEDBUTTON_NAME forecolor .9 .9 .9 1 ; \ + setitemcolor NAMEDBUTTON_NAME backcolor .5 .5 .5 .4 \ + } \ + \ + mouseExit { \ + setitemcolor NAMEDBUTTON_NAME forecolor .6 .6 .6 1 ; \ + setitemcolor NAMEDBUTTON_NAME backcolor .3 .3 .3 .4 \ + } \ + \ + action { \ + setitemcolor NAMEDBUTTON_NAME forecolor .6 .6 .6 1 ; \ + setitemcolor NAMEDBUTTON_NAME backcolor .3 .3 .3 .4 ; \ + play "sound/menu/select.wav" ; \ + NAMEDBUTTON_ACTION \ + } \ + } + +#define NAMEDBUTTONEXT( NAMEDBUTTONEXT_NAME, NAMEDBUTTONEXT_X, NAMEDBUTTONEXT_Y, NAMEDBUTTONEXT_W, NAMEDBUTTONEXT_H, NAMEDBUTTONEXT_TEXT, NAMEDBUTTONEXT_TEXT_SCALE, NAMEDBUTTONEXT_TEXT_ALIGN_Y, NAMEDBUTTONEXT_ACTION, NAMEDBUTTONEXT_EXT ) \ + itemDef { \ + name NAMEDBUTTONEXT_NAME \ + group GROUP_NAME \ + rect $evalfloat(NAMEDBUTTONEXT_X) $evalfloat(NAMEDBUTTONEXT_Y) $evalfloat(NAMEDBUTTONEXT_W) $evalfloat(NAMEDBUTTONEXT_H) \ + type ITEM_TYPE_BUTTON \ + text NAMEDBUTTONEXT_TEXT \ + textfont UI_FONT_COURBD_30 \ + textscale NAMEDBUTTONEXT_TEXT_SCALE \ + textalign ITEM_ALIGN_CENTER \ + textalignx $evalfloat(0.5*(NAMEDBUTTONEXT_W)) \ + textaligny NAMEDBUTTONEXT_TEXT_ALIGN_Y \ + style WINDOW_STYLE_FILLED \ + backcolor .3 .3 .3 .4 \ + forecolor .6 .6 .6 1 \ + border WINDOW_BORDER_FULL \ + bordercolor .1 .1 .1 .5 \ + visible 1 \ + \ + mouseEnter { \ + setitemcolor NAMEDBUTTONEXT_NAME forecolor .9 .9 .9 1 ; \ + setitemcolor NAMEDBUTTONEXT_NAME backcolor .5 .5 .5 .4 \ + } \ + \ + mouseExit { \ + setitemcolor NAMEDBUTTONEXT_NAME forecolor .6 .6 .6 1 ; \ + setitemcolor NAMEDBUTTONEXT_NAME backcolor .3 .3 .3 .4 \ + } \ + \ + action { \ + setitemcolor NAMEDBUTTONEXT_NAME forecolor .6 .6 .6 1 ; \ + setitemcolor NAMEDBUTTONEXT_NAME backcolor .3 .3 .3 .4 ; \ + play "sound/menu/select.wav" ; \ + NAMEDBUTTONEXT_ACTION \ + } \ + \ + NAMEDBUTTONEXT_EXT \ + } + +#define EDITFIELD( EDITFIELD_X, EDITFIELD_Y, EDITFIELD_W, EDITFIELD_H, EDITFIELD_TEXT, EDITFIELD_TEXT_SCALE, EDITFIELD_TEXT_ALIGN_Y, EDITFIELD_CVAR, EDITFIELD_MAXCHARS, EDITFIELD_MAXPAINTCHARS, EDITFIELD_TOOLTIP ) \ + itemDef { \ + name "efback"##EDITFIELD_TEXT \ + group GROUP_NAME \ + rect $evalfloat((EDITFIELD_X)+.5*(EDITFIELD_W)+6) $evalfloat(EDITFIELD_Y) $evalfloat(.5*(EDITFIELD_W)-6) $evalfloat(EDITFIELD_H) \ + style WINDOW_STYLE_FILLED \ + backcolor .5 .5 .5 .2 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "ef"##EDITFIELD_TEXT \ + group GROUP_NAME \ + rect $evalfloat(EDITFIELD_X) $evalfloat(EDITFIELD_Y) $evalfloat(EDITFIELD_W) $evalfloat(EDITFIELD_H) \ + type ITEM_TYPE_EDITFIELD \ + text EDITFIELD_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale EDITFIELD_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(EDITFIELD_W)) \ + textaligny EDITFIELD_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar EDITFIELD_CVAR \ + maxChars EDITFIELD_MAXCHARS \ + maxPaintChars EDITFIELD_MAXPAINTCHARS \ + visible 1 \ + tooltip EDITFIELD_TOOLTIP \ + } + +#define EDITFIELDLEFT( EDITFIELDLEFT_X, EDITFIELDLEFT_Y, EDITFIELDLEFT_W, EDITFIELDLEFT_H, EDITFIELDLEFT_TEXT, EDITFIELDLEFT_TEXT_SCALE, EDITFIELDLEFT_TEXT_ALIGN_Y, EDITFIELDLEFT_CVAR, EDITFIELDLEFT_MAXCHARS, EDITFIELDLEFT_MAXPAINTCHARS, EDITFIELDLEFT_TOOLTIP ) \ + itemDef { \ + name "efleft"##EDITFIELDLEFT_TEXT \ + group GROUP_NAME \ + rect $evalfloat(EDITFIELDLEFT_X) $evalfloat(EDITFIELDLEFT_Y) $evalfloat(EDITFIELDLEFT_W) $evalfloat(EDITFIELDLEFT_H) \ + type ITEM_TYPE_EDITFIELD \ + text EDITFIELDLEFT_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale EDITFIELDLEFT_TEXT_SCALE \ + textaligny EDITFIELDLEFT_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar EDITFIELDLEFT_CVAR \ + maxChars EDITFIELDLEFT_MAXCHARS \ + maxPaintChars EDITFIELDLEFT_MAXPAINTCHARS \ + visible 1 \ + tooltip EDITFIELDLEFT_TOOLTIP \ + } + +#define NUMERICFIELD( NUMERICFIELD_X, NUMERICFIELD_Y, NUMERICFIELD_W, NUMERICFIELD_H, NUMERICFIELD_TEXT, NUMERICFIELD_TEXT_SCALE, NUMERICFIELD_TEXT_ALIGN_Y, NUMERICFIELD_CVAR, NUMERICFIELD_MAXCHARS, NUMERICFIELD_TOOLTIP ) \ + itemDef { \ + name "nfback"##NUMERICFIELD_TEXT \ + group GROUP_NAME \ + rect $evalfloat((NUMERICFIELD_X)+.5*(NUMERICFIELD_W)+6) $evalfloat(NUMERICFIELD_Y) $evalfloat(.5*(NUMERICFIELD_W)-6) $evalfloat(NUMERICFIELD_H) \ + style WINDOW_STYLE_FILLED \ + backcolor .5 .5 .5 .2 \ + visible 1 \ + decoration \ + } \ + \ + itemDef { \ + name "nf"##NUMERICFIELD_TEXT \ + group GROUP_NAME \ + rect $evalfloat(NUMERICFIELD_X) $evalfloat(NUMERICFIELD_Y) $evalfloat(NUMERICFIELD_W) $evalfloat(NUMERICFIELD_H) \ + type ITEM_TYPE_NUMERICFIELD \ + text NUMERICFIELD_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale NUMERICFIELD_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(NUMERICFIELD_W)) \ + textaligny NUMERICFIELD_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar NUMERICFIELD_CVAR \ + maxChars NUMERICFIELD_MAXCHARS \ + visible 1 \ + tooltip NUMERICFIELD_TOOLTIP \ + } + +#define NUMERICFIELDLEFTEXT( NUMERICFIELDLEFTEXT_X, NUMERICFIELDLEFTEXT_Y, NUMERICFIELDLEFTEXT_W, NUMERICFIELDLEFTEXT_H, NUMERICFIELDLEFTEXT_TEXT, NUMERICFIELDLEFTEXT_TEXT_SCALE, NUMERICFIELDLEFTEXT_TEXT_ALIGN_Y, NUMERICFIELDLEFTEXT_CVAR, NUMERICFIELDLEFTEXT_MAXCHARS, NUMERICFIELDLEFTEXT_EXT, NUMERICFIELDLEFTEXT_TOOLTIP ) \ + itemDef { \ + name "nfleftext"##NUMERICFIELDLEFTEXT_TEXT \ + group GROUP_NAME \ + rect $evalfloat(NUMERICFIELDLEFTEXT_X) $evalfloat(NUMERICFIELDLEFTEXT_Y) $evalfloat(NUMERICFIELDLEFTEXT_W) $evalfloat(NUMERICFIELDLEFTEXT_H) \ + type ITEM_TYPE_NUMERICFIELD \ + text NUMERICFIELDLEFTEXT_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale NUMERICFIELDLEFTEXT_TEXT_SCALE \ + textaligny NUMERICFIELDLEFTEXT_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar NUMERICFIELDLEFTEXT_CVAR \ + maxChars NUMERICFIELDLEFTEXT_MAXCHARS \ + visible 1 \ + tooltip NUMERICFIELDLEFTEXT_TOOLTIP \ + \ + NUMERICFIELDLEFTEXT_EXT \ + } + +#define YESNO( YESNO_X, YESNO_Y, YESNO_W, YESNO_H, YESNO_TEXT, YESNO_TEXT_SCALE, YESNO_TEXT_ALIGN_Y, YESNO_CVAR, YESNO_TOOLTIP ) \ + itemDef { \ + name "yn"##YESNO_TEXT \ + group GROUP_NAME \ + rect $evalfloat(YESNO_X) $evalfloat(YESNO_Y) $evalfloat(YESNO_W) $evalfloat(YESNO_H) \ + type ITEM_TYPE_YESNO \ + text YESNO_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale YESNO_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(YESNO_W)) \ + textaligny YESNO_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar YESNO_CVAR \ + visible 1 \ + tooltip YESNO_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "yn"##YESNO_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "yn"##YESNO_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define YESNOALIGNX( YESNOALIGNX_X, YESNOALIGNX_Y, YESNOALIGNX_W, YESNOALIGNX_H, YESNOALIGNX_TEXT, YESNOALIGNX_TEXT_SCALE, YESNOALIGNX_TEXT_ALIGN_X, YESNOALIGNX_TEXT_ALIGN_Y, YESNOALIGNX_CVAR, YESNOALIGNX_TOOLTIP ) \ + itemDef { \ + name "ynalx"##YESNOALIGNX_TEXT \ + group GROUP_NAME \ + rect $evalfloat(YESNOALIGNX_X) $evalfloat(YESNOALIGNX_Y) $evalfloat(YESNOALIGNX_W) $evalfloat(YESNOALIGNX_H) \ + type ITEM_TYPE_YESNO \ + text YESNOALIGNX_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale YESNOALIGNX_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(YESNOALIGNX_W)+YESNOALIGNX_TEXT_ALIGN_X) \ + textaligny YESNOALIGNX_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar YESNOALIGNX_CVAR \ + visible 1 \ + tooltip YESNOALIGNX_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "ynalx"##YESNOALIGNX_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "ynalx"##YESNOALIGNX_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define YESNOACTION( YESNOACTION_X, YESNOACTION_Y, YESNOACTION_W, YESNOACTION_H, YESNOACTION_TEXT, YESNOACTION_TEXT_SCALE, YESNOACTION_TEXT_ALIGN_Y, YESNOACTION_CVAR, YESNOACTION_ACTION, YESNOACTION_TOOLTIP ) \ + itemDef { \ + name "ynaction"##YESNOACTION_TEXT \ + group GROUP_NAME \ + rect $evalfloat(YESNOACTION_X) $evalfloat(YESNOACTION_Y) $evalfloat(YESNOACTION_W) $evalfloat(YESNOACTION_H) \ + type ITEM_TYPE_YESNO \ + text YESNOACTION_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale YESNOACTION_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(YESNOACTION_W)) \ + textaligny YESNOACTION_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar YESNOACTION_CVAR \ + visible 1 \ + tooltip YESNOACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "ynaction"##YESNOACTION_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "ynaction"##YESNOACTION_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + YESNOACTION_ACTION \ + } \ + } + +#define CHECKBOX( CHECKBOX_X, CHECKBOX_Y, CHECKBOX_W, CHECKBOX_H, CHECKBOX_TEXT, CHECKBOX_TEXT_SCALE, CHECKBOX_TEXT_ALIGN_Y, CHECKBOX_CVAR, CHECKBOX_TOOLTIP ) \ + itemDef { \ + name "check"##CHECKBOX_TEXT \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOX_X) $evalfloat(CHECKBOX_Y) $evalfloat(CHECKBOX_W) $evalfloat(CHECKBOX_H) \ + type ITEM_TYPE_CHECKBOX \ + text CHECKBOX_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CHECKBOX_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(CHECKBOX_W)) \ + textaligny CHECKBOX_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOX_CVAR \ + visible 1 \ + tooltip CHECKBOX_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "check"##CHECKBOX_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "check"##CHECKBOX_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define CHECKBOXALIGNX( CHECKBOXALIGNX_X, CHECKBOXALIGNX_Y, CHECKBOXALIGNX_W, CHECKBOXALIGNX_H, CHECKBOXALIGNX_TEXT, CHECKBOXALIGNX_TEXT_SCALE, CHECKBOXALIGNX_TEXT_ALIGN_X, CHECKBOXALIGNX_TEXT_ALIGN_Y, CHECKBOXALIGNX_CVAR, CHECKBOXALIGNX_TOOLTIP ) \ + itemDef { \ + name "checkalx"##CHECKBOXALIGNX_TEXT \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOXALIGNX_X) $evalfloat(CHECKBOXALIGNX_Y) $evalfloat(CHECKBOXALIGNX_W) $evalfloat(CHECKBOXALIGNX_H) \ + type ITEM_TYPE_CHECKBOX \ + text CHECKBOXALIGNX_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CHECKBOXALIGNX_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(CHECKBOXALIGNX_W)+CHECKBOXALIGNX_TEXT_ALIGN_X) \ + textaligny CHECKBOXALIGNX_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOXALIGNX_CVAR \ + visible 1 \ + tooltip CHECKBOXALIGNX_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "checkalx"##CHECKBOXALIGNX_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "checkalx"##CHECKBOXALIGNX_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define CHECKBOXNOTEXT( CHECKBOXNOTEXT_NAME, CHECKBOXNOTEXT_X, CHECKBOXNOTEXT_Y, CHECKBOXNOTEXT_W, CHECKBOXNOTEXT_H, CHECKBOXNOTEXT_CVAR, CHECKBOXNOTEXT_TOOLTIP ) \ + itemDef { \ + name CHECKBOXNOTEXT_NAME \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOXNOTEXT_X) $evalfloat(CHECKBOXNOTEXT_Y) $evalfloat(CHECKBOXNOTEXT_W) $evalfloat(CHECKBOXNOTEXT_H) \ + type ITEM_TYPE_CHECKBOX \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOXNOTEXT_CVAR \ + visible 1 \ + tooltip CHECKBOXNOTEXT_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor CHECKBOXNOTEXT_NAME forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor CHECKBOXNOTEXT_NAME forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define CHECKBOXNOTEXTACTION( CHECKBOXNOTEXTACTION_NAME, CHECKBOXNOTEXTACTION_X, CHECKBOXNOTEXTACTION_Y, CHECKBOXNOTEXTACTION_W, CHECKBOXNOTEXTACTION_H, CHECKBOXNOTEXTACTION_CVAR, CHECKBOXNOTEXTACTION_ACTION, CHECKBOXNOTEXTACTION_TOOLTIP ) \ + itemDef { \ + name CHECKBOXNOTEXTACTION_NAME \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOXNOTEXTACTION_X) $evalfloat(CHECKBOXNOTEXTACTION_Y) $evalfloat(CHECKBOXNOTEXTACTION_W) $evalfloat(CHECKBOXNOTEXTACTION_H) \ + type ITEM_TYPE_CHECKBOX \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOXNOTEXTACTION_CVAR \ + visible 1 \ + tooltip CHECKBOXNOTEXTACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor CHECKBOXNOTEXTACTION_NAME forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor CHECKBOXNOTEXTACTION_NAME forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + CHECKBOXNOTEXTACTION_ACTION \ + } \ + } + +#define CHECKBOXACTION( CHECKBOXACTION_X, CHECKBOXACTION_Y, CHECKBOXACTION_W, CHECKBOXACTION_H, CHECKBOXACTION_TEXT, CHECKBOXACTION_TEXT_SCALE, CHECKBOXACTION_TEXT_ALIGN_Y, CHECKBOXACTION_CVAR, CHECKBOXACTION_ACTION, CHECKBOXACTION_TOOLTIP ) \ + itemDef { \ + name "checkaction"##CHECKBOXACTION_TEXT \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOXACTION_X) $evalfloat(CHECKBOXACTION_Y) $evalfloat(CHECKBOXACTION_W) $evalfloat(CHECKBOXACTION_H) \ + type ITEM_TYPE_CHECKBOX \ + text CHECKBOXACTION_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CHECKBOXACTION_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(CHECKBOXACTION_W)) \ + textaligny CHECKBOXACTION_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOXACTION_CVAR \ + visible 1 \ + tooltip CHECKBOXACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "checkaction"##CHECKBOXACTION_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "checkaction"##CHECKBOXACTION_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + CHECKBOXACTION_ACTION \ + } \ + } + +#define CHECKBOXALIGNXACTION( CHECKBOXALIGNXACTION_X, CHECKBOXALIGNXACTION_Y, CHECKBOXALIGNXACTION_W, CHECKBOXALIGNXACTION_H, CHECKBOXALIGNXACTION_TEXT, CHECKBOXALIGNXACTION_TEXT_SCALE, CHECKBOXALIGNXACTION_TEXT_ALIGN_X, CHECKBOXALIGNXACTION_TEXT_ALIGN_Y, CHECKBOXALIGNXACTION_CVAR, CHECKBOXALIGNXACTION_ACTION, CHECKBOXALIGNXACTION_TOOLTIP ) \ + itemDef { \ + name "checkactionalx"##CHECKBOXALIGNXACTION_TEXT \ + group GROUP_NAME \ + rect $evalfloat(CHECKBOXALIGNXACTION_X) $evalfloat(CHECKBOXALIGNXACTION_Y) $evalfloat(CHECKBOXALIGNXACTION_W) $evalfloat(CHECKBOXALIGNXACTION_H) \ + type ITEM_TYPE_CHECKBOX \ + text CHECKBOXALIGNXACTION_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CHECKBOXALIGNXACTION_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(CHECKBOXALIGNXACTION_W)+CHECKBOXALIGNXACTION_TEXT_ALIGN_X) \ + textaligny CHECKBOXALIGNXACTION_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CHECKBOXALIGNXACTION_CVAR \ + visible 1 \ + tooltip CHECKBOXALIGNXACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "checkactionalx"##CHECKBOXALIGNXACTION_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "checkactionalx"##CHECKBOXALIGNXACTION_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + CHECKBOXALIGNXACTION_ACTION \ + } \ + } + +#define TRICHECKBOXACTION( TRICHECKBOXACTION_X, TRICHECKBOXACTION_Y, TRICHECKBOXACTION_W, TRICHECKBOXACTION_H, TRICHECKBOXACTION_TEXT, TRICHECKBOXACTION_TEXT_SCALE, TRICHECKBOXACTION_TEXT_ALIGN_Y, TRICHECKBOXACTION_CVAR, TRICHECKBOXACTION_ACTION, TRICHECKBOXACTION_TOOLTIP ) \ + itemDef { \ + name "tricheckaction"##TRICHECKBOXACTION_TEXT \ + group GROUP_NAME \ + rect $evalfloat(TRICHECKBOXACTION_X) $evalfloat(TRICHECKBOXACTION_Y) $evalfloat(TRICHECKBOXACTION_W) $evalfloat(TRICHECKBOXACTION_H) \ + type ITEM_TYPE_TRICHECKBOX \ + text TRICHECKBOXACTION_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale TRICHECKBOXACTION_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(TRICHECKBOXACTION_W)) \ + textaligny TRICHECKBOXACTION_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar TRICHECKBOXACTION_CVAR \ + visible 1 \ + tooltip TRICHECKBOXACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "tricheckaction"##TRICHECKBOXACTION_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "tricheckaction"##TRICHECKBOXACTION_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + TRICHECKBOXACTION_ACTION \ + } \ + } + +#define TRICHECKBOXACTIONMULTI( TRICHECKBOXACTIONMULTI_X, TRICHECKBOXACTIONMULTI_Y, TRICHECKBOXACTIONMULTI_W, TRICHECKBOXACTIONMULTI_H, TRICHECKBOXACTIONMULTI_TEXT_SCALE, TRICHECKBOXACTIONMULTI_TEXT_ALIGN_Y, TRICHECKBOXACTIONMULTI_CVAR, TRICHECKBOXACTIONMULTI_CVARLIST, TRICHECKBOXACTIONMULTI_ACTION, TTRICHECKBOXACTIONMULTI_TOOLTIP ) \ + itemDef { \ + name "tricheckactionmulti"##TRICHECKBOXACTIONMULTI_CVAR \ + group GROUP_NAME \ + rect $evalfloat(TRICHECKBOXACTIONMULTI_X) $evalfloat(TRICHECKBOXACTIONMULTI_Y) $evalfloat(TRICHECKBOXACTIONMULTI_W) $evalfloat(TRICHECKBOXACTIONMULTI_H) \ + type ITEM_TYPE_TRICHECKBOX \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale TRICHECKBOXACTIONMULTI_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(TRICHECKBOXACTIONMULTI_W)) \ + textaligny TRICHECKBOXACTIONMULTI_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar TRICHECKBOXACTIONMULTI_CVAR \ + TRICHECKBOXACTIONMULTI_CVARLIST \ + visible 1 \ + tooltip TTRICHECKBOXACTIONMULTI_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "tricheckactionmulti"##TRICHECKBOXACTIONMULTI_CVAR forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "tricheckactionmulti"##TRICHECKBOXACTIONMULTI_CVAR forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + TRICHECKBOXACTIONMULTI_ACTION \ + } \ + } + +#define MULTI( MULTI_X, MULTI_Y, MULTI_W, MULTI_H, MULTI_TEXT, MULTI_TEXT_SCALE, MULTI_TEXT_ALIGN_Y, MULTI_CVAR, MULTI_CVARLIST, MULTI_TOOLTIP ) \ + itemDef { \ + name "multi"##MULTI_TEXT \ + group GROUP_NAME \ + rect $evalfloat(MULTI_X) $evalfloat(MULTI_Y) $evalfloat(MULTI_W) $evalfloat(MULTI_H) \ + type ITEM_TYPE_MULTI \ + text MULTI_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale MULTI_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(MULTI_W)) \ + textaligny MULTI_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar MULTI_CVAR \ + MULTI_CVARLIST \ + visible 1 \ + tooltip MULTI_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "multi"##MULTI_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "multi"##MULTI_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define MULTILEFT( MULTILEFT_X, MULTILEFT_Y, MULTILEFT_W, MULTILEFT_H, MULTILEFT_TEXT, MULTILEFT_TEXT_SCALE, MULTILEFT_TEXT_ALIGN_Y, MULTILEFT_CVAR, MULTILEFT_CVARLIST, MULTILEFT_TOOLTIP ) \ + itemDef { \ + name "multileft"##MULTILEFT_TEXT \ + group GROUP_NAME \ + rect $evalfloat(MULTILEFT_X) $evalfloat(MULTILEFT_Y) $evalfloat(MULTILEFT_W) $evalfloat(MULTILEFT_H) \ + type ITEM_TYPE_MULTI \ + text MULTILEFT_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale MULTILEFT_TEXT_SCALE \ + textaligny MULTILEFT_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar MULTILEFT_CVAR \ + MULTILEFT_CVARLIST \ + visible 1 \ + tooltip MULTILEFT_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "multileft"##MULTILEFT_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "multileft"##MULTILEFT_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define MULTIACTION( MULTIACTION_X, MULTIACTION_Y, MULTIACTION_W, MULTIACTION_H, MULTIACTION_TEXT, MULTIACTION_TEXT_SCALE, MULTIACTION_TEXT_ALIGN_Y, MULTIACTION_CVAR, MULTIACTION_CVARLIST, MULTIACTION_ACTION, MULTIACTION_TOOLTIP ) \ + itemDef { \ + name "multiaction"##MULTIACTION_TEXT \ + group GROUP_NAME \ + rect $evalfloat(MULTIACTION_X) $evalfloat(MULTIACTION_Y) $evalfloat(MULTIACTION_W) $evalfloat(MULTIACTION_H) \ + type ITEM_TYPE_MULTI \ + text MULTIACTION_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale MULTIACTION_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(MULTIACTION_W)) \ + textaligny MULTIACTION_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar MULTIACTION_CVAR \ + MULTIACTION_CVARLIST \ + visible 1 \ + tooltip MULTIACTION_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "multiaction"##MULTIACTION_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "multiaction"##MULTIACTION_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + MULTIACTION_ACTION \ + } \ + } + +#define MULTIACTIONLEFT( MULTIACTIONLEFT_X, MULTIACTIONLEFT_Y, MULTIACTIONLEFT_W, MULTIACTIONLEFT_H, MULTIACTIONLEFT_TEXT, MULTIACTIONLEFT_TEXT_SCALE, MULTIACTIONLEFT_TEXT_ALIGN_Y, MULTIACTIONLEFT_CVAR, MULTIACTIONLEFT_CVARLIST, MULTIACTIONLEFT_ACTION, MULTIACTIONLEFT_TOOLTIP ) \ + itemDef { \ + name "multiactionleft"##MULTIACTIONLEFT_TEXT \ + group GROUP_NAME \ + rect $evalfloat(MULTIACTIONLEFT_X) $evalfloat(MULTIACTIONLEFT_Y) $evalfloat(MULTIACTIONLEFT_W) $evalfloat(MULTIACTIONLEFT_H) \ + type ITEM_TYPE_MULTI \ + text MULTIACTIONLEFT_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale MULTIACTIONLEFT_TEXT_SCALE \ + textaligny MULTIACTIONLEFT_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar MULTIACTIONLEFT_CVAR \ + MULTIACTIONLEFT_CVARLIST \ + visible 1 \ + tooltip MULTIACTIONLEFT_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "multiactionleft"##MULTIACTIONLEFT_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "multiactionleft"##MULTIACTIONLEFT_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + MULTIACTIONLEFT_ACTION \ + } \ + } + +#define SLIDER( SLIDER_X, SLIDER_Y, SLIDER_W, SLIDER_H, SLIDER_TEXT, SLIDER_TEXT_SCALE, SLIDER_TEXT_ALIGN_Y, SLIDER_CVARFLOAT, SLIDER_TOOLTIP ) \ + itemDef { \ + name "slider"##SLIDER_TEXT \ + group GROUP_NAME \ + rect $evalfloat(SLIDER_X) $evalfloat(SLIDER_Y) $evalfloat(SLIDER_W) $evalfloat(SLIDER_H) \ + type ITEM_TYPE_SLIDER \ + text SLIDER_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale SLIDER_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(SLIDER_W)) \ + textaligny SLIDER_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvarFloat SLIDER_CVARFLOAT \ + visible 1 \ + tooltip SLIDER_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "slider"##SLIDER_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "slider"##SLIDER_TEXT forecolor .6 .6 .6 1 ; \ + } \ + } + +#define BIND( BIND_X, BIND_Y, BIND_W, BIND_H, BIND_TEXT, BIND_TEXT_SCALE, BIND_TEXT_ALIGN_Y, BIND_CVAR, BIND_TOOLTIP ) \ + itemDef { \ + name "bind"##BIND_TEXT \ + group GROUP_NAME \ + rect $evalfloat(BIND_X) $evalfloat(BIND_Y) $evalfloat(BIND_W) $evalfloat(BIND_H) \ + type ITEM_TYPE_BIND \ + text BIND_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale BIND_TEXT_SCALE \ + textalign ITEM_ALIGN_RIGHT \ + textalignx $evalfloat(0.5*(BIND_W)) \ + textaligny BIND_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar BIND_CVAR \ + visible 1 \ + tooltip BIND_TOOLTIP \ + \ + mouseEnter { \ + setitemcolor "bind"##BIND_TEXT forecolor .9 .9 .9 1 ; \ + } \ + \ + mouseExit { \ + setitemcolor "bind"##BIND_TEXT forecolor .6 .6 .6 1 ; \ + } \ + \ + action { \ + play "sound/menu/filter.wav" ; \ + } \ + } + +#define LABEL( LABEL_X, LABEL_Y, LABEL_W, LABEL_H, LABEL_TEXT, LABEL_TEXT_SCALE, LABEL_TEXT_ALIGN, LABEL_TEXT_ALIGN_X, LABEL_TEXT_ALIGN_Y ) \ + itemDef { \ + name "label"##LABEL_TEXT \ + group GROUP_NAME \ + rect $evalfloat(LABEL_X) $evalfloat(LABEL_Y) $evalfloat(LABEL_W) $evalfloat(LABEL_H) \ + type ITEM_TYPE_TEXT \ + text LABEL_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale LABEL_TEXT_SCALE \ + textalign LABEL_TEXT_ALIGN \ + textalignx $evalfloat(LABEL_TEXT_ALIGN_X) \ + textaligny $evalfloat(LABEL_TEXT_ALIGN_Y) \ + forecolor .6 .6 .6 1 \ + visible 1 \ + decoration \ + autowrapped \ + } + +#define LABELWHITE( LABELWHITE_X, LABELWHITE_Y, LABELWHITE_W, LABELWHITE_H, LABELWHITE_TEXT, LABELWHITE_TEXT_SCALE, LABELWHITE_TEXT_ALIGN, LABELWHITE_TEXT_ALIGN_X, LABELWHITE_TEXT_ALIGN_Y ) \ + itemDef { \ + name "labelwhite"##LABELWHITE_TEXT \ + group GROUP_NAME \ + rect $evalfloat(LABELWHITE_X) $evalfloat(LABELWHITE_Y) $evalfloat(LABELWHITE_W) $evalfloat(LABELWHITE_H) \ + type ITEM_TYPE_TEXT \ + text LABELWHITE_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale LABELWHITE_TEXT_SCALE \ + textalign LABELWHITE_TEXT_ALIGN \ + textalignx $evalfloat(LABELWHITE_TEXT_ALIGN_X) \ + textaligny $evalfloat(LABELWHITE_TEXT_ALIGN_Y) \ + forecolor 1 1 1 1 \ + visible 1 \ + decoration \ + autowrapped \ + } + +#define CVARLABEL( CVARLABEL_X, CVARLABEL_Y, CVARLABEL_W, CVARLABEL_H, CVARLABEL_CVAR, CVARLABEL_TEXT_SCALE, CVARLABEL_TEXT_ALIGN, CVARLABEL_TEXT_ALIGN_X, CVARLABEL_TEXT_ALIGN_Y ) \ + itemDef { \ + name "cvarlabel"##CVARLABEL_CVAR \ + group GROUP_NAME \ + rect $evalfloat(CVARLABEL_X) $evalfloat(CVARLABEL_Y) $evalfloat(CVARLABEL_W) $evalfloat(CVARLABEL_H) \ + type ITEM_TYPE_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CVARLABEL_TEXT_SCALE \ + textalign CVARLABEL_TEXT_ALIGN \ + textalignx CVARLABEL_TEXT_ALIGN_X \ + textaligny CVARLABEL_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CVARLABEL_CVAR \ + visible 1 \ + decoration \ + autowrapped \ + } + +#define CVARFLOATLABEL( CVARFLOATLABEL_X, CVARFLOATLABEL_Y, CVARFLOATLABEL_W, CVARFLOATLABEL_H, CVARFLOATLABEL_CVAR, CVARFLOATLABEL_TEXT_SCALE, CVARFLOATLABEL_TEXT_ALIGN, CVARFLOATLABEL_TEXT_ALIGN_X, CVARFLOATLABEL_TEXT_ALIGN_Y ) \ + itemDef { \ + name "cvarfloatlabel"##CVARFLOATLABEL_CVAR \ + group GROUP_NAME \ + rect $evalfloat(CVARFLOATLABEL_X) $evalfloat(CVARFLOATLABEL_Y) $evalfloat(CVARFLOATLABEL_W) $evalfloat(CVARFLOATLABEL_H) \ + type ITEM_TYPE_TEXT \ + textfont UI_FONT_COURBD_21 \ + textstyle ITEM_TEXTSTYLE_SHADOWED \ + textscale CVARFLOATLABEL_TEXT_SCALE \ + textalign CVARFLOATLABEL_TEXT_ALIGN \ + textalignx CVARFLOATLABEL_TEXT_ALIGN_X \ + textaligny CVARFLOATLABEL_TEXT_ALIGN_Y \ + forecolor .6 .6 .6 1 \ + cvar CVARFLOATLABEL_CVAR \ + visible 1 \ + decoration \ + textasfloat \ + } diff --git a/src/SConscript.bspc b/src/SConscript.bspc new file mode 100644 index 0000000..46befc0 --- /dev/null +++ b/src/SConscript.bspc @@ -0,0 +1,144 @@ +# -*- mode: python -*- +# Enemy Territory build script +# TTimo +# http://scons.sourceforge.net + +import sys, os +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +bspc_string = """ +_files.c +aas_areamerging.c +aas_cfg.c +aas_create.c +aas_edgemelting.c +aas_facemerging.c +aas_file.c +aas_gsubdiv.c +aas_map.c +aas_prunenodes.c +aas_store.c +be_aas_bspc.c +../botlib/be_aas_bspq3.c +../botlib/be_aas_cluster.c +../botlib/be_aas_move.c +../botlib/be_aas_optimize.c +../botlib/be_aas_reach.c +../botlib/be_aas_sample.c +brushbsp.c +bspc.c +../qcommon/cm_load.c +../qcommon/cm_patch.c +../qcommon/cm_test.c +../qcommon/cm_trace.c +csg.c +faces.c +glfile.c +l_bsp_ent.c +l_bsp_q3.c +l_cmd.c +../botlib/l_libvar.c +l_log.c +l_math.c +l_mem.c +l_poly.c +../botlib/l_precomp.c +l_qfiles.c +../botlib/l_script.c +../botlib/l_struct.c +l_threads.c +l_utils.c +leakfile.c +map.c +map_q3.c +../qcommon/md4.c +nodraw.c +portals.c +prtfile.c +textures.c +tree.c +../qcommon/unzip.c +writebsp.c +aas_areamerging.h +aas_cfg.h +aas_create.h +aas_edgemelting.h +aas_facemerging.h +aas_file.h +aas_gsubdiv.h +aas_map.h +aas_prunenodes.h +aas_store.h +../botlib/aasfile.h +../botlib/be_aas.h +../game/be_aas.h +../botlib/be_aas_bsp.h +be_aas_bspc.h +../botlib/be_aas_cluster.h +../botlib/be_aas_debug.h +../botlib/be_aas_def.h +../botlib/be_aas_entity.h +../botlib/be_aas_file.h +../botlib/be_aas_funcs.h +../botlib/be_aas_main.h +../botlib/be_aas_move.h +../botlib/be_aas_optimize.h +../botlib/be_aas_reach.h +../botlib/be_aas_route.h +../botlib/be_aas_routealt.h +../botlib/be_aas_routetable.h +../botlib/be_aas_sample.h +../botlib/be_interface.h +../botlib/botlib.h +../qcommon/cm_local.h +../qcommon/cm_patch.h +../qcommon/cm_polylib.h +../qcommon/cm_public.h +l_bsp_ent.h +l_bsp_hl.h +l_bsp_q1.h +l_bsp_q2.h +l_bsp_q3.h +l_bsp_sin.h +l_cmd.h +../botlib/l_libvar.h +../botlib/l_log.h +l_log.h +l_math.h +l_mem.h +../botlib/l_memory.h +l_poly.h +../botlib/l_precomp.h +l_qfiles.h +../botlib/l_script.h +../botlib/l_struct.h +l_threads.h +../botlib/l_utils.h +l_utils.h +q2files.h +q3files.h +../game/q_shared.h +qbsp.h +../qcommon/qcommon.h +qfiles.h +../qcommon/qfiles.h +sinfiles.h +../game/surfaceflags.h +../qcommon/unzip.h +""" + +bspc_list = scons_utils.BuildList( 'bspc', bspc_string ) + +local_env = g_env.Clone() + +local_env.Append( CPPDEFINES = [ 'BSPC' ] ) + +source_list = bspc_list + +local_env['LINK'] = local_env['CC'] + +ret = local_env.Program( target = 'bspc', source = source_list ) +Return( 'ret' ) diff --git a/src/SConscript.cgame b/src/SConscript.cgame new file mode 100644 index 0000000..2a4a73c --- /dev/null +++ b/src/SConscript.cgame @@ -0,0 +1,85 @@ +# -*- mode: python -*- +# Enemy Territory build script +# TTimo +# http://scons.sourceforge.net + +import sys, os +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +cgame_string = """ +cg_atmospheric.c +cg_character.c +cg_commandmap.c +cg_consolecmds.c +cg_debriefing.c +cg_draw.c +cg_drawtools.c +cg_effects.c +cg_ents.c +cg_event.c +cg_fireteamoverlay.c +cg_fireteams.c +cg_flamethrower.c +cg_info.c +cg_limbopanel.c +cg_loadpanel.c +cg_localents.c +cg_main.c +cg_marks.c +cg_missionbriefing.c +cg_multiview.c +cg_newDraw.c +cg_particles.c +cg_players.c +cg_playerstate.c +cg_polybus.c +cg_popupmessages.c +cg_predict.c +cg_scoreboard.c +cg_servercmds.c +cg_snapshot.c +cg_sound.c +cg_spawn.c +cg_statsranksmedals.c +cg_syscalls.c +cg_trails.c +cg_view.c +cg_weapons.c +cg_window.c +../game/bg_animation.c +../game/bg_animgroup.c +../game/bg_character.c +../game/bg_classes.c +../game/bg_misc.c +../game/bg_pmove.c +../game/bg_slidemove.c +../game/bg_sscript.c +../game/bg_stats.c +../game/bg_tracemap.c +../game/q_math.c +../game/q_shared.c +../ui/ui_shared.c +""" + +cgame_list = scons_utils.BuildList( 'cgame', cgame_string ) + +local_env = g_env.Clone() + +local_env.Append( CPPDEFINES = [ 'CGAMEDLL' ] ) + +source_list = cgame_list + +local_env['LINK'] = local_env['CC'] + +# GAMERANGER isn't exported in all usage paths +try: + if ( GAMERANGER == '1' ): + local_env.Append( CPPDEFINES = [ 'GAMERANGER' ] ) +except: + pass + +ret = local_env.SharedLibrary( target = 'cgame', source = source_list ) +Return( 'ret' ) diff --git a/src/SConscript.core b/src/SConscript.core new file mode 100644 index 0000000..9b022dc --- /dev/null +++ b/src/SConscript.core @@ -0,0 +1,332 @@ +# -*- mode: python -*- +# Enemy Territory build script +# TTimo +# http://scons.sourceforge.net + +import sys, os +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +jpeg_string = """ +jcapimin.c +jchuff.c +jcinit.c +jccoefct.c +jccolor.c +jfdctflt.c +jcdctmgr.c +jcphuff.c +jcmainct.c +jcmarker.c +jcmaster.c +jcomapi.c +jcparam.c +jcprepct.c +jcsample.c +jdapimin.c +jdapistd.c +jdatasrc.c +jdcoefct.c +jdcolor.c +jddctmgr.c +jdhuff.c +jdinput.c +jdmainct.c +jdmarker.c +jdmaster.c +jdpostct.c +jdsample.c +jdtrans.c +jerror.c +jidctflt.c +jmemmgr.c +jmemnobs.c +jutils.c""" + +jpeg_list = scons_utils.BuildList( 'jpeg-6', jpeg_string ) + +renderer_string = """ +tr_animation_mdm.c +tr_animation_mds.c +tr_backend.c +tr_bsp.c +tr_cmds.c +tr_cmesh.c +tr_curve.c +tr_decals.c +tr_flares.c +tr_font.c +tr_image.c +tr_init.c +tr_light.c +tr_main.c +tr_marks.c +tr_mesh.c +tr_model.c +tr_noise.c +tr_scene.c +tr_shade.c +tr_shade_calc.c +tr_shader.c +tr_shadows.c +tr_sky.c +tr_surface.c +tr_world.c""" + +renderer_list = scons_utils.BuildList( 'renderer', renderer_string ) + +server_string = """ +sv_bot.c +sv_ccmds.c +sv_client.c +sv_game.c +sv_init.c +sv_main.c +sv_net_chan.c +sv_snapshot.c +sv_world.c""" + +server_list = scons_utils.BuildList( 'server', server_string ) + +qcommon_string = """ +cm_load.c +cm_patch.c +cm_polylib.c +cm_test.c +cm_trace.c +cmd.c +common.c +cvar.c +files.c +huffman.c +md4.c +msg.c +net_chan.c +unzip.c +vm.c +vm_interpreted.c""" + +if ( cpu == 'x86' ): + qcommon_string += " vm_x86.c" + +qcommon_list = scons_utils.BuildList( 'qcommon', qcommon_string ) + +splines_string = """ +math_angles.cpp +math_matrix.cpp +math_quaternion.cpp +math_vector.cpp +q_parse.cpp +q_shared.c +splines.cpp +util_str.cpp""" + + +splines_list = scons_utils.BuildList( 'splines', splines_string ) + +client_string = """ +cl_cgame.c +cl_cin.c +cl_console.c +cl_input.c +cl_keys.c +cl_main.c +cl_net_chan.c +cl_parse.c +cl_scrn.c +cl_ui.c +snd_adpcm.c +snd_dma.c +snd_mem.c +snd_mix.c +snd_wavelet.c""" + +client_list = scons_utils.BuildList( 'client', client_string ) + +linux_sources = [ + 'unix/linux_signals.c', + 'unix/unix_main.c', + 'unix/unix_net.c', + 'unix/unix_shared.c', + 'game/q_shared.c', + 'game/q_math.c', +# 'unix/matha.S' +] + +linux_full_sources = [ + 'unix/linux_glimp.c', + 'unix/linux_qgl.c', + 'unix/linux_snd.c', + 'unix/linux_joystick.c', +# 'unix/snd_mixa.S' +] + +mac_sources = [ + 'game/q_shared.c', + 'game/q_math.c', + 'mac/mac_main.cpp', + 'mac/mac_input.cpp', + 'mac/mac_glimp.cpp', + 'mac/mac_files.cpp', + 'mac/mac_net.cpp', + 'mac/mac_console.c', + 'mac/mac_snddma.c', + 'mac/CarbonMouse.cpp', + 'mac/mac_event.cpp', +# 'mac/HID Manager/HID_Configure.c', +# 'mac/HID Manager/HID_CFM.c', + 'mac/MacPrefs.cpp', + 'mac/PickMonitor/pickmonitor.cpp', + 'mac/PickMonitor/userpane.cpp', + 'mac/mac_qgl.c', + 'mac/mac_specific.cpp', + 'mac/AGLUtils.cpp', + 'mac/CDrawSprocket.cpp' +] + +win32_sources = [ + 'win32/win_main.c', + 'win32/win_net.c', + 'win32/win_shared.c', + 'win32/win_wndproc.c', + 'win32/win_syscon.c', + 'game/q_shared.c', + 'game/q_math.c', +] + +win32_full_sources = [ + 'win32/win_input.c', + 'win32/win_glimp.c', + 'win32/win_qgl.c', + 'win32/win_gamma.c', + 'win32/win_snd.c', +] + +botlib_list = [ + 'be_aas_bspq3.c', + 'be_aas_cluster.c', + 'be_aas_debug.c', + 'be_aas_entity.c', + 'be_aas_file.c', + 'be_aas_main.c', + 'be_aas_move.c', + 'be_aas_optimize.c', + 'be_aas_reach.c', + 'be_aas_route.c', + 'be_aas_routealt.c', + 'be_aas_routetable.c', + 'be_aas_sample.c', + 'be_ai_char.c', + 'be_ai_chat.c', + 'be_ai_gen.c', + 'be_ai_goal.c', + 'be_ai_move.c', + 'be_ai_weap.c', + 'be_ai_weight.c', + 'be_ea.c', + 'be_interface.c', + 'l_crc.c', + 'l_libvar.c', + 'l_log.c', + 'l_memory.c', + 'l_precomp.c', + 'l_script.c', + 'l_struct.c' +] + +botlib_env = g_env.Clone() +botlib_env.Append( CPPDEFINES = [ 'BOTLIB' ] ) + +botlib_objs = [] +for i in botlib_list: + botlib_objs.append( botlib_env.StaticObject( os.path.join( 'botlib', i ) ) ) + +staticx11_libs = [ 'libXxf86dga.a', 'libXxf86vm.a' ] +if ( os.path.exists( os.path.join( '/usr/lib', staticx11_libs[0] ) ) ): + staticx11_libs = [ os.path.join( '/usr/lib', i ) for i in staticx11_libs ] +else: + staticx11_libs = [ os.path.join( '/usr/X11R6/lib', i ) for i in staticx11_libs ] + +if ( g_os == 'Linux' ): + nasm_env = Environment( tools = [ 'nasm' ] ) + nasm_env['ASFLAGS'] = '-f elf' + snapvector = nasm_env.StaticObject( 'unix/snapvector.asm' ) + + # gas assembly, with preprocessing + gas_env = Environment( tools = [ 'gcc', 'gas' ] ) + gas_env.Append( CPPDEFINES = [ 'ELF' ] ) + gas_env.Append( ASFLAGS = [ '-m32', '-x', 'assembler-with-cpp' ] ) + matha = gas_env.StaticObject( 'unix/matha.spp' ) + snd_mixa = gas_env.StaticObject( 'unix/snd_mixa.spp' ) + +splines_env = g_env.Clone() +splines_lib = splines_env.StaticLibrary( splines_list ) + +local_env = g_env.Clone() + +if ( g_os == 'Linux' ): + local_env.Append( LIBS = [ 'dl' ] ) + if ( local_dedicated == 0 ): + local_env.Append( LIBS = [ 'X11', 'Xext', 'm' ] ) + local_env.Append( LIBPATH = [ '/usr/X11R6/lib' ] ) + local_env.Append( CPPPATH = [ 'curl-7.12.2/include' ] ) + else: + local_env.Append( CPPDEFINES = [ 'DEDICATED' ] ) + local_env.Append( LIBS = [ 'm' ] ) + + if gcc3 == 0: + g_env.Append(LINKFLAGS=[ os.popen( g_env['CXX'] + ' -print-file-name=libstdc++.a' ).read().rstrip() ]) + else: + local_env['ENV']['CC'] = g_env['CC'] + local_env['LINK'] = 'unix/staticlinkcpp.sh' +elif ( g_os == 'win32' ): + local_env.Append( LIBS = [ 'm', 'winmm', 'wsock32', 'gdi32', 'ole32', 'iphlpapi' ] ) + if ( local_dedicated == 0 ): + local_env.Append( LIBS = [ 'dinput', 'dsound' ] ) + local_env.Append( CPPPATH = [ 'curl-7.12.2/include' ] ) + else: + local_env.Append( CPPDEFINES = [ 'DEDICATED' ] ) + local_env['LINK'] = g_env['CXX'] +elif ( g_os == 'Darwin' ): + local_env.Append( LINKFLAGS = [ '-framework', 'AGL', '-framework', 'OpenGL', '-framework', 'Carbon', '-framework', 'IOKit', '-framework', 'DrawSprocket', '-lcurl' ] ) + +if ( MASTER != '' ): + local_env.Append( CPPDEFINES = [ 'MASTER_SERVER_NAME=\\"%s\\"' % MASTER ] ) + +source_list = server_list + qcommon_list +if ( g_os == 'Linux' ): + source_list += linux_sources + snapvector + matha +elif ( g_os == 'win32' ): + source_list += win32_sources +elif ( g_os == 'Darwin' ): + if ( BUILD == 'release' ): + local_env.Append( CPPFLAGS = [ '-include', 'mac/MacPrefix.h' ] ) + else: + local_env.Append( CPPFLAGS = [ '-include', 'mac/MacPrefixDebug.h' ] ) + mac_objects = [] + for i in mac_sources: + mac_objects.append( local_env.StaticObject( i ) ) + source_list += mac_objects + +source_list += botlib_objs + +if ( local_dedicated == 0 ): + source_list += jpeg_list + source_list += renderer_list + source_list += client_list + source_list += [ 'qcommon/dl_main_curl.c' ] + source_list += curl_lib + if ( g_os == 'Linux' ): + source_list += snd_mixa + source_list += linux_full_sources + source_list += staticx11_libs + if ( g_os == 'win32' ): + source_list += win32_full_sources +else: + source_list += [ 'null/null_client.c', 'null/null_input.c', 'null/null_snddma.c', 'qcommon/dl_main_stubs.c' ] +source_list += splines_lib + +ret = local_env.Program( target = 'et', source = source_list ) +Return( 'ret' ) diff --git a/src/SConscript.curl b/src/SConscript.curl new file mode 100644 index 0000000..c6f5464 --- /dev/null +++ b/src/SConscript.curl @@ -0,0 +1,49 @@ +# -*- mode: python -*- +# ET build script +# TTimo +# http://scons.sourceforge.net + +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +class idBuildCurl( scons_utils.idSetupBase ): + + def Compile( self, target = None, source = None, env = None ): + if ( g_os == 'win32' ): + self.TrySimpleCommand( 'cd ' + self.curl_dir + '/lib/ ; make -f Makefile.m32 clean' ) + cmd = 'cd ' + self.curl_dir + '/lib/ ; CC=\'' + env['CC'] + '\' make -f Makefile.m32 libcurl.a' + self.SimpleCommand( cmd ) + if ( self.debug ): + self.SimpleCommand( 'cd ' + self.curl_dir + ' ; mv ./lib/libcurl.a ./lib/.libs/libcurl-debug.a' ) + else: + self.SimpleCommand( 'cd ' + self.curl_dir + ' ; mv ./lib/libcurl.a ./lib/.libs/libcurl-release.a' ) + else: + self.TrySimpleCommand( 'cd ' + self.curl_dir + ' ; make distclean' ) + cmd = 'cd ' + self.curl_dir + ' ; CC=\'' + env['CC'] + '\' ./configure --enable-shared=no --enable-static=yes --enable-http --enable-ftp --disable-gopher --enable-file --disable-ldap --disable-dict --disable-telnet --disable-manual --enable-libgcc --disable-ipv6 --disable-ares --without-ssl --without-zlib --without-libidn ' + if ( self.debug ): + cmd += '--enable-debug' + else: + cmd += '--disable-debug' + self.SimpleCommand( cmd ) + self.SimpleCommand( 'cd ' + self.curl_dir + ' ; make' ) + if ( self.debug ): + self.SimpleCommand( 'cd ' + self.curl_dir + ' ; mv ./lib/.libs/libcurl.a ./lib/.libs/libcurl-debug.a' ) + else: + self.SimpleCommand( 'cd ' + self.curl_dir + ' ; mv ./lib/.libs/libcurl.a ./lib/.libs/libcurl-release.a' ) + return 0 + +build = idBuildCurl() +build.curl_dir = 'curl-7.12.2' +if ( local_curl == 1 ): + build.debug = 1 + target_name = '#' + build.curl_dir + '/lib/.libs/libcurl-debug.a' +else: + build.debug = 0 + target_name = '#' + build.curl_dir + '/lib/.libs/libcurl-release.a' + +g_env.Command( target_name, None, Action( build.Compile ) ) + +curl_libs = [ target_name ] +Return( 'curl_libs' ) diff --git a/src/SConscript.game b/src/SConscript.game new file mode 100644 index 0000000..4365af3 --- /dev/null +++ b/src/SConscript.game @@ -0,0 +1,97 @@ +# -*- mode: python -*- +# Enemy Territory build script +# TTimo +# http://scons.sourceforge.net + +import sys, os +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +botai_string = """ +ai_cmd.c +ai_dmgoal_mp.c +ai_dmnet_mp.c +ai_dmq3.c +ai_main.c +ai_script.c +ai_script_actions.c +ai_team.c +""" + +botai_list = scons_utils.BuildList( 'botai', botai_string ) + +qagame_string = """ +bg_animation.c +bg_animgroup.c +bg_campaign.c +bg_character.c +bg_classes.c +bg_misc.c +bg_pmove.c +bg_slidemove.c +bg_sscript.c +bg_stats.c +bg_tracemap.c +g_active.c +g_alarm.c +g_antilag.c +g_bot.c +g_buddy_list.c +g_character.c +g_client.c +g_cmds.c +g_cmds_ext.c +g_combat.c +g_config.c +g_fireteams.c +g_items.c +g_main.c +g_match.c +g_mem.c +g_misc.c +g_missile.c +g_mover.c +g_multiview.c +g_props.c +g_referee.c +g_save.c +g_script.c +g_script_actions.c +g_session.c +g_spawn.c +g_stats.c +g_sv_entities.c +g_svcmds.c +g_syscalls.c +g_systemmsg.c +g_target.c +g_team.c +g_teammapdata.c +g_trigger.c +g_utils.c +g_vote.c +g_weapon.c +q_math.c +q_shared.c +""" + +qagame_list = scons_utils.BuildList( 'game', qagame_string ) + +#botai_objs = [] +#for i in botai_list: +# botlib_ai.append( botai_env.StaticObject( os.path.join( 'botlib', i ) ) ) + +local_env = g_env.Clone() + +local_env.Append( CPPDEFINES = [ 'GAMEDLL' ] ) + +local_env['LINK'] = local_env['CC'] + +source_list = botai_list +source_list += qagame_list + +#ret = local_env.Program( target = 'et', source = source_list ) +ret = local_env.SharedLibrary( target = 'qagame', source = source_list ) +Return( 'ret' ) diff --git a/src/SConscript.ui b/src/SConscript.ui new file mode 100644 index 0000000..5142d24 --- /dev/null +++ b/src/SConscript.ui @@ -0,0 +1,39 @@ +# -*- mode: python -*- +# Enemy Territory build script +# TTimo +# http://scons.sourceforge.net + +import sys, os +import scons_utils + +Import( 'GLOBALS' ) +Import( GLOBALS ) + +ui_string = """ +ui_atoms.c +ui_gameinfo.c +ui_loadpanel.c +ui_main.c +ui_players.c +ui_shared.c +ui_syscalls.c +ui_util.c +../game/bg_campaign.c +../game/bg_classes.c +../game/bg_misc.c +../game/q_math.c +../game/q_shared.c +""" + +ui_list = scons_utils.BuildList( 'ui', ui_string ) + +local_env = g_env.Clone() + +local_env.Append( CPPDEFINES = [ 'UIDLL' ] ) + +source_list = ui_list + +local_env['LINK'] = local_env['CC'] + +ret = local_env.SharedLibrary( target = 'ui', source = source_list ) +Return( 'ret' ) diff --git a/src/SConstruct b/src/SConstruct new file mode 100644 index 0000000..dc69c93 --- /dev/null +++ b/src/SConstruct @@ -0,0 +1,408 @@ +# -*- mode: python -*- +# ET build script +# TTimo +# http://scons.sourceforge.net + +import sys, os, time, commands, re, pickle, StringIO, popen2, commands, pdb, zipfile, string, tempfile +import SCons + +import scons_utils + +conf_filename='site.conf' +# choose configuration variables which should be saved between runs +# ( we handle all those as strings ) +serialized=[ 'CC', 'CXX', 'JOBS', 'BUILD', 'BUILD_ROOT', 'DEDICATED', 'TARGET_CORE', 'TARGET_BSPC', 'TARGET_CGAME', 'TARGET_GAME', 'TARGET_UI', 'MASTER' ] + +# help ------------------------------------------- + +Help(""" +Usage: scons [OPTIONS] [TARGET] [CONFIG] + +[OPTIONS] and [TARGET] are covered in command line options, use scons -H + +[CONFIG]: KEY="VALUE" [...] +a number of configuration options saved between runs in the """ + conf_filename + """ file +erase """ + conf_filename + """ to start with default settings again + +CC (default gcc) +CXX (default g++) + Specify C and C++ compilers (defaults gcc and g++) + ex: CC="gcc-3.3" + You can use ccache and distcc, for instance: + CC="ccache distcc gcc" CXX="ccache distcc g++" + +JOBS (default 1) + Parallel build + +BUILD (default debug) + Use debug-all/debug/release to select build settings + ex: BUILD="release" + debug-all: no optimisations, debugging symbols + debug: -O -g + release: all optimisations, including CPU target etc. + +DEDICATED (default 2) + Control regular / dedicated type of build: + 0 - client + 1 - dedicated server + 2 - both + +TARGET_CORE (default 1) + Build engine + +TARGET_BSPC (default 0) + Build bspc utility + +TARGET_GAME (default 1) + Build game module + +TARGET_CGAME (default 1) + Build cgame module + +TARGET_UI (default 1) + Build ui module + +BUILD_ROOT (default 'build') + change the build root directory + +NOCONF (default 0, not saved) + ignore site configuration and use defaults + command line only + +MASTER (default '') + set to override the default master server address + +COPYBINS (default 0, not saved) + copy the binaries in a ready-to-release format + +BUILDMPBIN (default 0, not saved) + build mp_bin.pk3 using bin/ directories and game binaries for all platforms +""" +) + +# end help --------------------------------------- + +# sanity ----------------------------------------- + +EnsureSConsVersion( 0, 96 ) + +# end sanity ------------------------------------- + +# system detection ------------------------------- + +# CPU type +cpu = commands.getoutput('uname -m') +dll_cpu = '???' # grmbl, alternative naming for .so +exp = re.compile('.*i?86.*') +if exp.match(cpu): + cpu = 'x86' + dll_cpu = 'i386' +else: + cpu = commands.getoutput('uname -p') + if ( cpu == 'powerpc' ): + cpu = 'ppc' + dll_cpu = cpu + else: + cpu = 'cpu' + dll_cpu = cpu +OS = commands.getoutput( 'uname -s' ) +if ( OS == 'Darwin' ): + print 'EXPERIMENTAL - INCOMPLETE' + print 'Use the XCode projects to compile ET universal binaries for OSX' + cpu = 'osx' + dll_cpu = 'osx' + +print 'cpu: ' + cpu + +# end system detection --------------------------- + +# default settings ------------------------------- + +CC = 'gcc -m32' +CXX = 'g++ -m32' +JOBS = '1' +BUILD = 'debug' +DEDICATED = '2' +TARGET_CORE = '1' +TARGET_GAME = '1' +TARGET_CGAME = '1' +TARGET_UI = '1' +TARGET_BSPC = '0' +BUILD_ROOT = 'build' +NOCONF = '0' +MASTER = '' +COPYBINS = '0' +BUILDMPBIN = '0' + +# end default settings --------------------------- + +# arch detection --------------------------------- + +def gcc_major(): + major = os.popen( CC + ' -dumpversion' ).read().strip() + major = re.sub('^([^.]+)\\..*$', '\\1', major) + print 'gcc major: %s' % major + return major + +gcc3 = (gcc_major() != '2') + +def gcc_is_mingw(): + mingw = os.popen( CC + ' -dumpmachine' ).read() + return re.search('mingw', mingw) != None + +if gcc_is_mingw(): + g_os = 'win32' +elif OS == 'Darwin': + g_os = 'Darwin' +else: + g_os = 'Linux' +print 'OS: %s' % g_os + +# end arch detection ----------------------------- + +# site settings ---------------------------------- + +if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): + site_dict = {} + if (os.path.exists(conf_filename)): + site_file = open(conf_filename, 'r') + p = pickle.Unpickler(site_file) + site_dict = p.load() + print 'Loading build configuration from ' + conf_filename + ':' + for k, v in site_dict.items(): + exec_cmd = k + '=\'' + v + '\'' + print ' ' + exec_cmd + exec(exec_cmd) +else: + print 'Site settings ignored' + +# end site settings ------------------------------ + +# command line settings -------------------------- + +for k in ARGUMENTS.keys(): + exec_cmd = k + '=\'' + ARGUMENTS[k] + '\'' + print 'Command line: ' + exec_cmd + exec( exec_cmd ) + +# end command line settings ---------------------- + +# save site configuration ---------------------- + +if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): + for k in serialized: + exec_cmd = 'site_dict[\'' + k + '\'] = ' + k + exec(exec_cmd) + + site_file = open(conf_filename, 'w') + p = pickle.Pickler(site_file) + p.dump(site_dict) + site_file.close() + +# end save site configuration ------------------ + +# general configuration, target selection -------- + +g_build = BUILD_ROOT + '/' + BUILD + +SConsignFile( 'scons.signatures' ) + +SetOption('num_jobs', JOBS) + +if ( OS == 'Linux' ): + LINK = CC +else: + LINK = CXX + +# common flags +# BASE + CORE + OPT for engine +# BASE + GAME + OPT for game + +BASECPPFLAGS = [ ] +CORECPPPATH = [ ] +CORELIBPATH = [ ] +CORECPPFLAGS = [ ] +GAMECPPFLAGS = [ ] +BASELINKFLAGS = [ ] +CORELINKFLAGS = [ ] + +# for release build, further optimisations that may not work on all files +OPTCPPFLAGS = [ ] + +if ( OS == 'Darwin' ): + BASECPPFLAGS += [ '-D__MACOS__', '-Wno-long-double', '-Wno-unknown-pragmas', '-Wno-trigraphs', '-fpascal-strings', '-fasm-blocks', '-Wreturn-type', '-Wunused-variable', '-ffast-math', '-fno-unsafe-math-optimizations', '-fvisibility=hidden', '-mmacosx-version-min=10.4', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk' ] + BASECPPFLAGS += [ '-arch', 'i386' ] + BASELINKFLAGS += [ '-arch', 'i386' ] + +BASECPPFLAGS.append( '-pipe' ) +# warn all +BASECPPFLAGS.append( '-Wall' ) +# don't wrap gcc messages +BASECPPFLAGS.append( '-fmessage-length=0' ) + +if ( BUILD == 'debug-all' ): + BASECPPFLAGS.append( '-g' ) + BASECPPFLAGS.append( '-D_DEBUG' ) +elif ( BUILD == 'debug' ): + BASECPPFLAGS.append( '-g' ) + BASECPPFLAGS.append( '-O1' ) + BASECPPFLAGS.append( '-D_DEBUG' ) +elif ( BUILD == 'release' ): + BASECPPFLAGS.append( '-DNDEBUG' ) + if ( OS == 'Linux' ): + # -fomit-frame-pointer: gcc manual indicates -O sets this implicitely, + # only if that doesn't affect debugging + # on Linux, this affects backtrace capability, so I'm assuming this is needed + # -finline-functions: implicit at -O3 + # -fschedule-insns2: implicit at -O3 + # -funroll-loops ? + # -mfpmath=sse -msse ? + OPTCPPFLAGS = [ '-O3', '-march=i686', '-Winline', '-ffast-math', '-fomit-frame-pointer', '-finline-functions', '-fschedule-insns2' ] + elif ( OS == 'Darwin' ): + OPTCPPFLAGS = [] +else: + print 'Unknown build configuration ' + BUILD + sys.exit(0) + +# create the build environements +g_base_env = Environment( ENV = os.environ, CC = CC, CXX = CXX, LINK = LINK, CPPFLAGS = BASECPPFLAGS, LINKFLAGS = BASELINKFLAGS, CPPPATH = CORECPPPATH, LIBPATH = CORELIBPATH ) +scons_utils.SetupUtils( g_base_env ) + +g_env = g_base_env.Clone() + +g_env['CPPFLAGS'] += OPTCPPFLAGS +g_env['CPPFLAGS'] += CORECPPFLAGS +g_env['LINKFLAGS'] += CORELINKFLAGS + +if ( OS == 'Darwin' ): + # configure for dynamic bundle + g_env['SHLINKFLAGS'] = '$LINKFLAGS -bundle -flat_namespace -undefined suppress' + g_env['SHLIBSUFFIX'] = '.so' + +# maintain this dangerous optimization off at all times +g_env.Append( CPPFLAGS = '-fno-strict-aliasing' ) + +if ( int(JOBS) > 1 ): + print 'Using buffered process output' + scons_utils.SetupBufferedOutput( g_env ) + +# mark the globals + +local_dedicated = 0 +# curl +local_curl = 0 # config selection +curl_lib = [] + +GLOBALS = 'g_env OS g_os BUILD local_dedicated curl_lib local_curl MASTER gcc3 cpu' + +# end general configuration ---------------------- + +# win32 cross compilation ---------------------- + +if g_os == 'win32' and os.name != 'nt': + # mingw doesn't define the cpu type, but our only target is x86 + g_env.Append(CPPDEFINES = '_M_IX86=400') + g_env.Append(LINKFLAGS = '-static-libgcc') + # scons doesn't support cross-compiling, so set these up manually + g_env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + g_env['WIN32DEFSUFFIX'] = '.def' + g_env['PROGSUFFIX'] = '.exe' + g_env['SHLIBSUFFIX'] = '.dll' + g_env['SHCCFLAGS'] = '$CCFLAGS' + +# end win32 cross compilation ------------------ + +# targets ---------------------------------------- + +toplevel_targets = [] + +# build curl if needed +if ( TARGET_CORE == '1' and DEDICATED != '1' and OS != 'Darwin' ): + # 1: debug, 2: release + if ( BUILD == 'release' ): + local_curl = 2 + else: + local_curl = 1 + Export( 'GLOBALS ' + GLOBALS ) + curl_lib = SConscript( 'SConscript.curl' ) + +if ( TARGET_CORE == '1' ): + if ( DEDICATED == '0' or DEDICATED == '2' ): + local_dedicated = 0 + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/core', '.', duplicate = 0 ) + et = SConscript( g_build + '/core/SConscript.core' ) + if ( g_os == 'win32' ): + toplevel_targets.append( InstallAs( '#et.exe', et ) ) + else: + toplevel_targets.append( InstallAs( '#et.' + cpu, et ) ) + + if ( DEDICATED == '1' or DEDICATED == '2' ): + local_dedicated = 1 + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/dedicated', '.', duplicate = 0 ) + etded = SConscript( g_build + '/dedicated/SConscript.core' ) + if ( g_os == 'win32' ): + toplevel_targets.append( InstallAs( '#etded.exe', etded ) ) + else: + toplevel_targets.append( InstallAs( '#etded.' + cpu, etded ) ) + +if ( TARGET_BSPC == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/bspc', '.', duplicate = 0 ) + bspc = SConscript( g_build + '/bspc/SConscript.bspc' ) + toplevel_targets.append( InstallAs( '#bspc.' + cpu, bspc ) ) + +if ( TARGET_GAME == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/game', '.', duplicate = 0 ) + game = SConscript( g_build + '/game/SConscript.game' ) + toplevel_targets.append( InstallAs( '#qagame.mp.%s.so' % dll_cpu, game ) ) + +if ( TARGET_CGAME == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/cgame', '.', duplicate = 0 ) + cgame = SConscript( g_build + '/cgame/SConscript.cgame' ) + toplevel_targets.append( InstallAs( '#cgame.mp.%s.so' % dll_cpu, cgame ) ) + +if ( TARGET_UI == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/ui', '.', duplicate = 0 ) + ui = SConscript( g_build + '/ui/SConscript.ui' ) + toplevel_targets.append( InstallAs( '#ui.mp.%s.so' % dll_cpu, ui ) ) + +class CopyBins(scons_utils.idSetupBase): + def copy_bins( self, target, source, env ): + for i in source: + j = os.path.normpath( os.path.join( os.path.dirname( i.abspath ), '../bin', os.path.basename( i.abspath ) ) ) + self.SimpleCommand( 'cp ' + i.abspath + ' ' + j ) + if ( OS == 'Linux' ): + self.SimpleCommand( 'strip ' + j ) + else: + # see strip and otool man pages on mac + self.SimpleCommand( 'strip -ur ' + j ) + +copybins_target = [] +if ( COPYBINS != '0' ): + copy = CopyBins() + copybins_target.append( Command( 'copybins', toplevel_targets, Action( copy.copy_bins ) ) ) + +class MpBin(scons_utils.idSetupBase): + def mp_bin( self, target, source, env ): + temp_dir = tempfile.mkdtemp( prefix = 'mp_bin' ) + self.SimpleCommand( 'cp ../bin/ui* ' + temp_dir ) + self.SimpleCommand( 'cp ../bin/cgame* ' + temp_dir ) + # zip the mac bundles + mac_bundle_dir = tempfile.mkdtemp( prefix = 'mp_mac' ) + self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/ui_mac.bundle" ' + mac_bundle_dir ) + self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/cgame_mac.bundle" ' + mac_bundle_dir ) + self.SimpleCommand( 'find %s -name \.svn | xargs rm -rf' % mac_bundle_dir ) + self.SimpleCommand( 'cd %s ; zip -r -D %s/ui_mac.zip ui_mac.bundle ; mv %s/ui_mac.zip %s/ui_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) + self.SimpleCommand( 'cd %s ; zip -r -D %s/cgame_mac.zip cgame_mac.bundle ; mv %s/cgame_mac.zip %s/cgame_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) + mp_bin_path = os.path.abspath( os.path.join ( os.getcwd(), '../etmain/mp_bin.pk3' ) ) + self.SimpleCommand( 'cd %s ; zip -r -D %s *' % ( temp_dir, mp_bin_path ) ) + +if ( BUILDMPBIN != '0' ): + mp_bin = MpBin() + mpbin_target = Command( 'mp_bin', toplevel_targets + copybins_target, Action( mp_bin.mp_bin ) ) + +# end targets ------------------------------------ diff --git a/src/SConstruct.sdk b/src/SConstruct.sdk new file mode 100644 index 0000000..cee6562 --- /dev/null +++ b/src/SConstruct.sdk @@ -0,0 +1,380 @@ +# -*- mode: python -*- +# ET build script +# TTimo +# http://scons.sourceforge.net + +import sys, os, time, commands, re, pickle, StringIO, popen2, commands, pdb, zipfile, string, tempfile +import SCons + +import scons_utils + +conf_filename='site.conf' +# choose configuration variables which should be saved between runs +# ( we handle all those as strings ) +serialized=[ 'CC', 'CXX', 'JOBS', 'BUILD', 'BUILD_ROOT', 'TARGET_CGAME', 'TARGET_GAME', 'TARGET_UI', 'GAMERANGER' ] + +# help ------------------------------------------- + +Help(""" +Usage: scons [OPTIONS] [TARGET] [CONFIG] + +[OPTIONS] and [TARGET] are covered in command line options, use scons -H + +[CONFIG]: KEY="VALUE" [...] +a number of configuration options saved between runs in the """ + conf_filename + """ file +erase """ + conf_filename + """ to start with default settings again + +CC (default gcc) +CXX (default g++) + Specify C and C++ compilers (defaults gcc and g++) + ex: CC="gcc-3.3" + You can use ccache and distcc, for instance: + CC="ccache distcc gcc" CXX="ccache distcc g++" + +JOBS (default 1) + Parallel build + +BUILD (default debug) + Use debug-all/debug/release to select build settings + ex: BUILD="release" + debug-all: no optimisations, debugging symbols + debug: -O -g + release: all optimisations, including CPU target etc. + +DEDICATED (default 2) + Control regular / dedicated type of build: + 0 - client + 1 - dedicated server + 2 - both + +TARGET_GAME (default 1) + Build game module + +TARGET_CGAME (default 1) + Build cgame module + +TARGET_UI (default 1) + Build ui module + +BUILD_ROOT (default 'build') + change the build root directory + +NOCONF (default 0, not saved) + ignore site configuration and use defaults + command line only + +COPYBINS (default 0, not saved) + copy the binaries in a ready-to-release format + +BUILDMPBIN (default 0, not saved) + build mp_bin.pk3 using bin/ directories and game binaries for all platforms + +BUILDBUNDLE (default 0, not saved) + create mac bundle files +""" +) + +# end help --------------------------------------- + +# sanity ----------------------------------------- + +EnsureSConsVersion( 0, 96 ) + +# end sanity ------------------------------------- + +# system detection ------------------------------- + +# CPU type +cpu = commands.getoutput('uname -m') +dll_cpu = '???' # grmbl, alternative naming for .so +exp = re.compile('.*i?86.*') +if exp.match(cpu): + cpu = 'x86' + dll_cpu = 'i386' +else: + cpu = commands.getoutput('uname -p') + if ( cpu == 'powerpc' ): + cpu = 'ppc' + dll_cpu = cpu + else: + cpu = 'cpu' + dll_cpu = cpu +OS = commands.getoutput( 'uname -s' ) + +print 'cpu: ' + cpu + +# end system detection --------------------------- + +# default settings ------------------------------- + +CC = 'gcc' +CXX = 'g++' +JOBS = '1' +BUILD = 'debug' +DEDICATED = '2' +TARGET_GAME = '1' +TARGET_CGAME = '1' +TARGET_UI = '1' +GAMERANGER = '0' +BUILD_ROOT = 'build' +NOCONF = '0' +COPYBINS = '0' +BUILDMPBIN = '0' +BUILDBUNDLE = '0' + +# end default settings --------------------------- + +# arch detection --------------------------------- + +def gcc_major(): + major = os.popen( CC + ' -dumpversion' ).read().strip() + print 'dumpversion: %s' % major + major = re.sub('^([^.]+)\\..*$', '\\1', major) + print 'gcc major: %s' % major + return major + +gcc3 = (gcc_major() != '2') + +def gcc_is_mingw(): + mingw = os.popen( CC + ' -dumpmachine' ).read() + return re.search('mingw', mingw) != None + +if gcc_is_mingw(): + g_os = 'win32' +elif cpu == 'ppc': + g_os = 'Darwin' +else: + g_os = 'Linux' +print 'os: %s' % g_os + +# end arch detection ----------------------------- + +# site settings ---------------------------------- + +if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): + site_dict = {} + if (os.path.exists(conf_filename)): + site_file = open(conf_filename, 'r') + p = pickle.Unpickler(site_file) + site_dict = p.load() + print 'Loading build configuration from ' + conf_filename + ':' + for k, v in site_dict.items(): + exec_cmd = k + '=\'' + v + '\'' + print ' ' + exec_cmd + exec(exec_cmd) +else: + print 'Site settings ignored' + +# end site settings ------------------------------ + +# command line settings -------------------------- + +for k in ARGUMENTS.keys(): + exec_cmd = k + '=\'' + ARGUMENTS[k] + '\'' + print 'Command line: ' + exec_cmd + exec( exec_cmd ) + +# end command line settings ---------------------- + +# save site configuration ---------------------- + +if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): + for k in serialized: + exec_cmd = 'site_dict[\'' + k + '\'] = ' + k + exec(exec_cmd) + + site_file = open(conf_filename, 'w') + p = pickle.Pickler(site_file) + p.dump(site_dict) + site_file.close() + +# end save site configuration ------------------ + +# arch detection --------------------------------- + +def gcc_major(): + major = os.popen( CC + ' -dumpversion' ).read().strip() + major = re.sub('^([^.]+)\\..*$', '\\1', major) + print 'gcc major: %s' % major + return major + +gcc3 = (gcc_major() != '2') + +def gcc_is_mingw(): + mingw = os.popen( CC + ' -dumpmachine' ).read() + return re.search('mingw', mingw) != None + +win32_build = gcc_is_mingw() + +# end arch detection ----------------------------- + +# general configuration, target selection -------- + +g_build = BUILD_ROOT + '/' + BUILD + +SConsignFile( 'scons.signatures' ) + +SetOption('num_jobs', JOBS) + +if ( OS == 'Linux' ): + LINK = CC +else: + LINK = CXX + +# common flags +# BASE + GAME + OPT for game + +BASECPPFLAGS = [ ] +CORECPPPATH = [ ] +CORELIBPATH = [ ] +CORECPPFLAGS = [ ] +GAMECPPFLAGS = [ ] +BASELINKFLAGS = [ ] +CORELINKFLAGS = [ ] + +# for release build, further optimisations that may not work on all files +OPTCPPFLAGS = [ ] + +if ( OS == 'Darwin' ): + # taken from xcode's default project settings + BASECPPFLAGS += [ '-D__MACOS__', '-Wno-long-double', '-arch', 'ppc', '-fasm-blocks', '-fpascal-strings', '-faltivec', '-mcpu=G4', '-mtune=G4', '-Wno-unknown-pragmas' ] + +BASECPPFLAGS.append( '-pipe' ) +# warn all +BASECPPFLAGS.append( '-Wall' ) +# don't wrap gcc messages +BASECPPFLAGS.append( '-fmessage-length=0' ) + +if ( BUILD == 'debug-all' ): + BASECPPFLAGS.append( '-g' ) + BASECPPFLAGS.append( '-D_DEBUG' ) +elif ( BUILD == 'debug' ): + BASECPPFLAGS.append( '-g' ) + BASECPPFLAGS.append( '-O1' ) + BASECPPFLAGS.append( '-D_DEBUG' ) +elif ( BUILD == 'release' ): + BASECPPFLAGS.append( '-DNDEBUG' ) + if ( OS == 'Linux' ): + # -fomit-frame-pointer: gcc manual indicates -O sets this implicitely, + # only if that doesn't affect debugging + # on Linux, this affects backtrace capability, so I'm assuming this is needed + # -finline-functions: implicit at -O3 + # -fschedule-insns2: implicit at -O3 + # -funroll-loops ? + # -mfpmath=sse -msse ? + OPTCPPFLAGS = [ '-O3', '-march=i686', '-Winline', '-ffast-math', '-fomit-frame-pointer', '-finline-functions', '-fschedule-insns2' ] + elif ( OS == 'Darwin' ): + OPTCPPFLAGS = [ '-O3', '-falign-functions=16', '-falign-loops=16', '-finline' ] +else: + print 'Unknown build configuration ' + BUILD + sys.exit(0) + +# create the build environments +g_base_env = Environment( ENV = os.environ, CC = CC, CXX = CXX, LINK = LINK, CPPFLAGS = BASECPPFLAGS, LINKFLAGS = BASELINKFLAGS, CPPPATH = CORECPPPATH, LIBPATH = CORELIBPATH ) +scons_utils.SetupUtils( g_base_env ) + +g_env = g_base_env.Clone() + +g_env['CPPFLAGS'] += OPTCPPFLAGS +g_env['CPPFLAGS'] += CORECPPFLAGS +g_env['LINKFLAGS'] += CORELINKFLAGS + +if ( OS == 'Darwin' ): + # configure for dynamic bundle + g_env['SHLINKFLAGS'] = '$LINKFLAGS -bundle -flat_namespace -undefined suppress' + g_env['SHLIBSUFFIX'] = '.so' + +# maintain this dangerous optimization off at all times +g_env.Append( CPPFLAGS = '-fno-strict-aliasing' ) + +if ( int(JOBS) > 1 ): + print 'Using buffered process output' + scons_utils.SetupBufferedOutput( g_env ) + +# mark the globals + +GLOBALS = 'g_env OS g_os BUILD gcc3 cpu GAMERANGER' + +# end general configuration ---------------------- + +# win32 cross compilation ---------------------- + +if g_os == 'win32' and os.name != 'nt': + # mingw doesn't define the cpu type, but our only target is x86 + g_env.Append(CPPDEFINES = '_M_IX86=400') + g_env.Append(LINKFLAGS = '-static-libgcc') + # scons doesn't support cross-compiling, so set these up manually + g_env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + g_env['WIN32DEFSUFFIX'] = '.def' + g_env['PROGSUFFIX'] = '.exe' + g_env['SHLIBSUFFIX'] = '.dll' + g_env['SHCCFLAGS'] = '$CCFLAGS' + +# end win32 cross compilation ------------------ + +# targets ---------------------------------------- + +toplevel_targets = [] + +if ( TARGET_GAME == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/game', '.', duplicate = 0 ) + game = SConscript( g_build + '/game/SConscript.game' ) + toplevel_targets.append( InstallAs( '#qagame.mp.%s.so' % dll_cpu, game ) ) + +if ( TARGET_CGAME == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/cgame', '.', duplicate = 0 ) + cgame = SConscript( g_build + '/cgame/SConscript.cgame' ) + toplevel_targets.append( InstallAs( '#cgame.mp.%s.so' % dll_cpu, cgame ) ) + +if ( TARGET_UI == '1' ): + Export( 'GLOBALS ' + GLOBALS ) + BuildDir( g_build + '/ui', '.', duplicate = 0 ) + ui = SConscript( g_build + '/ui/SConscript.ui' ) + toplevel_targets.append( InstallAs( '#ui.mp.%s.so' % dll_cpu, ui ) ) + +class CopyBins(scons_utils.idSetupBase): + def copy_bins( self, target, source, env ): + for i in source: + j = os.path.normpath( os.path.join( os.path.dirname( i.abspath ), '../bin', os.path.basename( i.abspath ) ) ) + self.SimpleCommand( 'cp ' + i.abspath + ' ' + j ) + if ( OS == 'Linux' ): + self.SimpleCommand( 'strip ' + j ) + else: + # see strip and otool man pages on mac + self.SimpleCommand( 'strip -ur ' + j ) + +copybins_target = [] +if ( COPYBINS != '0' ): + copy = CopyBins() + copybins_target.append( Command( 'copybins', toplevel_targets, Action( copy.copy_bins ) ) ) + +class MpBin(scons_utils.idSetupBase): + def mp_bin( self, target, source, env ): + temp_dir = tempfile.mkdtemp( prefix = 'mp_bin' ) + self.SimpleCommand( 'cp ../bin/ui* ' + temp_dir ) + self.SimpleCommand( 'cp ../bin/cgame* ' + temp_dir ) + # zip the mac bundles + mac_bundle_dir = tempfile.mkdtemp( prefix = 'mp_mac' ) + self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/ui_mac.bundle" ' + mac_bundle_dir ) + self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/cgame_mac.bundle" ' + mac_bundle_dir ) + self.SimpleCommand( 'find %s -name \.svn | xargs rm -rf' % mac_bundle_dir ) + self.SimpleCommand( 'cd %s ; zip -r -D %s/ui_mac.zip ui_mac.bundle ; mv %s/ui_mac.zip %s/ui_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) + self.SimpleCommand( 'cd %s ; zip -r -D %s/cgame_mac.zip cgame_mac.bundle ; mv %s/cgame_mac.zip %s/cgame_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) + mp_bin_path = os.path.abspath( os.path.join ( os.getcwd(), '../etmain/mp_bin.pk3' ) ) + self.SimpleCommand( 'cd %s ; zip -r -D %s *' % ( temp_dir, mp_bin_path ) ) + +if ( BUILDMPBIN != '0' ): + mp_bin = MpBin() + mpbin_target = Command( 'mp_bin', toplevel_targets + copybins_target, Action( mp_bin.mp_bin ) ) + +class BuildBundle(scons_utils.idSetupBase): + def make_bundle( self, target, source, env ): + for i in source: + self.SimpleCommand( './makebundle.sh %s' % i ) + +if ( BUILDBUNDLE != '0' ): + build_bundle = BuildBundle() + bundle_target = Command( 'build_bundle', toplevel_targets, Action( build_bundle.make_bundle ) ) + +# end targets ------------------------------------ diff --git a/src/botai/ai_cmd.c b/src/botai/ai_cmd.c new file mode 100644 index 0000000..c172e03 --- /dev/null +++ b/src/botai/ai_cmd.c @@ -0,0 +1,1540 @@ +/* +=========================================================================== + +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: ai_cmd.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +#include "../game/g_local.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +// +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + + +#ifdef DEBUG +/* +================== +BotPrintTeamGoal +================== +*/ +void BotPrintTeamGoal( bot_state_t *bs ) { + char netname[MAX_NETNAME]; + float t; + + ClientName( bs->client, netname, sizeof( netname ) ); + t = bs->teamgoal_time - trap_AAS_Time(); + switch ( bs->ltgtype ) { + case LTG_TEAMHELP: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t ); + break; + } + case LTG_TEAMACCOMPANY: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t ); + break; + } + case LTG_GETFLAG: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t ); + break; + } + case LTG_RUSHBASE: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t ); + break; + } + case LTG_RETURNFLAG: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t ); + break; + } + case LTG_DEFENDKEYAREA: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t ); + break; + } + case LTG_GETITEM: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t ); + break; + } + case LTG_KILL: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t ); + break; + } + case LTG_CAMP: + case LTG_CAMPORDER: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t ); + break; + } + case LTG_PATROL: + { + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t ); + break; + } + default: + { + if ( bs->ctfroam_time > trap_AAS_Time() ) { + t = bs->ctfroam_time - trap_AAS_Time(); + BotAI_Print( PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t ); + } else { + BotAI_Print( PRT_MESSAGE, "%s: I've got a regular goal\n", netname ); + } + } + } +} +#endif //DEBUG + +/* +================== +BotGetItemTeamGoal + +FIXME: add stuff like "upper rocket launcher" +"the rl near the railgun", "lower grenade launcher" etc. +================== +*/ +int BotGetItemTeamGoal( char *goalname, bot_goal_t *goal ) { + int i; + + if ( !strlen( goalname ) ) { + return qfalse; + } + i = -1; + do { + i = trap_BotGetLevelItemGoal( i, goalname, goal ); + if ( i > 0 ) { // && !AvoidGoalTime(&bs->gs, goal.number)) + return qtrue; + } + } while ( i > 0 ); + return qfalse; +} + +/* +================== +BotGetMessageTeamGoal +================== +*/ +int BotGetMessageTeamGoal( bot_state_t *bs, char *goalname, bot_goal_t *goal ) { + bot_waypoint_t *cp; + + if ( BotGetItemTeamGoal( goalname, goal ) ) { + return qtrue; + } + + cp = BotFindWayPoint( bs->checkpoints, goalname ); + if ( cp ) { + memcpy( goal, &cp->goal, sizeof( bot_goal_t ) ); + return qtrue; + } + return qfalse; +} + +/* +================== +BotGetTime +================== +*/ +float BotGetTime( bot_match_t *match ) { + bot_match_t timematch; + char timestring[MAX_MESSAGE_SIZE]; + float t; + + //if the matched string has a time + if ( match->subtype & ST_TIME ) { + //get the time string + trap_BotMatchVariable( match, TIME, timestring, MAX_MESSAGE_SIZE ); + //match it to find out if the time is in seconds or minutes + if ( trap_BotFindMatch( timestring, &timematch, MTCONTEXT_TIME ) ) { + if ( timematch.type == MSG_FOREVER ) { + t = 99999999; + } else { + trap_BotMatchVariable( &timematch, TIME, timestring, MAX_MESSAGE_SIZE ); + if ( timematch.type == MSG_MINUTES ) { + t = atof( timestring ) * 60; + } else if ( timematch.type == MSG_SECONDS ) { + t = atof( timestring ); + } else { t = 0;} + } + //if there's a valid time + if ( t > 0 ) { + return trap_AAS_Time() + t; + } + } + } + return 0; +} + +/* +================== +FindClientByName +================== +*/ +int FindClientByName( char *name ) { + int i, j; + char buf[MAX_INFO_STRING]; + + for ( j = 0; j < level.numConnectedClients; j++ ) { + i = level.sortedClients[j]; + ClientName( i, buf, sizeof( buf ) ); + if ( !Q_stricmp( buf, name ) ) { + return i; + } + } + + for ( j = 0; j < level.numConnectedClients; j++ ) { + i = level.sortedClients[j]; + ClientName( i, buf, sizeof( buf ) ); + if ( stristr( buf, name ) ) { + return i; + } + } + + return -1; +} + +/* +================== +FindEnemyByName +================== +*/ +int FindEnemyByName( bot_state_t *bs, char *name ) { + int i, j; + char buf[MAX_INFO_STRING]; + + for ( j = 0; j < level.numConnectedClients; j++ ) { + i = level.sortedClients[j]; + if ( BotSameTeam( bs, i ) ) { + continue; + } + ClientName( i, buf, sizeof( buf ) ); + if ( !Q_stricmp( buf, name ) ) { + return i; + } + } + for ( j = 0; j < level.numConnectedClients; j++ ) { + i = level.sortedClients[j]; + if ( BotSameTeam( bs, i ) ) { + continue; + } + ClientName( i, buf, sizeof( buf ) ); + if ( stristr( buf, name ) ) { + return i; + } + } + return -1; +} + +/* +================== +NumPlayersOnSameTeam +================== +*/ +int NumPlayersOnSameTeam( bot_state_t *bs ) { + int i, j, num = 0; + + for ( j = 0; j < level.numConnectedClients; j++ ) { + i = level.sortedClients[j]; + if ( i == bs->client ) { + continue; + } + + if ( BotSameTeam( bs, j ) ) { + num++; + } + } + return num; +} + +/* +================== +TeamPlayIsOn +================== +*/ +int BotGetPatrolWaypoints( bot_state_t *bs, bot_match_t *match ) { + char keyarea[MAX_MESSAGE_SIZE]; + int patrolflags; + bot_waypoint_t *wp, *newwp, *newpatrolpoints; + bot_match_t keyareamatch; + bot_goal_t goal; + + newpatrolpoints = NULL; + patrolflags = 0; + // + trap_BotMatchVariable( match, KEYAREA, keyarea, MAX_MESSAGE_SIZE ); + // + while ( 1 ) { + if ( !trap_BotFindMatch( keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA ) ) { + trap_EA_SayTeam( bs->client, "what do you say?" ); + BotFreeWaypoints( newpatrolpoints ); + bs->patrolpoints = NULL; + return qfalse; + } + trap_BotMatchVariable( &keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE ); + if ( !BotGetMessageTeamGoal( bs, keyarea, &goal ) ) { + //BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL); + //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + BotFreeWaypoints( newpatrolpoints ); + bs->patrolpoints = NULL; + return qfalse; + } + //create a new waypoint + newwp = BotCreateWayPoint( keyarea, goal.origin, goal.areanum ); + //add the waypoint to the patrol points + newwp->next = NULL; + for ( wp = newpatrolpoints; wp && wp->next; wp = wp->next ) ; + if ( !wp ) { + newpatrolpoints = newwp; + newwp->prev = NULL; + } else { + wp->next = newwp; + newwp->prev = wp; + } + // + if ( keyareamatch.subtype & ST_BACK ) { + patrolflags = PATROL_LOOP; + break; + } else if ( keyareamatch.subtype & ST_REVERSE ) { + patrolflags = PATROL_REVERSE; + break; + } else if ( keyareamatch.subtype & ST_MORE ) { + trap_BotMatchVariable( &keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE ); + } else { + break; + } + } + // + if ( !newpatrolpoints || !newpatrolpoints->next ) { + trap_EA_SayTeam( bs->client, "I need more key points to patrol\n" ); + BotFreeWaypoints( newpatrolpoints ); + newpatrolpoints = NULL; + return qfalse; + } + // + BotFreeWaypoints( bs->patrolpoints ); + bs->patrolpoints = newpatrolpoints; + // + bs->curpatrolpoint = bs->patrolpoints; + // + return qtrue; +} + +/* +================== +BotAddressedToBot +================== +*/ +int BotAddressedToBot( bot_state_t *bs, bot_match_t *match ) { + char addressedto[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + char name[MAX_MESSAGE_SIZE]; + char botname[128]; + int client; + bot_match_t addresseematch; + + trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); + client = ClientFromName( netname ); + if ( client < 0 ) { + return qfalse; + } + if ( !BotSameTeam( bs, client ) ) { + return qfalse; + } + //if the message is addressed to someone + if ( match->subtype & ST_ADDRESSED ) { + trap_BotMatchVariable( match, ADDRESSEE, addressedto, sizeof( addressedto ) ); + //the name of this bot + ClientName( bs->client, botname, 128 ); + // + while ( trap_BotFindMatch( addressedto, &addresseematch, MTCONTEXT_ADDRESSEE ) ) { + if ( addresseematch.type == MSG_EVERYONE ) { + return qtrue; + } else if ( addresseematch.type == MSG_MULTIPLENAMES ) { + trap_BotMatchVariable( &addresseematch, TEAMMATE, name, sizeof( name ) ); + if ( strlen( name ) ) { + if ( stristr( botname, name ) ) { + return qtrue; + } + if ( stristr( bs->subteam, name ) ) { + return qtrue; + } + } + trap_BotMatchVariable( &addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE ); + } else { + trap_BotMatchVariable( &addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE ); + if ( strlen( name ) ) { + if ( stristr( botname, name ) ) { + return qtrue; + } + if ( stristr( bs->subteam, name ) ) { + return qtrue; + } + } + break; + } + } + //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto); + //trap_EA_Say(bs->client, buf); + return qfalse; + } else { + //make sure not everyone reacts to this message + if ( random() > 1.f / (float)NumPlayersOnSameTeam( bs ) ) { + return qfalse; + } + } + return qtrue; +} + +/* +================== +BotGPSToPosition +================== +*/ +int BotGPSToPosition( char *buf, vec3_t position ) { + int i, j = 0; + int num, sign; + + for ( i = 0; i < 3; i++ ) { + num = 0; + while ( buf[j] == ' ' ) j++; + if ( buf[j] == '-' ) { + j++; + sign = -1; + } else { + sign = 1; + } + while ( buf[j] ) { + if ( buf[j] >= '0' && buf[j] <= '9' ) { + num = num * 10 + buf[j] - '0'; + j++; + } else { + j++; + break; + } + } + BotAI_Print( PRT_MESSAGE, "%d\n", sign * num ); + position[i] = (float) sign * num; + } + return qtrue; +} + +/* +================== +BotMatch_HelpAccompany +================== +*/ +void BotMatch_HelpAccompany( bot_state_t *bs, bot_match_t *match ) { +/* + int client, other, areanum; + char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE]; + char itemname[MAX_MESSAGE_SIZE]; + bot_match_t teammatematch; + aas_entityinfo_t entinfo; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //get the team mate name + trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); + //get the client to help + if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && + //if someone asks for him or herself + teammatematch.type == MSG_ME) { + //get the netname + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + client = ClientFromName(netname); + other = qfalse; + } + else { + //asked for someone else + client = FindClientByName(teammate); + //if this is the bot self + if (client == bs->client) { + other = qfalse; + } + else if (!BotSameTeam(bs, client)) { + //FIXME: say "I don't help the enemy" + return; + } + else { + other = qtrue; + } + } + //if the bot doesn't know who to help (FindClientByName returned -1) + if (client < 0) { + if (other) BotAI_BotInitialChat(bs, "whois", teammate, NULL); + else BotAI_BotInitialChat(bs, "whois", netname, NULL); + trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //don't help or accompany yourself + if (client == bs->client) { + return; + } + // + bs->teamgoal.entitynum = -1; + BotEntityInfo(client, &entinfo); + //if info is valid (in PVS) + if (entinfo.valid) { + areanum = BotPointAreaNum(entinfo.number, entinfo.origin); + if (areanum && trap_AAS_AreaReachability(areanum)) { + bs->teamgoal.entitynum = client; + bs->teamgoal.areanum = areanum; + VectorCopy(entinfo.origin, bs->teamgoal.origin); + VectorSet(bs->teamgoal.mins, -8, -8, -8); + VectorSet(bs->teamgoal.maxs, 8, 8, 8); + } + } + //if no teamgoal yet + if (bs->teamgoal.entitynum < 0) { + //if near an item + if (match->subtype & ST_NEARITEM) { + //get the match variable + trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); + // + if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { + //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); + //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + } + } + // + if (bs->teamgoal.entitynum < 0) { + if (other) BotAI_BotInitialChat(bs, "whereis", teammate, NULL); + else BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); + trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //the team mate + bs->teammate = client; + //last time the team mate was assumed visible + bs->teammatevisible_time = trap_AAS_Time(); + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //get the team goal time + bs->teamgoal_time = BotGetTime(match); + //set the ltg type + if (match->type == MSG_HELP) { + bs->ltgtype = LTG_TEAMHELP; + if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME; + } + else { + bs->ltgtype = LTG_TEAMACCOMPANY; + if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME; + bs->arrive_time = 0; + } +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +*/ +} + +/* +================== +BotMatch_DefendKeyArea +================== +*/ +void BotMatch_DefendKeyArea( bot_state_t *bs, bot_match_t *match ) { + char itemname[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + //get the match variable + trap_BotMatchVariable( match, KEYAREA, itemname, sizeof( itemname ) ); + // + if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) { + //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); + //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_DEFENDKEYAREA; + //get the team goal time + bs->teamgoal_time = BotGetTime( match ); + //set the team goal time + if ( !bs->teamgoal_time ) { + bs->teamgoal_time = trap_AAS_Time() + TEAM_DEFENDKEYAREA_TIME; + } + //away from defending +#ifdef DEBUG + BotPrintTeamGoal( bs ); +#endif //DEBUG +} + +/* +================== +BotMatch_GetItem +================== +*/ +void BotMatch_GetItem( bot_state_t *bs, bot_match_t *match ) { + char itemname[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + //get the match variable + trap_BotMatchVariable( match, ITEM, itemname, sizeof( itemname ) ); + // + if ( !BotGetMessageTeamGoal( bs, itemname, &bs->teamgoal ) ) { + //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); + //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_GETITEM; + //set the team goal time + bs->teamgoal_time = trap_AAS_Time() + TEAM_GETITEM_TIME; +#ifdef DEBUG + BotPrintTeamGoal( bs ); +#endif //DEBUG +} + +/* +================== +BotMatch_Camp +================== +*/ +void BotMatch_Camp( bot_state_t *bs, bot_match_t *match ) { +/* + int client, areanum; + char netname[MAX_MESSAGE_SIZE]; + char itemname[MAX_MESSAGE_SIZE]; + aas_entityinfo_t entinfo; + + if (!TeamPlayIsOn()) return; + //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + // + trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); + //asked for someone else + client = FindClientByName(netname); + //if there's no valid client with this name + if (client < 0) { + BotAI_BotInitialChat(bs, "whois", netname, NULL); + trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //get the match variable + trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname)); + //in CTF it could be the base + if (match->subtype & ST_THERE) { + //camp at the spot the bot is currently standing + bs->teamgoal.entitynum = bs->entitynum; + bs->teamgoal.areanum = bs->areanum; + VectorCopy(bs->origin, bs->teamgoal.origin); + VectorSet(bs->teamgoal.mins, -8, -8, -8); + VectorSet(bs->teamgoal.maxs, 8, 8, 8); + } + else if (match->subtype & ST_HERE) { + //if this is the bot self + if (client == bs->client) return; + // + bs->teamgoal.entitynum = -1; + BotEntityInfo(client, &entinfo); + //if info is valid (in PVS) + if (entinfo.valid) { + areanum = BotPointAreaNum(entinfo.number, entinfo.origin); + if (areanum && trap_AAS_AreaReachability(areanum)) { + //NOTE: just cheat and assume the bot knows where the person is + //if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) { + bs->teamgoal.entitynum = client; + bs->teamgoal.areanum = areanum; + VectorCopy(entinfo.origin, bs->teamgoal.origin); + VectorSet(bs->teamgoal.mins, -8, -8, -8); + VectorSet(bs->teamgoal.maxs, 8, 8, 8); + //} + } + } + //if the other is not visible + if (bs->teamgoal.entitynum < 0) { + BotAI_BotInitialChat(bs, "whereareyou", netname, NULL); + trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + } + else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { + //BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); + //trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); + return; + } + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_CAMPORDER; + //get the team goal time + bs->teamgoal_time = BotGetTime(match); + //set the team goal time + if (!bs->teamgoal_time) bs->teamgoal_time = trap_AAS_Time() + TEAM_CAMP_TIME; + //the teammate that requested the camping + bs->teammate = client; + //not arrived yet + bs->arrive_time = 0; + // +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG +*/ +} + +/* +================== +BotMatch_Patrol +================== +*/ +void BotMatch_Patrol( bot_state_t *bs, bot_match_t *match ) { + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + //get the patrol waypoints + if ( !BotGetPatrolWaypoints( bs, match ) ) { + return; + } + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_PATROL; + //get the team goal time + bs->teamgoal_time = BotGetTime( match ); + //set the team goal time if not set already + if ( !bs->teamgoal_time ) { + bs->teamgoal_time = trap_AAS_Time() + TEAM_PATROL_TIME; + } + // +#ifdef DEBUG + BotPrintTeamGoal( bs ); +#endif //DEBUG +} + +/* +================== +BotMatch_GetFlag +================== +*/ +void BotMatch_GetFlag( bot_state_t *bs, bot_match_t *match ) { + //if not in CTF mode + return; +/* //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_GETFLAG; + //set the team goal time + bs->teamgoal_time = trap_AAS_Time() + CTF_GETFLAG_TIME; +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG*/ +} + +/* +================== +BotMatch_RushBase +================== +*/ +void BotMatch_RushBase( bot_state_t *bs, bot_match_t *match ) { + //if not in CTF mode + return; +/* //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_RUSHBASE; + //set the team goal time + bs->teamgoal_time = trap_AAS_Time() + CTF_RUSHBASE_TIME; + bs->rushbaseaway_time = 0; +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG*/ +} + + +/* +================== +BotMatch_ReturnFlag +================== +*/ +void BotMatch_ReturnFlag( bot_state_t *bs, bot_match_t *match ) { + //if not in CTF mode + return; +/* //if not addressed to this bot + if (!BotAddressedToBot(bs, match)) return; + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_RETURNFLAG; + //set the team goal time + bs->teamgoal_time = trap_AAS_Time() + CTF_RETURNFLAG_TIME; + bs->rushbaseaway_time = 0; +#ifdef DEBUG + BotPrintTeamGoal(bs); +#endif //DEBUG*/ +} + +/* +================== +BotMatch_JoinSubteam +================== +*/ +void BotMatch_JoinSubteam( bot_state_t *bs, bot_match_t *match ) { + char teammate[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + //get the sub team name + trap_BotMatchVariable( match, TEAMNAME, teammate, MAX_MESSAGE_SIZE ); + //set the sub team name + strncpy( bs->subteam, teammate, 32 ); + bs->subteam[31] = '\0'; + // + BotAI_BotInitialChat( bs, "joinedteam", teammate, NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); +} + +/* +================== +BotMatch_LeaveSubteam +================== +*/ +void BotMatch_LeaveSubteam( bot_state_t *bs, bot_match_t *match ) { + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + // + if ( strlen( bs->subteam ) ) { + BotAI_BotInitialChat( bs, "leftteam", bs->subteam, NULL ); + } //end if + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + strcpy( bs->subteam, "" ); +} + +/* +================== +BotMatch_LeaveSubteam +================== +*/ +void BotMatch_WhichTeam( bot_state_t *bs, bot_match_t *match ) { + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + // + if ( strlen( bs->subteam ) ) { + BotAI_BotInitialChat( bs, "inteam", bs->subteam, NULL ); + } else { + BotAI_BotInitialChat( bs, "noteam", NULL ); + } + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); +} + +/* +================== +BotMatch_CheckPoint +================== +*/ +void BotMatch_CheckPoint( bot_state_t *bs, bot_match_t *match ) { + int areanum; + char buf[MAX_MESSAGE_SIZE]; + vec3_t position; + bot_waypoint_t *cp; + + if ( !TeamPlayIsOn() ) { + return; + } + // + trap_BotMatchVariable( match, POSITION, buf, MAX_MESSAGE_SIZE ); + VectorClear( position ); + //BotGPSToPosition(buf, position); + sscanf( buf, "%f %f %f", &position[0], &position[1], &position[2] ); + position[2] += 0.5; + areanum = BotPointAreaNum( bs->client, position ); + if ( !areanum ) { + if ( BotAddressedToBot( bs, match ) ) { + BotAI_BotInitialChat( bs, "checkpoint_invalid", NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + } + return; + } + // + trap_BotMatchVariable( match, NAME, buf, MAX_MESSAGE_SIZE ); + //check if there already exists a checkpoint with this name + cp = BotFindWayPoint( bs->checkpoints, buf ); + if ( cp ) { + if ( cp->next ) { + cp->next->prev = cp->prev; + } + if ( cp->prev ) { + cp->prev->next = cp->next; + } else { bs->checkpoints = cp->next;} + cp->inuse = qfalse; + } + //create a new check point + cp = BotCreateWayPoint( buf, position, areanum ); + //add the check point to the bot's known chech points + cp->next = bs->checkpoints; + if ( bs->checkpoints ) { + bs->checkpoints->prev = cp; + } + bs->checkpoints = cp; + // + if ( BotAddressedToBot( bs, match ) ) { + Com_sprintf( buf, sizeof( buf ), "%1.0f %1.0f %1.0f", cp->goal.origin[0], + cp->goal.origin[1], + cp->goal.origin[2] ); + + BotAI_BotInitialChat( bs, "checkpoint_confirm", cp->name, buf, NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + } +} + +/* +================== +BotMatch_FormationSpace +================== +*/ +void BotMatch_FormationSpace( bot_state_t *bs, bot_match_t *match ) { + char buf[MAX_MESSAGE_SIZE]; + float space; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + // + trap_BotMatchVariable( match, NUMBER, buf, MAX_MESSAGE_SIZE ); + //if it's the distance in feet + if ( match->subtype & ST_FEET ) { + space = 0.3048 * 32 * atof( buf ); + } + //else it's in meters + else {space = 32 * atof( buf );} + //check if the formation intervening space is valid + if ( space < 48 || space > 500 ) { + space = 100; + } + +} + +/* +================== +BotMatch_Dismiss +================== +*/ +void BotMatch_Dismiss( bot_state_t *bs, bot_match_t *match ) { + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + // + bs->ltgtype = 0; + bs->lead_time = 0; + // + BotAI_BotInitialChat( bs, "dismissed", NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); +} + +/* +================== +BotMatch_StartTeamLeaderShip +================== +*/ +void BotMatch_StartTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) { + int client; + char teammate[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + //if chats for him or herself + if ( match->subtype & ST_I ) { + //get the team mate that will be the team leader + trap_BotMatchVariable( match, NETNAME, teammate, sizeof( teammate ) ); + strncpy( bs->teamleader, teammate, sizeof( bs->teamleader ) ); + bs->teamleader[sizeof( bs->teamleader )] = '\0'; + } + //chats for someone else + else { + //get the team mate that will be the team leader + trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) ); + client = FindClientByName( teammate ); + if ( client >= 0 ) { + ClientName( client, bs->teamleader, sizeof( bs->teamleader ) ); + } + } +} + +/* +================== +BotMatch_StopTeamLeaderShip +================== +*/ +void BotMatch_StopTeamLeaderShip( bot_state_t *bs, bot_match_t *match ) { + int client; + char teammate[MAX_MESSAGE_SIZE]; + char netname[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + //get the team mate that stops being the team leader + trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) ); + //if chats for him or herself + if ( match->subtype & ST_I ) { + trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); + client = FindClientByName( netname ); + } + //chats for someone else + else { + client = FindClientByName( teammate ); + } //end else + if ( client >= 0 ) { + if ( !Q_stricmp( bs->teamleader, ClientName( client, netname, sizeof( netname ) ) ) ) { + bs->teamleader[0] = '\0'; + } + } +} + +/* +================== +BotMatch_WhoIsTeamLeader +================== +*/ +void BotMatch_WhoIsTeamLeader( bot_state_t *bs, bot_match_t *match ) { + char netname[MAX_MESSAGE_SIZE]; + + if ( !TeamPlayIsOn() ) { + return; + } + + ClientName( bs->client, netname, sizeof( netname ) ); + //if this bot IS the team leader + if ( !Q_stricmp( netname, bs->teamleader ) ) { + trap_EA_SayTeam( bs->client, "I'm the team leader\n" ); + } +} + +/* +================== +BotMatch_WhatAreYouDoing +================== +*/ +void BotMatch_WhatAreYouDoing( bot_state_t *bs, bot_match_t *match ) { + char netname[MAX_MESSAGE_SIZE]; + char goalname[MAX_MESSAGE_SIZE]; + + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + // + switch ( bs->ltgtype ) { + case LTG_TEAMHELP: + { + trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); + EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE ); + BotAI_BotInitialChat( bs, "helping", netname, NULL ); + break; + } + case LTG_TEAMACCOMPANY: + { + trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); + EasyClientName( bs->teammate, netname, MAX_MESSAGE_SIZE ); + BotAI_BotInitialChat( bs, "accompanying", netname, NULL ); + break; + } + case LTG_DEFENDKEYAREA: + { + trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) ); + BotAI_BotInitialChat( bs, "defending", goalname, NULL ); + break; + } + case LTG_GETITEM: + { + trap_BotGoalName( bs->teamgoal.number, goalname, sizeof( goalname ) ); + BotAI_BotInitialChat( bs, "gettingitem", goalname, NULL ); + break; + } + case LTG_KILL: + { + ClientName( bs->teamgoal.entitynum, netname, sizeof( netname ) ); + BotAI_BotInitialChat( bs, "killing", netname, NULL ); + break; + } + case LTG_CAMP: + case LTG_CAMPORDER: + { + BotAI_BotInitialChat( bs, "camping", NULL ); + break; + } + case LTG_PATROL: + { + BotAI_BotInitialChat( bs, "patrolling", NULL ); + break; + } + case LTG_GETFLAG: + { + BotAI_BotInitialChat( bs, "capturingflag", NULL ); + break; + } + case LTG_RUSHBASE: + { + BotAI_BotInitialChat( bs, "rushingbase", NULL ); + break; + } + case LTG_RETURNFLAG: + { + BotAI_BotInitialChat( bs, "returningflag", NULL ); + break; + } + default: + { + BotAI_BotInitialChat( bs, "roaming", NULL ); + break; + } + } + //chat what the bot is doing + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); +} + +/* +================== +BotMatch_WhatIsMyCommand +================== +*/ +void BotMatch_WhatIsMyCommand( bot_state_t *bs, bot_match_t *match ) { + char netname[MAX_NETNAME]; + + ClientName( bs->client, netname, sizeof( netname ) ); + if ( Q_stricmp( netname, bs->teamleader ) != 0 ) { + return; + } + +} + +/* +================== +BotNearestVisibleItem +================== +*/ +float BotNearestVisibleItem( bot_state_t *bs, char *itemname, bot_goal_t *goal ) { + int i; + char name[64]; + bot_goal_t tmpgoal; + float dist, bestdist; + vec3_t dir; + bsp_trace_t trace; + + bestdist = 999999; + i = -1; + do { + i = trap_BotGetLevelItemGoal( i, itemname, &tmpgoal ); + trap_BotGoalName( tmpgoal.number, name, sizeof( name ) ); + if ( Q_stricmp( itemname, name ) != 0 ) { + continue; + } + VectorSubtract( tmpgoal.origin, bs->origin, dir ); + dist = VectorLength( dir ); + if ( dist < bestdist ) { + //trace from start to end + BotAI_Trace( &trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP ); + if ( trace.fraction >= 1.0 ) { + bestdist = dist; + memcpy( goal, &tmpgoal, sizeof( bot_goal_t ) ); + } + } + } while ( i > 0 ); + return bestdist; +} + +/* +================== +BotMatch_WhereAreYou +================== +*/ +void BotMatch_WhereAreYou( bot_state_t *bs, bot_match_t *match ) { + float dist, bestdist; + int i, bestitem /*, redflagtt, blueflagtt, redtobluett*/; + bot_goal_t goal; + char *nearbyitems[] = { + "Shotgun", + "Grenade Launcher", + "Rocket Launcher", + "Plasmagun", + "Railgun", + "Lightning Gun", + "BFG10K", + "Quad Damage", + "Regeneration", + "Battle Suit", + "Speed", + "Invisibility", + "Flight", + "Armor", + "Heavy Armor", + "Red Flag", + "Blue Flag", + NULL + }; + // + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + + bestitem = -1; + bestdist = 999999; + for ( i = 0; nearbyitems[i]; i++ ) { + dist = BotNearestVisibleItem( bs, nearbyitems[i], &goal ); + if ( dist < bestdist ) { + bestdist = dist; + bestitem = i; + } + } + if ( bestitem != -1 ) { + BotAI_BotInitialChat( bs, "location", nearbyitems[bestitem], NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + } +} + +/* +================== +BotMatch_LeadTheWay +================== +*/ +void BotMatch_LeadTheWay( bot_state_t *bs, bot_match_t *match ) { + aas_entityinfo_t entinfo; + char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE]; + int client, areanum, other; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + //if someone asks for someone else + if ( match->subtype & ST_SOMEONE ) { + //get the team mate name + trap_BotMatchVariable( match, TEAMMATE, teammate, sizeof( teammate ) ); + client = FindClientByName( teammate ); + //if this is the bot self + if ( client == bs->client ) { + other = qfalse; + } else if ( !BotSameTeam( bs, client ) ) { + //FIXME: say "I don't help the enemy" + return; + } else { + other = qtrue; + } + } else { + //get the netname + trap_BotMatchVariable( match, NETNAME, netname, sizeof( netname ) ); + client = ClientFromName( netname ); + other = qfalse; + } + //if the bot doesn't know who to help (FindClientByName returned -1) + if ( client < 0 ) { + BotAI_BotInitialChat( bs, "whois", netname, NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + return; + } + // + bs->lead_teamgoal.entitynum = -1; + BotEntityInfo( client, &entinfo ); + //if info is valid (in PVS) + if ( entinfo.valid ) { + areanum = BotPointAreaNum( entinfo.number, entinfo.origin ); + if ( areanum && trap_AAS_AreaReachability( areanum ) ) { + bs->lead_teamgoal.entitynum = client; + bs->lead_teamgoal.areanum = areanum; + VectorCopy( entinfo.origin, bs->lead_teamgoal.origin ); + VectorSet( bs->lead_teamgoal.mins, -8, -8, -8 ); + VectorSet( bs->lead_teamgoal.maxs, 8, 8, 8 ); + } + } + + if ( bs->teamgoal.entitynum < 0 ) { + if ( other ) { + BotAI_BotInitialChat( bs, "whereis", teammate, NULL ); + } else { BotAI_BotInitialChat( bs, "whereareyou", netname, NULL );} + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + return; + } + + bs->lead_time = trap_AAS_Time() + TEAM_LEAD_TIME; + + +} + +/* +================== +BotMatch_Kill +================== +*/ +void BotMatch_Kill( bot_state_t *bs, bot_match_t *match ) { + char enemy[MAX_MESSAGE_SIZE]; + int client; + + if ( !TeamPlayIsOn() ) { + return; + } + //if not addressed to this bot + if ( !BotAddressedToBot( bs, match ) ) { + return; + } + + trap_BotMatchVariable( match, ENEMY, enemy, sizeof( enemy ) ); + // + client = FindEnemyByName( bs, enemy ); + if ( client < 0 ) { + BotAI_BotInitialChat( bs, "whois", enemy, NULL ); + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + return; + } + bs->teamgoal.entitynum = client; + //set the time to send a message to the team mates + bs->teammessage_time = trap_AAS_Time() + 2 * random(); + //set the ltg type + bs->ltgtype = LTG_KILL; + //set the team goal time + bs->teamgoal_time = trap_AAS_Time() + TEAM_KILL_SOMEONE; +#ifdef DEBUG + BotPrintTeamGoal( bs ); +#endif //DEBUG +} + +/* +================== +BotMatchMessage +================== +*/ +int BotMatchMessage( bot_state_t *bs, char *message ) { + bot_match_t match; + + match.type = 0; + //if it is an unknown message + if ( !trap_BotFindMatch( message, &match, MTCONTEXT_ENTERGAME + | MTCONTEXT_INITIALTEAMCHAT + | MTCONTEXT_CTF ) ) { + return qfalse; + } + //react to the found message + switch ( match.type ) { + case MSG_HELP: //someone calling for help + case MSG_ACCOMPANY: //someone calling for company + { + BotMatch_HelpAccompany( bs, &match ); + break; + } + case MSG_DEFENDKEYAREA: //teamplay defend a key area + { + BotMatch_DefendKeyArea( bs, &match ); + break; + } + case MSG_CAMP: //camp somewhere + { + BotMatch_Camp( bs, &match ); + break; + } + case MSG_PATROL: //patrol between several key areas + { + BotMatch_Patrol( bs, &match ); + break; + } + case MSG_GETITEM: + { + BotMatch_GetItem( bs, &match ); + break; + } + case MSG_JOINSUBTEAM: //join a sub team + { + BotMatch_JoinSubteam( bs, &match ); + break; + } + case MSG_LEAVESUBTEAM: //leave a sub team + { + BotMatch_LeaveSubteam( bs, &match ); + break; + } + case MSG_WHICHTEAM: + { + BotMatch_WhichTeam( bs, &match ); + break; + } + case MSG_CHECKPOINT: //remember a check point + { + BotMatch_CheckPoint( bs, &match ); + break; + } + case MSG_CREATENEWFORMATION: //start the creation of a new formation + { + trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" ); + break; + } + case MSG_FORMATIONPOSITION: //tell someone his/her position in the formation + { + trap_EA_SayTeam( bs->client, "the part of my brain to create formations has been damaged" ); + break; + } + case MSG_FORMATIONSPACE: //set the formation space + { + BotMatch_FormationSpace( bs, &match ); + break; + } + case MSG_DOFORMATION: //form a certain formation + { + break; + } + case MSG_DISMISS: //dismiss someone + { + BotMatch_Dismiss( bs, &match ); + break; + } + case MSG_STARTTEAMLEADERSHIP: //someone will become the team leader + { + BotMatch_StartTeamLeaderShip( bs, &match ); + break; + } + case MSG_STOPTEAMLEADERSHIP: //someone will stop being the team leader + { + BotMatch_StopTeamLeaderShip( bs, &match ); + break; + } + case MSG_WHOISTEAMLAEDER: + { + BotMatch_WhoIsTeamLeader( bs, &match ); + break; + } + case MSG_WHATAREYOUDOING: //ask a bot what he/she is doing + { + BotMatch_WhatAreYouDoing( bs, &match ); + break; + } + case MSG_WHATISMYCOMMAND: + { + BotMatch_WhatIsMyCommand( bs, &match ); + break; + } + case MSG_WHEREAREYOU: + { + BotMatch_WhereAreYou( bs, &match ); + break; + } + case MSG_LEADTHEWAY: + { + BotMatch_LeadTheWay( bs, &match ); + break; + } + case MSG_KILL: + { + BotMatch_Kill( bs, &match ); + break; + } + case MSG_ENTERGAME: //someone entered the game + { + //NOTE: eliza chats will catch this + //BotMatchVariable(&match, NETNAME, netname); + //Com_sprintf(buf, sizeof(buf), "heya %s", netname); + //EA_Say(bs->client, buf); + break; + } + case MSG_WAIT: + { + break; + } + default: + { + BotAI_Print( PRT_MESSAGE, "unknown match type\n" ); + break; + } + } + return qtrue; +} diff --git a/src/botai/ai_cmd.h b/src/botai/ai_cmd.h new file mode 100644 index 0000000..5ea48b8 --- /dev/null +++ b/src/botai/ai_cmd.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: ai_cmd.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +int BotMatchMessage( bot_state_t *bs, char *message ); +void BotPrintTeamGoal( bot_state_t *bs ); + diff --git a/src/botai/ai_distances.h b/src/botai/ai_distances.h new file mode 100644 index 0000000..25fe1f3 --- /dev/null +++ b/src/botai/ai_distances.h @@ -0,0 +1,262 @@ +/* +=========================================================================== + +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: ai_distances.h + * + * desc: Distance constants used by the AI in Single Player + * + * + *****************************************************************************/ + +// Distance bots follow the leader +#define kBOT_FOLLOW_DIST 100 + +// When is a bot close enough to it's goal? +// For when the goal is another bot +#define kBOT_CLOSE_ENOUGH 72 + +// For when we have a location goal +#define kBOT_LOC_CLOSE_ENOUGH 40 + +// When looking for nearby goals (to pick up health/ammo/flags), how far away can we look +#define kBOT_NEARBY_GOAL_DIST 384 +// When looking for nearby goals (to pick up health/ammo/flags), how long can we take to get there +#define kBOT_NEARBY_GOAL_TIME 1000 + +// For goals where we need to touch our target - revive, get disguise, open door +#define kBOT_TOUCHING_DIST 50 + +// How far to retreat after reviving someone +#define kBOT_REVIVE_RETREAT_DIST 96 + +// How close we should be to call in an airstrike, or throw a smoke grenade +#define kBOT_AIRSTRIKE_DISTANCE 160 + +// The range we're using for the grenade launcher WP_M7 +#define kBOT_M7_RANGE 1024 + +// Ok, so these aren't distances. Oh well +// The health fractions at which bots start requesting a medic +#define kBOT_INJURED_LEVEL 0.4f +#define kBOT_REALLYINJURED_LEVEL 0.2f + +// It a client is going faster than this, we think they're moving +#define kBOT_ENTITY_MOVING_UNSTEALTHY_SPEED 2 + +// If our best enemy is this close to us, and we're on an mg42, drop +// the gun... +#define kBOT_DROP_MG42_DISTANCE 240 // 20 feet roughly + +// If our best enemy is this close, and out of our gun arc, drop +// the mg42 +#define kBOT_DROP_MG42_DISTANCE_OUT_OF_ARC 480 // 40 feet roughly + +// Really way too far for an mg42 to shoot +#define kBOT_MAX_MG42_TARGET_RANGE 2400 + +// Radius to call for help - idle friendly bots in this radius are expected to come help their teammates +#define kBOT_HELP_RADIUS 1000 + +// how close should player be to play the idle animation +#define kBOT_IDLE_ANIM_DISTANCE 384 + +// A distance which is much bigger than any we'd really ever need +#define kBOT_GIGANTIC_DISTANCE 999999 + +// The range out of which a bot will advance towards the player +#define kBOT_CHASE_RANGE 1200 + +// Don't go to a seek cover spot further than this +#define kBOT_MAX_SEEK_COVER_RANGE 1000 + +// Maximum distance enemies can be from you to be considered in the +// AAS_Retreat code +#define kBOT_MAX_RETREAT_ENEMY_DIST 2000 + +// How much away from our current pos we'd like to be on retreat +#define kBOT_RETREAT_FROM_CURRENT_POS_DIST 200 + +// How much away from any danger we'd like to be on retreat +#define kBOT_RETREAT_FROM_DANGER_DIST 800 + +// How close is close enough when retreating to the player +#define kBOT_RETREAT_TO_PLAYER_DIST 192 + +// How close is close enough to apply the "I'm near a leader" bonusses +#define kBOT_NEAR_LEADER_DISTANCE 800 + +// How far is far enough that we get max "No leader" penalty +#define kBOT_FAR_FROM_LEADER_DISTANCE 2000 + +// How long when following to change modes +#define kBOT_FOLLOW_DEFAULT_TIME 1500 +#define kBOT_FOLLOW_DEFAULT_RAND_TIME 2500 + +// time to change follow stance +#define kBOT_FOLLOW_STANCE_TIME 1000 +#define kBOT_FOLLOW_STANCE_RAND_TIME 4000 + +// Time after standing still to seek cover +#define kBOT_STAND_SEEKCOVER_TIME 5000 +#define kBOT_STAND_SEEKCOVER_RAND_TIME 3000 + +// Distance can travel on stand to seek cover - should be pretty small +#define kBOT_STAND_SEEKCOVER_DIST 256 + +/////////////////////////////////////// +// +// SPEED CONSTANTS +// +/////////////////////////////////////// + +#define kBOT_FOLLOW_SPEED_BONUS ( 1.25f ) + +/////////////////////////////////////// +// +// TIME CONSTANTS +// +// Most of these are in milliseconds +// +/////////////////////////////////////// + + +// A short period of time to indicate we were just shot by someone +#define kBOT_JUST_SHOT_TIME 1000 + +// Max travel time a bot will spend travelling to heal/give ammo on a give team health/ammo command +// NOTE: also checks autonomy range, so this is just an additional check +#define kBOT_MAX_RESUPPLY_TRAVEL_TIME 3000 + + +////////////////////////////// +// Combat time constants +// + +// Time to give up on an enemy when you haven't been able to shoot at him +#define kBOT_ENEMY_GIVEUP_TIME 20000 + +// How long minimum to stay at a combat spot? +#define kBOT_MIN_COMBAT_SPOT_TIME 3500 + +// Random addition for staying at a combat spot +#define kBOT_RANDOM_COMBAT_SPOT_TIME 5500 + +// Time to stay at a cover spot if we're switching between it and an exposed spot +#define kBOT_MIN_COVERSPOT_TIME_WITH_EXPOSED 12000 +// and random addition +#define kBOT_RANDOM_COVERSPOT_TIME_WITH_EXPOSED 4000 + +// How long minimum to stay crouched down? +#define kBOT_HIDE_MIN_TIME 3500 +// How much to vary the crouch time randomly +#define kBOT_HIDE_TIME_RANDOM 2500 + +// How long to stand up after hiding in a seek cover spot +#define kBOT_COVER_STAND_MIN_TIME 500 +// and random addition +#define kBOT_COVER_STAND_TIME_RANDOM 1000 + +// For how long after we've been shot will we do evasive maneuvers (only with no seek cover spot!) +#define kBOT_EVASIVE_MANEUVER_TIME 5000 + +// How often do we check to see if we can fire +#define kBOT_MIN_FIRE_CYCLE_TIME 1500 +#define kBOT_MAX_FIRE_CYCLE_TIME 2700 + +// For how long do we consider an axis cover spot claimed +#define kBOT_IGNORE_COMBAT_SPOT_TIME 500 + +// Don't keep replanning our retreat location too often. +#define kBOT_MIN_RETREAT_REPLAN_TIME 3000 + +// If we've retreated and not been shot for a while, go to Stand. +#define kBOT_MAX_RETREAT_TIME 45000 + +// Minimum time to wait between calling for help +#define kBOT_MIN_CALL_FOR_HELP_TIME 15000 + +// If an enemy is within this distance, bot won't go prone +#define kBOT_MIN_PRONE_DIST 256 + +//////////////////////////////////////////////// +// Retreat to Player constants +// + +// How frequently do we check to see if we should retreat? +#define kBOT_RETREAT_UPDATE_TIME 100 +#define kBOT_RETREAT_UPDATE_RANDOM 500 + +// Damage level below which we never break - this gets modified by the wimp factor +#define kBOT_RETREAT_DAMAGE_THRESHOLD 0.75f +// Damage level at which we are considered to be about to die +#define kBOT_RETREAT_ABOUT_TO_DIE_DAMAGE 0.8f + +// Multiplier to wimp factor to decide when we retreat to player +#define kBOT_RETREAT_MULTIPLIER 1.0 +// when we retreat all out +#define kBOT_RETREAT_ALLOUT_MULTIPLIER 2.0 + +// Percent of damage ratio to add to likeliness to break +#define kBOT_RETREAT_DAMAGE_FACTOR 0.5f + +// Multiplier to distance ratio to add to likeliness +#define kBOT_RETREAT_DISTANCE_FACTOR 0.3f + +// Maximum distFactor we care about for retreating +#define kBOT_MAX_RETREAT_DIST_FACTOR 3.0f + +// Amount to increase break likeliness if... +// can't see the player +#define kBOT_RETREAT_PLAYER_OUTOFSIGHT 0.2f + +// player is dead +#define kBOT_RETREAT_PLAYER_DEAD 0.4f + +// How long after retreating before we can engage again? +#define kBOT_RETREAT_TIMETOENGAGE 20000 + +//////////////////////////////////////////////// +// Combat penalty constants +// + +// How much to penalize damage ratio based on distance from leader +// NOTE: 1 == take full damage at max dist, 0 == use scripted ratio +#define NO_LEADER_DAMAGE_PENALTY ( 0.6f ) + +// How much to penalize the aiming accuracy based on distance from +// leader. 1 == can't hit at all, 0 == No penalty +#define NO_LEADER_MAX_AIM_PENALTY ( 0.5f ) + +// how much inaccuracy to remove. e.g. 0.5 = 50% less inaccuracy +#define AIM_ACCURACY_BONUS_PRONE 0.5 +#define AIM_ACCURACY_BONUS_CROUCH 0.3 +#define AIM_SKILL_BONUS_PRONE 0.5 +#define AIM_ACCURACY_ENEMY_PENALTY_PRONE 0.5 +#define AIM_ACCURACY_ENEMY_PENALTY_CROUCH 0.2 diff --git a/src/botai/ai_dmgoal_mp.c b/src/botai/ai_dmgoal_mp.c new file mode 100644 index 0000000..1b8c8bd --- /dev/null +++ b/src/botai/ai_dmgoal_mp.c @@ -0,0 +1,3079 @@ +/* +=========================================================================== + +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: ai_dmgoal_mp.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +// +// MULTIPLAYER GOAL AI +// + +#include "../game/g_local.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" +// +#include "ai_main.h" +#include "ai_team.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +// +#include "ai_dmnet_mp.h" +#include "ai_dmgoal_mp.h" + +static bot_goal_t target; + +/* +================== +BotMP_CheckClassActions() +================== +*/ +qboolean BotMP_CheckClassActions( bot_state_t *bs ) { + qboolean hasLeader; + // + // if carrying flag, screw others + if ( BotCarryingFlag( bs->client ) && bs->enemy > -1 ) { + return qfalse; + } + + // if we are following a carrier, or a non-bot, then stay close + hasLeader = qfalse; + if ( bs->leader > -1 ) { + if ( BotCarryingFlag( bs->leader ) || !( g_entities[bs->leader].r.svFlags & SVF_BOT ) ) { + hasLeader = qtrue; + } + } + +/* if( bs->script.frameFlags & BSFFL_MOVETOTARGET ) { + hasLeader = qtrue; // dont go so far if we're trying to get to a marker + }*/ + + if ( bs->ainode == AINode_MP_MoveToAutonomyRange ) { + hasLeader = qtrue; // dont go too far off course + } + + if ( BotClass_MedicCheckRevives( bs, ( hasLeader ? 300 : 600 ), &target, qtrue ) ) { + if ( target.entitynum != bs->target_goal.entitynum || bs->ainode != AINode_MP_MedicRevive ) { + bs->target_goal = target; + AIEnter_MP_MedicRevive( bs ); + } + return qtrue; + } + + if ( BotCarryingFlag( bs->client ) ) { + return qfalse; // only revive if we have flag (Gordon: or skip other checks if we have flag to make more sense...) + } + + if ( BotClass_MedicCheckGiveHealth( bs, ( hasLeader ? 200 : 800 ), &target ) ) { + if ( target.entitynum != bs->target_goal.entitynum || bs->ainode != AINode_MP_MedicGiveHealth ) { + bs->target_goal = target; + AIEnter_MP_MedicGiveHealth( bs ); + } + return qtrue; + } + + if ( BotClass_LtCheckGiveAmmo( bs, ( hasLeader ? 200 : 800 ), &target ) ) { + if ( target.entitynum != bs->target_goal.entitynum || bs->ainode != AINode_MP_GiveAmmo ) { + bs->target_goal = target; + AIEnter_MP_GiveAmmo( bs ); + } + return qtrue; + } + + return qfalse; +} + +/* +=================== +BotMP_CheckEmergencyGoals +=================== +*/ +qboolean BotMP_CheckEmergencyGoals( bot_state_t *bs ) { + gentity_t *trav, *flag; + int i, t; + int list[32], numList; + int oldest = 0, oldestTime, oldIgnoreTime, areanum, numTeammates; + float dist, bestDist; + vec3_t center, brushPos, vec; + trace_t tr; + // + if ( bs->last_checkemergencytargets > level.time - 300 ) { + return qfalse; + } + bs->last_checkemergencytargets = level.time + rand() % 200; + + // + oldIgnoreTime = bs->ignore_specialgoal_time; + bs->ignore_specialgoal_time = level.time + 2000; + numTeammates = BotNumTeamMates( bs, NULL, 0 ); + + // check for voice chats + BotCheckVoiceChats( bs ); + + // if a bot finds itself in a void, it should use DrirectMove() to get out of it + if ( !bs->areanum ) { + if ( bs->ainode == AINode_MP_NavigateFromVoid ) { + return qfalse; + } + AIEnter_MP_NavigateFromVoid( bs ); + return qtrue; + } + + // DEBUG MODE: bot_debug = 11 + trap_Cvar_Update( &bot_debug ); + if ( g_cheats.integer && bot_debug.integer == BOT_DEBUG_FOLLOW_PLAYER ) { + bs->leader = 0; + if ( BotGoalForEntity( bs, bs->leader, &target, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } else { + bs->leader = -1; + } + // DEBUG MODE: bot_debug = 12 (dont look for goals) + } else if ( g_cheats.integer && bot_debug.integer == BOT_DEBUG_FOLLOW_PLAYER + 1 ) { + return qfalse; + } + + if ( !BotIsPOW( bs ) ) { + // check for being outside autonomy range within reasonable cause + if ( BotCheckMovementAutonomy( bs, &bs->target_goal ) ) { + if ( bs->target_goal.entitynum == bs->leader && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + if ( bs->ainode == AINode_MP_MoveToAutonomyRange ) { + return qfalse; + } + // get back to it + AIEnter_MP_MoveToAutonomyRange( bs ); + return qtrue; + } + + // check for dropped flag + if ( BotFindDroppedFlag( &trav ) ) { + // if we are the owner and we just dropped it, then ignore it + if ( !( trav->r.ownerNum == bs->client && trav->botIgnoreTime > level.time ) ) { + // if we are already going for it + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; //keep going for it + } + // GO GET IT + // do we have a route to the enemy flag? + if ( BotGoalForEntity( NULL, trav->s.number, &target, BGU_HIGH ) ) { + target.flags = GFL_NOSLOWAPPROACH; + // + if ( VectorDistanceSquared( target.origin, bs->origin ) < ( 1600 * 1600 ) || trap_InPVS( target.origin, bs->origin ) ) { + if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ) ) { + bs->target_goal = target; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + //if (g_cheats.integer) + // G_Printf( "WARNING: dropped objective is unreachable\n" ); + } + } + } + } + } else { + return qtrue; + } + + // if we have a visible & damagable script_mover target, then we should try to blow it up + if ( ( trav = BotGetVisibleDamagableScriptMover( bs ) ) ) { + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_AttackTarget ) { + return qfalse; + } + + if ( BotGoalForEntity( bs, trav->s.number, &target, BGU_MAXIMUM ) ) { + bs->target_goal = target; + AIEnter_MP_AttackTarget( bs ); + return qtrue; + } + } + + // if a checkpoint is nearby, touch it + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_CHECKPOINT ) ) ) { + // if the opposition team controls this checkpoint, or it hasnt been captured yet + if ( trav->count == ( bs->sess.sessionTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS ) || ( trav->count < 0 ) ) { + // if we can see it + VectorAdd( trav->r.absmin, trav->r.absmax, center ); + VectorScale( center, 0.5, center ); + if ( VectorDistanceSquared( center, bs->origin ) > ( 1024 * 1024 ) ) { + continue; + } + center[0] = trav->r.absmax[0]; + center[1] = trav->r.absmax[1]; + VectorSubtract( trav->r.absmax, trav->r.absmin, vec ); + vec[2] = 0; + VectorNormalize( vec ); + VectorAdd( center, vec, brushPos ); + if ( !trap_InPVS( brushPos, bs->origin ) ) { + continue; + } + // GO GET IT + // do we have a route to the enemy flag? + if ( BotGoalForEntity( bs, trav->s.number, &target, BGU_MEDIUM ) ) { + // if we are already going for it + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; //keep going for it + } + target.flags = GFL_NOSLOWAPPROACH; + // + if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ) ) { + bs->target_goal = target; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } + } + } + + // in GT_WOLF mode on defense, if the flag is not at base, then all units should help get it back + if ( level.captureFlagMode ) { + // + if ( bs->sess.sessionTeam != level.attackingTeam ) { + // + if ( !BotFlagAtBase( bs->sess.sessionTeam, &flag ) ) { + // + // if we are pursuing the flag carrier, then continue + if ( bs->enemy >= 0 && BotCarryingFlag( bs->enemy ) ) { + return qfalse; + } + // + // go to the transmission point? + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !trav ) { + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( trav ) { + // + if ( BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) < (int)ceil( 0.5 * numTeammates ) ) { + // + // if we are already heading for it, then continue + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + // + VectorAdd( trav->r.absmin, trav->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + // find the best goal area + numList = trap_AAS_BBoxAreas( trav->r.absmin, trav->r.absmax, list, 32 ); + if ( numList ) { + oldestTime = -1; + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + if ( bestDist > 0 && oldestTime < 4000 && bestDist < 2000 ) { + BotClearGoal( &target ); + // use this as the goal origin + VectorCopy( center, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = trav->s.number; + target.flags = GFL_NOSLOWAPPROACH; + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + // if we are already heading there, continue + if ( bs->target_goal.entitynum && bs->target_goal.entitynum == trav->s.number ) { + return qfalse; + } + // + bs->target_goal = target; + BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + + }/* else { // go for the document room + + BotFlagAtBase(bs->sess.sessionTeam, &flag); + // + // do we have a route to the flag? + BotClearGoal(&target); + target.entitynum = flag->s.number; + VectorCopy( flag->r.currentOrigin, center ); + center[2] += 30; + target.areanum = trap_AAS_PointAreaNum(center); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( flag->r.mins, target.mins ); + VectorCopy( flag->r.maxs, target.maxs ); + VectorCopy( flag->r.currentOrigin, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, target.areanum, bs->tfl); + if (t) { + // if we are already heading there, continue + if (bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_TouchTarget) return qfalse; + bs->target_goal = target; + BotFindSparseDefendArea(bs, &bs->target_goal); + AIEnter_MP_DefendTarget(bs); + return qtrue; + } + }*/ + } + // keep sending some troops to the flag carrier, so we have the most direct route covered + flag = BotGetEnemyFlagCarrier( bs ); + if ( flag && ( VectorDistanceSquared( flag->r.currentOrigin, bs->eye ) < ( 1024 * 1024 ) || trap_InPVS( flag->r.currentOrigin, bs->eye ) || BotNumTeamMatesWithTarget( bs, flag->s.number, NULL, 0 ) < 1 ) ) { + // HACK, err... lets just take a snapshot of where they are now, and go down there + bs->enemyvisible_time = trap_AAS_Time(); + //update the reachability area and origin if possible + areanum = BotPointAreaNum( flag->s.number, flag->r.currentOrigin ); + if ( areanum && trap_AAS_AreaReachability( areanum ) && trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ) ) { + VectorCopy( flag->r.currentOrigin, bs->lastenemyorigin ); + bs->lastenemyareanum = areanum; + // + if ( BotGoalForEntity( bs, flag->s.number, &target, BGU_HIGH ) ) { + bs->enemy = flag->s.number; + AIEnter_MP_Battle_Chase( bs ); + bs->chase_time += 20.0; + return qtrue; + } + } + } + } + } else // if we are close to the flag, get it! + { + // is the enemy flag at base? + if ( ( ( bs->leader == -1 ) || !trap_InPVS( bs->origin, g_entities[bs->leader].r.currentOrigin ) || ( VectorDistanceSquared( bs->origin, g_entities[bs->leader].r.currentOrigin ) > ( MAX_BOTLEADER_DIST * MAX_BOTLEADER_DIST ) ) ) + && ( !BotCarryingFlag( bs->client ) ) + && ( BotFlagAtBase( level.attackingTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS, &flag ) == qtrue ) ) { + qboolean getflag = qtrue; + // + // check special circumstances in which we should not go for the flag + if ( bs->ainode == AINode_MP_MedicRevive || bs->ainode == AINode_MP_MedicGiveHealth ) { + // if we are close to them, continue + if ( trap_InPVS( bs->origin, g_entities[bs->target_goal.entitynum].r.currentOrigin ) && + VectorDistanceSquared( bs->origin, g_entities[bs->target_goal.entitynum].r.currentOrigin ) < ( 512 * 512 ) ) { + getflag = qfalse; + } + } + // + if ( getflag ) { + // GO GET IT + // do we have a route to the enemy flag? + if ( BotGoalForEntity( bs, flag->s.number, &target, BGU_MEDIUM ) ) { + target.flags = GFL_NOSLOWAPPROACH; + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( t ) { + if ( trap_InPVS( bs->origin, target.origin ) ) { + t = t / 2 - 300; + } + if ( t <= 0 ) { + t = 1; + } + if ( t < 700 ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } + } + } + } + } + + } + } + + // check for medic trying to give us health + // NOTE: check that we havent just spawned, since this can cause congestion at the start + if ( ( g_entities[bs->client].client->respawnTime < level.time - 12000 ) && g_entities[bs->client].awaitingHelpTime > level.time + 200 ) { + if ( bs->enemy < 0 && bs->stand_time < trap_AAS_Time() && !BotCarryingFlag( bs->client ) ) { + AIEnter_MP_Stand( bs ); + bs->stand_time = trap_AAS_Time() + 1.0; + return qtrue; + } + } + + // if we are a medic, we should always revive nearby players + if ( BotClass_MedicCheckRevives( bs, 300, &target, qtrue ) ) { + if ( !BotCarryingFlag( bs->client ) || ( ( bs->enemy == -1 ) && ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ) > 600 ) ) ) { + if ( target.entitynum == bs->target_goal.entitynum && bs->ainode != AINode_MP_MedicRevive ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_MedicRevive( bs ); + return qtrue; + } + } + + // PANZER TARGETS + if ( ( COM_BitCheck( bs->cur_ps.weapons, WP_PANZERFAUST ) ) ) { + if ( ( BotWeaponWantScale( bs, WP_PANZERFAUST ) > 0 ) ) { + team_t team = bs->sess.sessionTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + // shoot towards gun emplacements after spawning in + i = 0; + trav = NULL; + numList = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_MG42 ) ) ) { + if ( ( trav->aiInactive && ( 1 << team ) ) ) { + continue; + } + if ( trav->s.powerups != STATE_DEFAULT ) { + continue; + } + if ( trav->health <= 0 ) { + continue; + } + // make sure we can see it + VectorCopy( trav->r.currentOrigin, center ); + center[2] += 12; + if ( !trap_InPVS( bs->eye, center ) ) { + continue; + } + trap_Trace( &tr, bs->eye, NULL, NULL, center, bs->client, MASK_MISSILESHOT ); + if ( tr.fraction < 1.0 && tr.entityNum != trav->s.number ) { + continue; + } + // + list[numList++] = trav->s.number; + i++; + } + // + if ( i ) { + // pick a random mg42 + t = rand() % i; + trav = BotGetEntity( list[t] ); + // trav is the mg42 + trav->missionLevel = level.time + 10000; + // + target.entitynum = trav->s.number; + VectorCopy( trav->r.currentOrigin, center ); + center[2] += 12; + VectorCopy( center, target.origin ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + bs->target_goal = target; + AIEnter_MP_PanzerTarget( bs ); + return qtrue; + } + } + } + } + + // defense engineers should look for dynamite placed nearby objectives to defuse + { + int list[10], numList; + // + if ( ( numList = BotGetTargetDynamite( list, 10, NULL ) ) ) { + for ( i = 0; i < numList; i++ ) { + trav = BotGetEntity( list[i] ); + if ( !trav ) { + continue; + } + + // Gordon: WTH is this for? + if ( bs->sess.playerType != PC_ENGINEER ) { + if ( BotCanSnipe( bs, qtrue ) ) { + if ( level.explosiveTargets[bs->sess.sessionTeam == TEAM_AXIS ? 0 : 1] != BotGetTargetExplosives( bs->sess.sessionTeam, NULL, 0, qfalse ) + || BotNumTeamMatesWithTarget( bs, list[i], NULL, 0 ) >= (int)( floor( 0.3 * numTeammates / numList ) ) ) { + continue; + } + } + } + + // if this dynamite has been laid by our team, and we are an engineer, no need to defend it + if ( bs->sess.playerType == PC_ENGINEER && ( ( trav->s.teamNum % 4 ) == bs->sess.sessionTeam ) ) { + continue; + } + + //if (trav->missionLevel > level.time) continue; + if ( BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) > (int)floor( (float)( numTeammates / numList ) ) ) { + continue; + } + VectorAdd( trav->r.absmin, trav->r.absmax, center ); + VectorScale( center, 0.5, center ); + if ( ( areanum = BotPointAreaNum( trav->s.number, center ) ) ) { + //if (areanum = BotReachableBBoxAreaNum( bs, trav->r.absmin, trav->r.absmax )) { + if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ) ) { + // make this our goal + BotClearGoal( &target ); + target.entitynum = trav->s.number; + target.areanum = areanum; + VectorCopy( trav->r.mins, target.mins ); + VectorCopy( trav->r.maxs, target.maxs ); + VectorCopy( center, target.origin ); + // + // if we are an engineer, and this is hostile dynamite, we should defuse it, otherwise defend it + if ( bs->sess.playerType == PC_ENGINEER && ( ( trav->s.teamNum % 4 ) != bs->sess.sessionTeam ) ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + // if we are already heading there, continue + if ( bs->target_goal.entitynum && bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_DisarmDynamite ) { + return qfalse; + } + bs->target_goal = target; + trav->missionLevel = level.time + 5000; + AIEnter_MP_DisarmDynamite( bs ); + return qtrue; + } + // only "defend" the dynamite if they are yet to break through any of the defenses + } else if ( ( BotNumTeamMatesWithTargetAndCloser( bs, target.entitynum, target.areanum, NULL, 0, -1 ) < (int)( ceil( 0.3 * numTeammates / numList ) ) ) ) { + // if we are already heading there, continue + if ( bs->target_goal.entitynum && bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + // just "defend" it + bs->target_goal = target; + BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + } + } + } + // + // look for healing/giving ammo to others + if ( BotMP_CheckClassActions( bs ) ) { + return qtrue; + } + // + bs->ignore_specialgoal_time = oldIgnoreTime; + return qfalse; +} + +int botgoalPriorities_Standard[BFG_NUMBOTGOALTYPES] = { + 0, // BFG_FOLLOW_LEADER + -1, // BFG_CONSTRUCT + 2, // BFG_TRIGGER + -1, // BFG_DESTRUCTION_EXPLOSIVE + -1, // BFG_DESTRUCTION_BUILDING + -1, // BFG_MG42_REPAIR + -1, // BFG_MINE + 1, // BFG_ATTRACTOR + -1, // BFG_SNIPERSPOT + 2, // BFG_MG42 + -1, // BFG_SCANFORMINES + -1, // BFG_DESTRUCTION_SATCHEL +}; + +int botgoalPriorities_Engineer[BFG_NUMBOTGOALTYPES] = { + 1, // BFG_FOLLOW_LEADER + 5, // BFG_CONSTRUCT + 3, // BFG_TRIGGER + 5, // BFG_DESTRUCTION_EXPLOSIVE + 5, // BFG_DESTRUCTION_BUILDING + 4, // BFG_MG42_REPAIR + 4, // BFG_MINE + 2, // BFG_ATTRACTOR + -1, // BFG_SNIPERSPOT + 0, // BFG_MG42 + -1, // BFG_SCANFORMINES + -1, // BFG_DESTRUCTION_SATCHEL +}; + +int botgoalPriorities_CovertOps[BFG_NUMBOTGOALTYPES] = { + 1, // BFG_FOLLOW_LEADER + -1, // BFG_CONSTRUCT + 3, // BFG_TRIGGER + -1, // BFG_DESTRUCTION_EXPLOSIVE + -1, // BFG_DESTRUCTION_BUILDING + -1, // BFG_MG42_REPAIR + -1, // BFG_MINE + 2, // BFG_ATTRACTOR + 4, // BFG_SNIPERSPOT + 0, // BFG_MG42 + 4, // BFG_SCANFORMINES + 4, // BFG_DESTRUCTION_SATCHEL +}; + +int *botgoalPriorities_Class[NUM_PLAYER_CLASSES] = { + botgoalPriorities_Standard, // PC_SOLDIER + botgoalPriorities_Standard, // PC_MEDIC + botgoalPriorities_Engineer, // PC_ENGINEER + botgoalPriorities_Standard, // PC_FIELDOPS + botgoalPriorities_CovertOps // PC_COVERTOPS +}; + +int botgoalMaxCloser[BFG_NUMBOTGOALTYPES] = { + 2, // BFG_FOLLOW_LEADER + 1, // BFG_CONSTRUCT + 2, // BFG_TRIGGER + 1, // BFG_DESTRUCTION_EXPLOSIVE + 1, // BFG_DESTRUCTION_BUILDING + 0, // BFG_MG42_REPAIR + 0, // BFG_MINE + 2, // BFG_ATTRACTOR + 0, // BFG_SNIPERSPOT + 0, // BFG_MG42 + 0, // BFG_SCANFORMINES + 0, // BFG_DESTRUCTION_SATCHEL +}; + +typedef struct botgoalFind_s { + gentity_t* ent; + botgoalFindType_t type; + qboolean ignore; + int priority; + int defaultPriority; + int numPlayers; +} botgoalFind_t; + +#define MAX_FIND_BOTGOALS 32 +#define BFG_CHECKMAX if ( goalNum >= maxGoals ) { return maxGoals; } +#define BFG_CHECKDISABLED( ent,bs ) ent->aiInactive & ( 1 << bs->sess.sessionTeam ) + +int BotMP_FindGoal_BuildGoalList( bot_state_t* bs, botgoalFind_t* pGoals, int maxGoals ) { + int goalNum = 0; + int leader, numTargets; + int k, c; + int tlist[10]; + gentity_t *ent, *trav; + + BFG_CHECKMAX; + + leader = BotGetLeader( bs, ( bs->sess.playerType == PC_SOLDIER ) ); + if ( leader != -1 && g_entities[leader].r.svFlags & SVF_BOT ) { + pGoals[goalNum].type = BFG_FOLLOW_LEADER; + pGoals[goalNum].ent = &g_entities[leader]; + pGoals[goalNum].priority = g_entities[leader].goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + // trigger_multiples + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_TRIGGER_MULTIPLE ) ) ) { + // is it disabled? + if ( BFG_CHECKDISABLED( trav, bs ) ) { + continue; + } + + // is it active? + if ( !trav->r.linked || ( trav->nextthink > level.time + 1000 ) ) { + continue; // if it's thinking, then it's not active (doesnt respond to touch) + } + + pGoals[goalNum].type = BFG_TRIGGER; + pGoals[goalNum].ent = trav; + pGoals[goalNum].priority = trav->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + if ( bs->sess.playerType == PC_ENGINEER ) { + // Constructibles + // find a constructible + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + // is it disabled? + if ( BFG_CHECKDISABLED( trav, bs ) ) { + continue; + } + + // find the constructible + ent = G_ConstructionForTeam( trav, bs->sess.sessionTeam ); + if ( !ent ) { + continue; + } + + if ( !BotIsConstructible( bs->sess.sessionTeam, trav->s.number ) ) { + continue; + } + + pGoals[goalNum].type = BFG_CONSTRUCT; + pGoals[goalNum].ent = trav; + pGoals[goalNum].priority = trav->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + // check for dynamite + // look for things to blow up + if ( BotWeaponCharged( bs, WP_DYNAMITE ) ) { + numTargets = BotGetTargetExplosives( bs->sess.sessionTeam, tlist, 10, qfalse ); + for ( k = 0; k < numTargets; k++ ) { + // see if this is a worthy goal + ent = &g_entities[tlist[k]]; + + if ( ent->s.eType == ET_OID_TRIGGER ) { + pGoals[goalNum].type = BFG_DESTRUCTION_EXPLOSIVE; + if ( ent->target_ent ) { + pGoals[goalNum].priority = ent->target_ent->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + } else { + // Gordon: this shouldn't happen really.... + pGoals[goalNum].priority = ent->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + } + } else { + pGoals[goalNum].type = BFG_DESTRUCTION_BUILDING; + pGoals[goalNum].priority = ent->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + } + pGoals[goalNum].ent = ent; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + } + + // look for a broken MG42 that isnt already being fixed + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_MG42 ) ) ) { + // is it disabled? + if ( BFG_CHECKDISABLED( trav, bs ) ) { + continue; + } + + if ( trav->health <= 0 ) { + if ( trav->melee ) { + ent = trav->melee; + if ( ( ent->botIgnoreTime < level.time ) ) { + pGoals[goalNum].type = BFG_MG42_REPAIR; + pGoals[goalNum].ent = ent; + pGoals[goalNum].priority = trav->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + } + } + } + + // PLANT LAND MINES + if ( G_CountTeamLandmines( bs->sess.sessionTeam ) < MAX_TEAM_LANDMINES ) { + // look for a land mine area that isn't full + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINE_AREA ) ) ) { + // is it disabled? + if ( BFG_CHECKDISABLED( trav, bs ) ) { + continue; + } + // is it for us? + if ( ( trav->spawnflags & 1 ) && ( bs->sess.sessionTeam != TEAM_AXIS ) ) { + continue; + } else if ( ( trav->spawnflags & 2 ) && ( bs->sess.sessionTeam != TEAM_ALLIES ) ) { + continue; + } + + // has it got enough landmines? + if ( trav->count2 >= trav->count ) { + continue; + } + + pGoals[goalNum].type = BFG_MINE; + pGoals[goalNum].ent = trav; + pGoals[goalNum].priority = trav->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + } + } + + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_ATTRACTOR ) ) ) { + if ( BFG_CHECKDISABLED( trav, bs ) ) { + continue; + } + + pGoals[goalNum].type = BFG_ATTRACTOR; + pGoals[goalNum].ent = trav; + pGoals[goalNum].priority = trav->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + // SNIPER SPOT + if ( BotCanSnipe( bs, qtrue ) ) { + c = BotBestSniperSpot( bs ); + if ( c != -1 ) { + pGoals[goalNum].type = BFG_SNIPERSPOT; + pGoals[goalNum].ent = &g_entities[c]; + pGoals[goalNum].priority = g_entities[c].goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + } + + if ( bs->sess.playerType == PC_COVERTOPS ) { + c = BotBestLandmineSpotingSpot( bs ); + if ( c != -1 ) { + pGoals[goalNum].type = BFG_SCANFORMINES; + pGoals[goalNum].ent = &g_entities[c]; + pGoals[goalNum].priority = g_entities[c].goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + numTargets = BotGetTargetsForSatchelCharge( bs->sess.sessionTeam, tlist, 10, qfalse ); + for ( k = 0; k < numTargets; k++ ) { + // see if this is a worthy goal + ent = &g_entities[tlist[k]]; + + pGoals[goalNum].type = BFG_DESTRUCTION_SATCHEL; + pGoals[goalNum].priority = ent->goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].ent = ent; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + } + + // MG42 + c = BotBestMG42Spot( bs, qfalse ); + if ( c != -1 ) { + pGoals[goalNum].type = BFG_MG42; + pGoals[goalNum].ent = &g_entities[c]; + pGoals[goalNum].priority = g_entities[c].goalPriority[bs->sess.sessionTeam - TEAM_AXIS]; + pGoals[goalNum].defaultPriority = botgoalPriorities_Class[bs->sess.playerType][pGoals[goalNum].type]; + + goalNum++; + + BFG_CHECKMAX; + } + + return goalNum; +} + +int QDECL BotMP_FindGoals_Sort_Standard( const void *a, const void *b ) { + botgoalFind_t* bg1 = (botgoalFind_t*)a; + botgoalFind_t* bg2 = (botgoalFind_t*)b; + + if ( botgoalPriorities_Standard[bg1->type] > botgoalPriorities_Standard[bg2->type] ) { + return -1; + } else if ( botgoalPriorities_Standard[bg2->type] > botgoalPriorities_Standard[bg1->type] ) { + return 1; + } + + if ( bg1->priority > bg2->priority ) { + return -1; + } else if ( bg2->priority > bg1->priority ) { + return 1; + } + + return 0; +} + +int QDECL BotMP_FindGoals_Sort_Engineer( const void *a, const void *b ) { + botgoalFind_t* bg1 = (botgoalFind_t*)a; + botgoalFind_t* bg2 = (botgoalFind_t*)b; + + if ( botgoalPriorities_Engineer[bg1->type] > botgoalPriorities_Engineer[bg2->type] ) { + return -1; + } else if ( botgoalPriorities_Engineer[bg2->type] > botgoalPriorities_Engineer[bg1->type] ) { + return 1; + } + + if ( bg1->priority > bg2->priority ) { + return -1; + } else if ( bg2->priority > bg1->priority ) { + return 1; + } + + return 0; +} + +int QDECL BotMP_FindGoals_Sort_CovertOps( const void *a, const void *b ) { + botgoalFind_t* bg1 = (botgoalFind_t*)a; + botgoalFind_t* bg2 = (botgoalFind_t*)b; + + if ( botgoalPriorities_CovertOps[bg1->type] > botgoalPriorities_CovertOps[bg2->type] ) { + return -1; + } else if ( botgoalPriorities_CovertOps[bg2->type] > botgoalPriorities_CovertOps[bg1->type] ) { + return 1; + } + + if ( bg1->priority > bg2->priority ) { + return -1; + } else if ( bg2->priority > bg1->priority ) { + return 1; + } + + return 0; +} + +typedef int QDECL sortFunc ( const void *a, const void *b ); + +sortFunc* botmp_sortFuncs[NUM_PLAYER_CLASSES] = { + BotMP_FindGoals_Sort_Standard, // PC_SOLDIER + BotMP_FindGoals_Sort_Standard, // PC_MEDIC + BotMP_FindGoals_Sort_Engineer, // PC_ENGINEER + BotMP_FindGoals_Sort_Standard, // PC_FIELDOPS + BotMP_FindGoals_Sort_CovertOps // PC_COVERTOPS +}; + + +int BotMP_FindGoal_ClassForGoalType( botgoalFindType_t type ) { + switch ( type ) { + case BFG_CONSTRUCT: + case BFG_DESTRUCTION_EXPLOSIVE: + case BFG_DESTRUCTION_BUILDING: + case BFG_MG42_REPAIR: + case BFG_MINE: + return PC_ENGINEER; + + case BFG_SNIPERSPOT: + case BFG_SCANFORMINES: + case BFG_DESTRUCTION_SATCHEL: + return PC_COVERTOPS; + default: + break; + } + + return -1; +} + +#define BFG_RETURN_SUCCESS return BPG_NEWGOAL +#define BFG_RETURN_FAILURE return BPG_FAILED +#define BFG_RETURN_ALREADY return BPG_ALREADYDOING +#define BFG_RETURN_FAILURE_AND_IGNORE bg->ignore = qtrue; BFG_RETURN_FAILURE +#define BFG_GOAL_ENTITYNUM bg->ent - g_entities + +typedef enum botMPpg_e { + BPG_FAILED, + BPG_NEWGOAL, + BPG_ALREADYDOING, +} botMPpg_t; + +botMPpg_t BotMP_FindGoal_ProcessGoal( bot_state_t* bs, botgoalFind_t* bg, bot_goal_t* target_goal ) { + int t, i; + vec3_t mins, maxs; + vec3_t center, brushPos, vec, end; + vec_t dist; + int list[32]; + int numList; + trace_t tr; + weapon_t weap; + + switch ( bg->type ) { + case BFG_FOLLOW_LEADER: + // we have a leader, follow them + if ( BotGoalForEntity( bs, BFG_GOAL_ENTITYNUM, target_goal, BGU_LOW ) ) { + target_goal->flags = GFL_LEADER; + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( t && t <= MAX_BOTLEADER_TRAVEL ) { + BotFindSparseDefendArea( bs, target_goal, qtrue ); + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_DefendTarget ) { + BFG_RETURN_ALREADY; + } + BFG_RETURN_SUCCESS; + } + } + + bs->leader = -1; + BFG_RETURN_FAILURE_AND_IGNORE; + + case BFG_CONSTRUCT: + if ( !BotGetReachableEntityArea( bs, BFG_GOAL_ENTITYNUM, target_goal ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; // not reachable + } + + if ( bs->target_goal.entitynum == BFG_GOAL_ENTITYNUM && bs->ainode == AINode_MP_ConstructibleTarget ) { + BFG_RETURN_ALREADY; // already going for it + } + + // Gordon: if this is a new goal, check pliers are half-full before heading off (optimist ;)) + if ( bs->cur_ps.classWeaponTime > ( level.time - ( level.engineerChargeTime[bs->sess.sessionTeam - 1] * 0.5f ) ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( !t ) { + BFG_RETURN_FAILURE_AND_IGNORE; // failed for whatever reason + } + BFG_RETURN_SUCCESS; + + + + case BFG_TRIGGER: + if ( !BotGetReachableEntityArea( bs, BFG_GOAL_ENTITYNUM, target_goal ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; // not reachable + } + + if ( !BotGoalWithinMovementAutonomy( bs, target_goal, BGU_LOW ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; // outside autonomy range + } + + // if the current destination is not touching the brush, abort + VectorAdd( target_goal->origin, bs->cur_ps.mins, mins ); + VectorAdd( target_goal->origin, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, bg->ent ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; + } + + // get the travel time + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( !t ) { + BFG_RETURN_FAILURE_AND_IGNORE; + } + + // is it for us only? + if ( ( ( bg->ent->spawnflags & 1 ) && ( bs->sess.sessionTeam == TEAM_AXIS ) ) || ( ( bg->ent->spawnflags & 2 ) && ( bs->sess.sessionTeam == TEAM_ALLIES ) ) ) { + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_TouchTarget ) { + BFG_RETURN_ALREADY; + } + BFG_RETURN_SUCCESS; + } else { // defend it + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_DefendTarget ) { + BFG_RETURN_ALREADY; + } + BFG_RETURN_SUCCESS; + } + + case BFG_DESTRUCTION_EXPLOSIVE: + // calc center + VectorAdd( bg->ent->r.absmin, bg->ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + + // find the best goal area + numList = trap_AAS_BBoxAreas( bg->ent->r.absmin, bg->ent->r.absmax, list, 32 ); + if ( numList ) { + int oldestTime = -1; + int bestDist = -1; + int oldest = 0; + + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 && t < 400 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + + if ( oldestTime > 0 ) { + // now trace from this area towards the func_explosive + trap_AAS_AreaCenter( oldest, center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, oldest, bs->tfl ); + if ( t ) { + bg->ent->lastHintCheckTime = level.time + 5000; + // use this as the goal origin + VectorCopy( center, target_goal->origin ); + VectorCopy( bs->cur_ps.mins, target_goal->mins ); + VectorCopy( bs->cur_ps.maxs, target_goal->maxs ); + target_goal->areanum = oldest; + target_goal->entitynum = BFG_GOAL_ENTITYNUM; + + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_DynamiteTarget ) { + BFG_RETURN_ALREADY; + } + BFG_RETURN_SUCCESS; + } + } + } + BFG_RETURN_FAILURE_AND_IGNORE; + + + + case BFG_DESTRUCTION_BUILDING: + case BFG_DESTRUCTION_SATCHEL: + if ( bg->type == BFG_DESTRUCTION_BUILDING ) { + weap = WP_DYNAMITE; + } else { + weap = WP_SATCHEL; + } + + VectorAdd( bg->ent->r.absmin, bg->ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + + VectorCopy( bg->ent->r.absmin, mins ); + VectorCopy( bg->ent->r.absmax, maxs ); + for ( i = 0; i < 2; i++ ) { + mins[i] -= G_GetWeaponDamage( weap ) * 0.2f; + maxs[i] += G_GetWeaponDamage( weap ) * 0.2f; + } + + // find the best goal area + numList = trap_AAS_BBoxAreas( mins, maxs, list, 32 ); + if ( numList ) { + int oldestTime = -1; + int bestDist = -1; + int oldest = 0; + + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + + // RF, make sure this is within range + trap_AAS_AreaCenter( list[i], center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + VectorSubtract( center, brushPos, vec ); + + G_AdjustedDamageVec( bg->ent, center, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( G_GetWeaponDamage( weap ) ) ) && CanDamage( bg->ent, center ) ) { + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t ) { + oldestTime = t; + oldest = list[i]; + bestDist = t; + } + } + } + + if ( oldestTime != -1 ) { + // now trace from this area towards the func_explosive + trap_AAS_AreaCenter( oldest, center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, oldest, bs->tfl ); + if ( t ) { + bg->ent->lastHintCheckTime = level.time + 5000; + // use this as the goal origin + VectorCopy( center, target_goal->origin ); + VectorCopy( bs->cur_ps.mins, target_goal->mins ); + VectorCopy( bs->cur_ps.maxs, target_goal->maxs ); + target_goal->areanum = oldest; + target_goal->entitynum = BFG_GOAL_ENTITYNUM; + + if ( BotGoalWithinMovementAutonomy( bs, target_goal, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == target_goal->entitynum ) { + if ( bs->ainode == AINode_MP_DynamiteTarget ) { + BFG_RETURN_ALREADY; + } + // Gordon: FIXME: insert satchel node here +/* if( bs->ainode == AINode_MP_DynamiteTarget ) { + BFG_RETURN_ALREADY; + }*/ + } + BFG_RETURN_SUCCESS; + } + BFG_RETURN_SUCCESS; + } + } + } + BFG_RETURN_FAILURE_AND_IGNORE; + + + case BFG_MG42_REPAIR: + if ( BotGoalForEntity( bs, BFG_GOAL_ENTITYNUM, target_goal, BGU_LOW ) ) { + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( t < 500 ) { + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_FixMG42 ) { + BFG_RETURN_ALREADY; + } + + // go fix it!! + BFG_RETURN_SUCCESS; + } + } + BFG_RETURN_FAILURE_AND_IGNORE; + + + case BFG_MINE: + if ( !BotGoalForEntity( bs, BFG_GOAL_ENTITYNUM, target_goal, BGU_LOW ) ) { + BFG_RETURN_FAILURE_AND_IGNORE; + } + + if ( bs->target_goal.entitynum == BFG_GOAL_ENTITYNUM && bs->ainode == AINode_MP_PlantMine ) { + BFG_RETURN_ALREADY; + } + + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( !t ) { + BFG_RETURN_FAILURE_AND_IGNORE; + } + + // find a spot inside the area to plant the mine + i = 0; + do { + VectorAdd( BotGetOrigin( BFG_GOAL_ENTITYNUM ), bg->ent->pos3, vec ); + vec[0] += crandom() * bg->ent->r.absmax[0] * 0.25; + vec[1] += crandom() * bg->ent->r.absmax[1] * 0.25; + + target_goal->areanum = BotPointAreaNum( bs->client, vec ); + VectorCopy( vec, target_goal->origin ); + } while ( ( ++i < 5 ) && ( ( target_goal->areanum < 0 ) || !PointInBounds( vec, bg->ent->r.absmin, bg->ent->r.absmax ) ) ); + + if ( i < 5 ) { // we found a valid spot + BFG_RETURN_SUCCESS; + } + BFG_RETURN_FAILURE_AND_IGNORE; + + case BFG_ATTRACTOR: + if ( BotGoalForEntity( bs, BFG_GOAL_ENTITYNUM, target_goal, BGU_MAXIMUM ) ) { + // get the distance, halve it if there is noone one going for this attractor + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target_goal->areanum, bs->tfl ); + if ( t ) { + BotFindSparseDefendArea( bs, target_goal, qtrue ); + if ( bs->target_goal.entitynum == target_goal->entitynum && bs->ainode == AINode_MP_DefendTarget ) { + BFG_RETURN_ALREADY; + } + BFG_RETURN_SUCCESS; + } + } + BFG_RETURN_FAILURE_AND_IGNORE; + + case BFG_SNIPERSPOT: + if ( bs->target_goal.entitynum == BFG_GOAL_ENTITYNUM && bs->ainode == AINode_MP_SniperSpot ) { + BFG_RETURN_ALREADY; + } + + target_goal->entitynum = BFG_GOAL_ENTITYNUM; + VectorCopy( bg->ent->s.origin, target_goal->origin ); + target_goal->areanum = BotPointAreaNum( -1, target_goal->origin ); + VectorCopy( bs->cur_ps.mins, target_goal->mins ); + VectorCopy( bs->cur_ps.maxs, target_goal->maxs ); + + BFG_RETURN_SUCCESS; + + case BFG_SCANFORMINES: + if ( bs->target_goal.entitynum == BFG_GOAL_ENTITYNUM && bs->ainode == AINode_MP_ScanForLandmines ) { + BFG_RETURN_ALREADY; + } + + target_goal->entitynum = BFG_GOAL_ENTITYNUM; + VectorCopy( bg->ent->s.origin, target_goal->origin ); + target_goal->areanum = BotPointAreaNum( -1, target_goal->origin ); + VectorCopy( bs->cur_ps.mins, target_goal->mins ); + VectorCopy( bs->cur_ps.maxs, target_goal->maxs ); + + BFG_RETURN_SUCCESS; + + case BFG_MG42: + if ( ( bs->target_goal.entitynum == BFG_GOAL_ENTITYNUM ) && ( bs->ainode == AINode_MP_MG42Mount || bs->ainode == AINode_MP_MG42Scan ) ) { + BFG_RETURN_ALREADY; + } + + target_goal->entitynum = BFG_GOAL_ENTITYNUM; + VectorCopy( bg->ent->s.origin, target_goal->origin ); + target_goal->areanum = BotPointAreaNum( -1, target_goal->origin ); + VectorCopy( bs->cur_ps.mins, target_goal->mins ); + VectorCopy( bs->cur_ps.maxs, target_goal->maxs ); + + BFG_RETURN_SUCCESS; + default: + break; + } + + BFG_RETURN_FAILURE; +} + +void BotMP_FindGoal_PostProcessGoal( bot_state_t* bs, botgoalFind_t* bg, bot_goal_t* goal ) { + bs->target_goal = *goal; + + switch ( bg->type ) { + case BFG_FOLLOW_LEADER: + bs->leader = goal->entitynum; + AIEnter_MP_DefendTarget( bs ); + break; + case BFG_CONSTRUCT: + AIEnter_MP_ConstructibleTarget( bs ); + break; + case BFG_TRIGGER: + if ( ( ( bg->ent->spawnflags & 1 ) && ( bs->sess.sessionTeam == TEAM_AXIS ) ) || ( ( bg->ent->spawnflags & 2 ) && ( bs->sess.sessionTeam == TEAM_ALLIES ) ) ) { + AIEnter_MP_TouchTarget( bs ); + } else { + AIEnter_MP_DefendTarget( bs ); + } + break; + case BFG_DESTRUCTION_EXPLOSIVE: + case BFG_DESTRUCTION_BUILDING: + AIEnter_MP_DynamiteTarget( bs ); + break; + case BFG_DESTRUCTION_SATCHEL: + AIEnter_MP_SatchelChargeTarget( bs ); + break; + case BFG_MG42_REPAIR: + AIEnter_MP_FixMG42( bs ); + break; + case BFG_MINE: + AIEnter_MP_PlantMine( bs ); + break; + case BFG_ATTRACTOR: + AIEnter_MP_DefendTarget( bs ); + break; + case BFG_SNIPERSPOT: + AIEnter_MP_SniperSpot( bs ); + break; + case BFG_SCANFORMINES: + AIEnter_MP_ScanForLandmines( bs ); + break; + case BFG_MG42: + AIEnter_MP_MG42Mount( bs ); + break; + default: + break; + } +} + +qboolean BotMP_AlreadyDoing_FastOut( bot_state_t *bs, botgoalFind_t *bg ) { + // return qtrue if the given goal, which we are already doing, is ok to continue doing, without looking for other goals + + switch ( bg->type ) { + case BFG_SNIPERSPOT: + case BFG_MG42: + case BFG_SCANFORMINES: + return qtrue; + + case BFG_FOLLOW_LEADER: + case BFG_CONSTRUCT: + case BFG_TRIGGER: + case BFG_DESTRUCTION_EXPLOSIVE: + case BFG_DESTRUCTION_BUILDING: + case BFG_DESTRUCTION_SATCHEL: + case BFG_MG42_REPAIR: + case BFG_MINE: + case BFG_ATTRACTOR: + default: + return qfalse; + } +} + +qboolean BotMP_FindGoal_New( bot_state_t* bs ) { + int numGoals, i, numCloser; + botgoalFind_t findGoals[MAX_FIND_BOTGOALS]; + botMPpg_t res, bestGoal_result = 0; + bot_goal_t goal, bestGoal; + int bestGoal_numCloser, bestGoal_InList; + +#ifdef _DEBUG +// int t = trap_Milliseconds(); +#endif // _DEBUG + + if ( bs->last_findspecialgoals > level.time - 1600 ) { + return qfalse; + } + bs->last_findspecialgoals = level.time + rand() % 400; + + if ( bs->ignore_specialgoal_time > level.time ) { + return qfalse; + } + + numGoals = BotMP_FindGoal_BuildGoalList( bs, findGoals, MAX_FIND_BOTGOALS ); + if ( !numGoals ) { + // Gordon: didnt find any goals :( +#ifdef _DEBUG +/* if(bot_profile.integer == 1) { + t = trap_Milliseconds() - t; + G_Printf( "Findgoal: %s: %i\n", BG_ClassnameForNumber( bs->sess.playerType ), t ); + }*/ +#endif // _DEBUG + return qfalse; + } + + for ( i = 0; i < numGoals; i++ ) { + if ( findGoals[i].defaultPriority == -1 ) { + findGoals[i].ignore = qtrue; + } else { + findGoals[i].ignore = qfalse; + } + } + + qsort( findGoals, numGoals, sizeof( botgoalFind_t ), botmp_sortFuncs[bs->cur_ps.stats[STAT_PLAYER_CLASS]] ); + + bestGoal_numCloser = -1; + bestGoal_InList = -1; + + for ( i = 0; i < numGoals; i++ ) { + if ( findGoals[i].ignore ) { + continue; + } + +#ifdef _DEBUG +/* if(bot_profile.integer == 1) { + G_Printf( "Processing goaltype %i\n", findGoals[i].type ); + }*/ +#endif // _DEBUG + res = BotMP_FindGoal_ProcessGoal( bs, &findGoals[i], &goal ); + switch ( res ) { + case BPG_FAILED: + continue; + case BPG_ALREADYDOING: // we should check for range also if we are already doing it, otherwise they may all get stuck at the same goal + if ( BotMP_AlreadyDoing_FastOut( bs, &findGoals[i] ) ) { +#ifdef _DEBUG +/* if(bot_profile.integer == 1) { + t = trap_Milliseconds() - t; + G_Printf( "Findgoal: %s: %i\n", BG_ClassnameForNumber( bs->sess.playerType ), t ); + }*/ +#endif // _DEBUG + return qfalse; + } + // else, drop down so we do a numCloser check + case BPG_NEWGOAL: + // Possible optimization here: record the travel time within each bot's AI node, so we can use that time inside BotNumTeamMatesWithTargetAndCloser() rather than calculating the time for all bots + findGoals[i].numPlayers = numCloser = BotNumTeamMatesWithTargetAndCloser( bs, goal.entitynum, goal.areanum, NULL, 0, BotMP_FindGoal_ClassForGoalType( findGoals[i].type ) ); + if ( bestGoal_numCloser >= 0 ) { + // First, check against maxCloser lookup + if ( numCloser > botgoalMaxCloser[findGoals[i].type] ) { + continue; + } + + // TODO: scale it down according to priority (?) + if ( numCloser > bestGoal_numCloser ) { + continue; + } + + // if this has the same numCloser, but a lower priority, then skip it + if ( numCloser == bestGoal_numCloser ) { + // if the default priority is lower, ignore + if ( findGoals[i].defaultPriority < findGoals[bestGoal_InList].defaultPriority ) { + continue; + } + + // if the default priority is the same, check script + if ( ( findGoals[i].defaultPriority == findGoals[bestGoal_InList].defaultPriority ) && + ( findGoals[i].priority <= findGoals[bestGoal_InList].priority ) ) { + continue; + } + } + } + bestGoal = goal; + bestGoal_result = res; + bestGoal_numCloser = numCloser; + bestGoal_InList = i; + break; + } + } + +#ifdef _DEBUG +/* if(bot_profile.integer == 1) { + t = trap_Milliseconds() - t; + G_Printf( "Findgoal: %s: %i\n", BG_ClassnameForNumber( bs->sess.playerType ), t ); + }*/ +#endif // _DEBUG + + if ( bestGoal_numCloser >= 0 ) { + switch ( bestGoal_result ) { + case BPG_NEWGOAL: + BotMP_FindGoal_PostProcessGoal( bs, &findGoals[bestGoal_InList], &bestGoal ); + return qtrue; + case BPG_ALREADYDOING: + return qfalse; + default: + break; + } + } + + return qfalse; +} + + + +/* +=================== +BotMP_FindGoal +=================== +*/ +qboolean BotMP_FindGoal( bot_state_t *bs ) { + int i, k, t = 0, c; + int oldest = -1, oldestTime = -1, closestTime = -1; + int tlist[10], numTargets; + int teamlist[64], numTeammates; + float dist, bestDist = -1, targetTime = 0, f = 0.0; + gentity_t *ent, *trav; + trace_t tr; + vec3_t center, end, brushPos, vec; + qboolean gotTarget, defendTarget = qfalse; + bot_goal_t bestTarget, secondary; + aas_altroutegoal_t altroutegoals[40]; + vec3_t mins, maxs; + int list[MAX_CLIENTS], numList; + float ourDist, *distances; + int targetPriority = 0, bestPriority = 0, bestGoalPriority = 0; + + if ( bs->last_findspecialgoals > level.time - 1600 ) { + return qfalse; + } + bs->last_findspecialgoals = level.time + rand() % 400; + + if ( bs->ignore_specialgoal_time > level.time ) { + return qfalse; + } + + BotClearGoal( &target ); + BotClearGoal( &secondary ); + numTeammates = BotNumTeamMates( bs, teamlist, 64 ); + gotTarget = qfalse; + + // LEADER + + // + // look for a (better?) leader + bs->leader = BotGetLeader( bs, ( bs->sess.playerType == PC_SOLDIER ) ); + if ( bs->leader > -1 ) { + // we have a leader, follow them + if ( BotGoalForEntity( bs, bs->leader, &secondary, BGU_LOW ) || !( g_entities[bs->leader].r.svFlags & SVF_BOT ) ) { + secondary.flags = GFL_LEADER; + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, secondary.areanum, bs->tfl ); + + if ( !t || t > MAX_BOTLEADER_TRAVEL ) { + bs->leader = -1; + } else { + targetTime = t; + gotTarget = qtrue; + defendTarget = qtrue; + bestTarget = secondary; + } + } else { + bs->leader = -1; + } + } + + // IMPORTANT CONSTRUCTIBLES + if ( bs->sess.playerType == PC_ENGINEER ) { + int pass; + // + for ( pass = 0; pass < 2; pass++ ) { + // find a constructible + trav = NULL; + ent = NULL; + closestTime = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + // find the constructible + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + + // is it not of highest priority? + if ( trav->goalPriority[bs->sess.sessionTeam - 1] < 9 ) { + continue; + } + + ent = trav->target_ent; + if ( !ent ) { + continue; + } + + if ( bs->sess.sessionTeam == TEAM_ALLIES ) { + if ( ent->spawnflags & AXIS_CONSTRUCTIBLE ) { + if ( ent->chain ) { + ent = ent->chain; + } else { + continue; + } + } + } else if ( bs->sess.sessionTeam == TEAM_AXIS ) { + if ( ent->spawnflags & ALLIED_CONSTRUCTIBLE ) { + if ( ent->chain ) { + ent = ent->chain; + } else { + continue; + } + } + } + + if ( ent->s.eType != ET_CONSTRUCTIBLE ) { + continue; + } + + // + if ( !BotIsConstructible( bs->sess.sessionTeam, trav->s.number ) ) { + continue; + } + if ( ( pass == 0 ) && BotNumTeamMatesWithTargetByClass( bs, trav->s.number, NULL, 0, PC_ENGINEER ) ) { + continue; // someone else is going for it + } + if ( !BotGetReachableEntityArea( bs, trav->s.number, &target ) ) { + continue; // not reachable + } + // + // we can build it + // + // if we are already heading for this goal, stay with it, otherwise look only for closest targets + // + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_ConstructibleTarget ) { + return qfalse; + } + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( closestTime && t > closestTime ) { + continue; + } + // + // if our weapon wont be ready in time + //if (bs->cur_ps.classWeaponTime + t*10 < (level.time - level.lieutenantChargeTime[bs->sess.sessionTeam-1])) continue; + // + bestTarget = target; + closestTime = t; + } + // + if ( closestTime ) { + bs->target_goal = bestTarget; + AIEnter_MP_ConstructibleTarget( bs ); + return qtrue; + } + } + } + + // IMPORTANT TRIGGER MULTIPLE + + { + // find a trigger_multiple + trav = NULL; + ent = NULL; + closestTime = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_TRIGGER_MULTIPLE ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + // is it active? + if ( !trav->r.linked || ( trav->nextthink > level.time + 1000 ) ) { + continue; // if it's thinking, then it's not active (doesnt respond to touch) + } + // is it not of highest priority? + if ( trav->goalPriority[bs->sess.sessionTeam - 1] < 9 ) { + continue; + } + // + if ( !BotGetReachableEntityArea( bs, trav->s.number, &target ) ) { + continue; // not reachable + } + if ( !BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + continue; // outside autonomy range + } + // if the current destination is not touching the brush, abort + VectorAdd( target.origin, bs->cur_ps.mins, mins ); + VectorAdd( target.origin, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, trav ) ) { + continue; + } + // + // get the travel time + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( !t ) { + continue; + } + // + targetPriority = 2; + // + // if there are too many going for this goal, then ignore it if we are the furthest away + if ( ( numList = BotNumTeamMatesWithTargetAndCloser( bs, trav->s.number, target.areanum, list, MAX_CLIENTS, -1 ) ) > (int)floor( 0.3 * numTeammates ) ) { + distances = BotSortPlayersByTraveltime( BotGetArea( trav->s.number ), list, numList ); + if ( distances[numList - 1] < (float)t ) { + // we are the furthest + targetPriority = 1; + } + } + // only go for it if our current best goal is of lesser priority + if ( targetPriority < bestPriority ) { + continue; + } + // + // we should touch it + // + if ( ( targetPriority <= bestPriority ) && ( closestTime && t > closestTime ) ) { + continue; + } + // + bestTarget = target; + closestTime = t; + bestPriority = targetPriority; + } + // + if ( closestTime ) { + trav = &g_entities[bestTarget.entitynum]; + // is it for us only? + if ( ( ( trav->spawnflags & 1 ) && ( bs->sess.sessionTeam == TEAM_AXIS ) ) || + ( ( trav->spawnflags & 2 ) && ( bs->sess.sessionTeam == TEAM_ALLIES ) ) ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } else { // defend it + if ( targetPriority > 1 ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } else { + targetTime = closestTime; + gotTarget = -1; + defendTarget = qtrue; + } + } + } + } + + // CONSTRUCTIBLES + + if ( bs->sess.playerType == PC_ENGINEER ) { + int pass; + // + for ( pass = 0; pass < 2; pass++ ) { + // find a constructible + trav = NULL; + ent = NULL; + closestTime = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + // find the constructible + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + + ent = trav->target_ent; + if ( !ent ) { + continue; + } + + if ( bs->sess.sessionTeam == TEAM_ALLIES ) { + if ( ent->spawnflags & AXIS_CONSTRUCTIBLE ) { + if ( ent->chain ) { + ent = ent->chain; + } else { + continue; + } + } + } else if ( bs->sess.sessionTeam == TEAM_AXIS ) { + if ( ent->spawnflags & ALLIED_CONSTRUCTIBLE ) { + if ( ent->chain ) { + ent = ent->chain; + } else { + continue; + } + } + } + + if ( ent->s.eType != ET_CONSTRUCTIBLE ) { + continue; + } + + // + if ( !BotIsConstructible( bs->sess.sessionTeam, trav->s.number ) ) { + continue; + } + if ( BotNumTeamMatesWithTargetByClass( bs, trav->s.number, NULL, 0, PC_ENGINEER ) > pass ) { + continue; // someone else is going for it + } + if ( !BotGetReachableEntityArea( bs, trav->s.number, &target ) ) { + continue; // not reachable + } + if ( bestGoalPriority > trav->goalPriority[bs->sess.sessionTeam - 1] ) { + continue; + } + // + // we can build it + // + // if we are already heading for this goal, stay with it, otherwise look only for closest targets + // + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_ConstructibleTarget ) { + return qfalse; + } + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( bestGoalPriority == trav->goalPriority[bs->sess.sessionTeam - 1] ) { + // only check the distance if the priorities are the same + if ( closestTime && t > closestTime ) { + continue; + } + } + // + // if our weapon wont be ready in time + //if (bs->cur_ps.classWeaponTime + t*10 < (level.time - level.lieutenantChargeTime[bs->sess.sessionTeam-1])) continue; + // + bestTarget = target; + closestTime = t; + bestGoalPriority = trav->goalPriority[bs->sess.sessionTeam - 1]; + } + // + if ( closestTime ) { + bs->target_goal = bestTarget; + AIEnter_MP_ConstructibleTarget( bs ); + return qtrue; + } + } + } + + // check for dynamite + if ( bs->sess.playerType == PC_ENGINEER ) { + // look for things to blow up + numTargets = BotGetTargetExplosives( bs->sess.sessionTeam, tlist, 10, qfalse ); + if ( numTargets ) { + closestTime = -1; + // pick the closest target + for ( k = 0; k < numTargets; k++ ) { + // + oldest = tlist[k]; + // + // see if this is a worthy goal + ent = BotGetEntity( oldest ); + // is it disabled? + // Gordon: forest script was setup to use the entity itself, NOT the toi (toi for building, entity itself for destruction) +// if (ent->aiInactive & (1<sess.sessionTeam)) continue; +// if (ent->parent && (ent->parent->aiInactive & (1<sess.sessionTeam))) continue; + // is the target disabled? +// if (ent->target_ent && (ent->target_ent->aiInactive & (1<sess.sessionTeam))) continue; + // + if ( ( numList = BotNumTeamMatesWithTarget( bs, oldest, list, 10 ) ) ) { + continue; + } + // + if ( ent->s.eType == ET_OID_TRIGGER ) { + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + // find the best goal area + numList = trap_AAS_BBoxAreas( ent->r.absmin, ent->r.absmax, list, 32 ); + if ( numList ) { + oldestTime = -1; + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 && ( !gotTarget || t < 400 ) ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + if ( oldestTime > 0 ) { + // now trace from this area towards the func_explosive + trap_AAS_AreaCenter( oldest, center ); + //trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, brushPos, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + //VectorCopy( tr.endpos, center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + //center[2] += 24; + //oldest = trap_AAS_PointAreaNum(center); + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, oldest, bs->tfl ); + if ( t && ( closestTime < 0 || closestTime > t ) ) { + ent->lastHintCheckTime = level.time + 5000; + // use this as the goal origin + VectorCopy( center, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = ent->s.number; + // + closestTime = t; + } + } + } + } else { + + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + VectorCopy( ent->r.absmin, mins ); + VectorCopy( ent->r.absmax, maxs ); + for ( i = 0; i < 2; i++ ) { + mins[i] -= G_GetWeaponDamage( WP_DYNAMITE ) * 0.25f; + maxs[i] += G_GetWeaponDamage( WP_DYNAMITE ) * 0.25f; + } + // find the best goal area + numList = trap_AAS_BBoxAreas( mins, maxs, list, 32 ); + if ( numList ) { + oldestTime = -1; + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + + // RF, make sure this is within range + trap_AAS_AreaCenter( list[i], center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + VectorSubtract( center, brushPos, vec ); + G_AdjustedDamageVec( ent, center, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( G_GetWeaponDamage( WP_DYNAMITE ) ) ) && CanDamage( ent, center ) ) { + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 && ( !gotTarget || t < 400 ) ) { + if ( bestDist < 0 || t < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = t; + } + } + } + } + if ( oldestTime > 0 ) { + // now trace from this area towards the func_explosive + trap_AAS_AreaCenter( oldest, center ); + //trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, brushPos, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + //VectorCopy( tr.endpos, center ); + VectorCopy( center, end ); + end[2] -= 512; + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, end, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + //center[2] += 24; + //oldest = trap_AAS_PointAreaNum(center); + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, oldest, bs->tfl ); + if ( t && ( closestTime < 0 || closestTime > t ) ) { + ent->lastHintCheckTime = level.time + 5000; + // use this as the goal origin + VectorCopy( center, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = ent->s.number; + // + closestTime = t; + } + } + } + } + } + + // if we have a closestTime, we have a target! + if ( closestTime > 0 ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DynamiteTarget ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_DynamiteTarget( bs ); + return qtrue; + } + } + } + } + + // BROKEN MG42 + if ( bs->sess.playerType == PC_ENGINEER ) { + // look for a broken MG42 that isnt already being fixed + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_MG42 ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + if ( trav->health <= 0 ) { + if ( trav->melee ) { + ent = trav->melee; + if ( ( ent->aiTeam == bs->sess.sessionTeam ) && ( ent->botIgnoreTime < level.time ) && ( BotNumTeamMatesWithTargetByClass( bs, trav->s.number, NULL, -1, PC_ENGINEER ) == 0 ) ) { + // go fix it? + if ( BotGoalForEntity( bs, ent->s.number, &target, BGU_LOW ) ) { + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + // + if ( t < 500 && ( !gotTarget || t < 100 ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_FixMG42 ) { + return qfalse; + } + // go fix it!! + bs->target_goal = target; + AIEnter_MP_FixMG42( bs ); + return qtrue; + } + } + } + } + } + } + } + + // PLANT LAND MINES + if ( bs->sess.playerType == PC_ENGINEER && ( G_CountTeamLandmines( bs->sess.sessionTeam ) < MAX_TEAM_LANDMINES ) ) { + // look for a land mine area that isn't full + closestTime = 0; + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINE_AREA ) ) ) { + // is it disabled? + //if (trav->aiInactive & (1<sess.sessionTeam)) continue; // FIXME: djbob needs to add the etype of the landmine hint to the filter + // is it for us? + if ( ( trav->spawnflags & 1 ) && ( bs->sess.sessionTeam != TEAM_AXIS ) ) { + continue; + } + if ( ( trav->spawnflags & 2 ) && ( bs->sess.sessionTeam != TEAM_ALLIES ) ) { + continue; + } + // has it got enough landmines? + if ( trav->count2 >= trav->count ) { + continue; + } + // already someone else heading for it? + if ( BotNumTeamMatesWithTargetByClass( bs, trav->s.number, NULL, 0, PC_ENGINEER ) ) { + continue; // someone else is going for it + } + // valid goal towards it? + if ( !BotGoalForEntity( bs, trav->s.number, &target, BGU_LOW ) ) { + continue; + } + // + // if we are already heading for this goal, stay with it, otherwise look only for closest targets + // + if ( bs->target_goal.entitynum == trav->s.number && bs->ainode == AINode_MP_PlantMine ) { + return qfalse; + } + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( !t ) { + continue; + } + if ( closestTime && t > closestTime ) { + continue; + } + // + bestTarget = target; + closestTime = t; + } + // + if ( closestTime ) { + // find a spot inside the area to plant the mine + trav = &g_entities[bestTarget.entitynum]; + i = 0; + do { + VectorAdd( BotGetOrigin( trav->s.number ), trav->pos3, vec ); + vec[0] += crandom() * trav->r.absmax[0] * 0.25; + vec[1] += crandom() * trav->r.absmax[1] * 0.25; + // + bestTarget.areanum = BotPointAreaNum( bs->client, vec ); + VectorCopy( vec, bestTarget.origin ); + } while ( ( ++i < 5 ) && ( ( bestTarget.areanum < 0 ) || !PointInBounds( vec, trav->r.absmin, trav->r.absmax ) ) ); + // + if ( i < 5 ) { // we found a valid spot + bs->target_goal = bestTarget; + AIEnter_MP_PlantMine( bs ); + return qtrue; + } + } + } + + // TRIGGER MULTIPLE + { + // find a trigger_multiple + trav = NULL; + ent = NULL; + closestTime = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_TRIGGER_MULTIPLE ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + // is it active? + if ( !trav->r.linked || ( trav->nextthink > level.time + 1000 ) ) { + continue; // if it's thinking, then it's not active (doesnt respond to touch) + } + // + if ( !BotGetReachableEntityArea( bs, trav->s.number, &target ) ) { + continue; // not reachable + } + if ( !BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + continue; // outside autonomy range + } + // if the current destination is not touching the brush, abort + VectorAdd( target.origin, bs->cur_ps.mins, mins ); + VectorAdd( target.origin, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, trav ) ) { + continue; + } + // + // get the travel time + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( !t ) { + continue; + } + // + targetPriority = 2; + // + // if there are too many going for this goal, then ignore it if we are the furthest away + if ( ( numList = BotNumTeamMatesWithTargetAndCloser( bs, trav->s.number, target.areanum, list, MAX_CLIENTS, -1 ) ) > (int)floor( 0.3 * numTeammates ) ) { + distances = BotSortPlayersByTraveltime( BotGetArea( trav->s.number ), list, numList ); + if ( distances[numList - 1] < (float)t ) { + // we are the furthest + targetPriority = 1; + // also consider other goals + t += 5000; + } + } + // only go for it if our current best goal is of lesser priority + if ( targetPriority < bestPriority ) { + continue; + } + // + // we should touch it + // + if ( ( targetPriority <= bestPriority ) && ( closestTime && t > closestTime ) ) { + continue; + } + // + bestTarget = target; + closestTime = t; + bestPriority = targetPriority; + } + // + if ( closestTime ) { + trav = &g_entities[bestTarget.entitynum]; + // is it for us only? + if ( ( ( trav->spawnflags & 1 ) && ( bs->sess.sessionTeam == TEAM_AXIS ) ) || + ( ( trav->spawnflags & 2 ) && ( bs->sess.sessionTeam == TEAM_ALLIES ) ) ) { + if ( targetPriority > 1 ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } else { // enough people going for it, so only go for it if there's nothing else to do + targetTime = closestTime; + gotTarget = -1; + defendTarget = qfalse; + } + } else { // defend it + if ( targetPriority > 1 ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } else { + targetTime = closestTime; + gotTarget = -1; + defendTarget = qtrue; + } + } + } + } + + //================================================================================================ + // SECONDARY TARGETS() + + // LEADER + + if ( !gotTarget ) { + if ( bs->leader > -1 ) { + if ( bs->lead_time > trap_AAS_Time() ) { + // we must follow them + goto secondaryTarget; + } + } + } + + // CHECKPOINTS + + if ( !gotTarget ) { + + // Defend checkpoints if they are owned by us. Make sure we disperse troops amongst checkpoints, and actively + // pursue checkpoints not owned by us more aggressively. + + t = 0; + gotTarget = qfalse; + // count the checkpoints + i = 0; + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_CHECKPOINT ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + i++; + } + if ( i ) { + f = 1.0f / i; + } + //if (level.captureFlagMode) { + // c = (int)ceil(0.35*f*numTeammates); + //} else { + c = (int)ceil( f * numTeammates ); + //} + + // see if we should get/defend one + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_CHECKPOINT ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + // if the opposition team controls this checkpoint, or it hasnt been captured yet + if ( trav->count == ( bs->sess.sessionTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS ) || ( trav->count < 0 ) ) { + if ( BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) < c ) { + // GO GET IT + // do we have a route to the enemy flag? + secondary.entitynum = trav->s.number; + VectorAdd( trav->r.absmin, trav->r.absmax, center ); + VectorScale( center, 0.5, center ); + secondary.areanum = BotReachableBBoxAreaNum( bs, trav->r.absmin, trav->r.absmax ); + if ( !secondary.areanum ) { + secondary.areanum = BotPointAreaNum( -1, center ); + } + //secondary.areanum = trap_AAS_PointAreaNum(center); + secondary.flags = GFL_NOSLOWAPPROACH; + VectorCopy( trav->r.mins, secondary.mins ); + VectorCopy( trav->r.maxs, secondary.maxs ); + VectorCopy( center, secondary.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, secondary.areanum, bs->tfl ); + if ( t && ( !gotTarget || t < targetTime ) ) { + targetTime = t; + gotTarget = -1; // use this if nothing else is around + defendTarget = qfalse; + bestTarget = secondary; + if ( t < 500 && BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) < 1 ) { + if ( BotGoalWithinMovementAutonomy( bs, &bestTarget, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } + } + } + } else { + // defend the checkpoint? + if ( !level.captureFlagMode && BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) < ( c > 1 ? c / 2 : c ) ) { + // GO GET IT + // do we have a route to the enemy flag? + secondary.entitynum = trav->s.number; + VectorAdd( trav->r.absmin, trav->r.absmax, center ); + VectorScale( center, 0.5, center ); + //VectorCopy( trav->r.currentOrigin, center ); + //center[2] += 30; + secondary.areanum = trap_AAS_PointAreaNum( center ); + if ( !secondary.areanum ) { + secondary.areanum = BotPointAreaNum( -1, center ); + } + secondary.flags = GFL_NOSLOWAPPROACH; + VectorCopy( trav->r.mins, secondary.mins ); + VectorCopy( trav->r.maxs, secondary.maxs ); + VectorCopy( trav->r.currentOrigin, secondary.origin ); + // + // don't worry about our flag as much + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, secondary.areanum, bs->tfl ); + if ( !t ) { + t = BotFindSparseDefendArea( bs, &secondary, qfalse ); + } + if ( t ) { + t = 200 + 2 * t; + t += bs->client * 100; // random element to spread bots out + if ( !gotTarget || t < targetTime ) { + if ( BotGoalWithinMovementAutonomy( bs, &secondary, BGU_LOW ) ) { + targetTime = t; + gotTarget = -1; + defendTarget = qtrue; + bestTarget = secondary; + } + } + } + } + } + } + } + + // ATTRACTORS + if ( !gotTarget ) { + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_ATTRACTOR ) ) ) { + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + if ( BotGoalForEntity( bs, trav->s.number, &target, BGU_MAXIMUM ) ) { + // get the distance, halve it if there is noone one going for this attractor + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) == 0 ) { + t /= 2; + } + if ( !gotTarget || ( t < targetTime ) ) { + targetTime = t; + gotTarget = -1; + defendTarget = qtrue; + bestTarget = target; + } + } + } + } + + // OBJECTIVE LOCATIONS + + // defend target objectives (both teams should try and "occupy" objectives) +/* if (gotTarget != qtrue) { + qboolean useObjective = qfalse; + // count the objectives + i = 0; + c = 999; + oldest = -1; + trav = NULL; + while ( trav = BotFindNextStaticEntity(trav, BOTSTATICENTITY_OBJECTIVE_INFO) ) { + // is it disabled? + if (trav->aiInactive & (1<sess.sessionTeam)) continue; + // + // only guard objectives that are attached to constructibles or destructibles + // + useObjective = qfalse; + // + if (trav->target_ent) { + if (trav->target_ent->s.eType == ET_CONSTRUCTIBLE) { + // has it been built? + // all constructibles are considered important, whether built or not, unless they are disabled in scripting + //if (BotIsConstructible( bs->sess.sessionTeam, trav->s.number )) { + useObjective = qtrue; + //} + } else if (trav->target_ent->s.eType == ET_EXPLOSIVE) { + // is it still valid? + if (trav->target_ent->r.linked) { + useObjective = qtrue; + } + } + } + // + if (!useObjective) continue; // ignore this objective + // + i++; + if ((k = BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 )) < c) { + if (!BotGetReachableEntityArea( bs, trav->s.number, &secondary )) continue; // not reachable + c = k; + oldest = trav->s.number; + } + } + if (i) { + f = 1.0f / i; + } + trav = NULL; + while (trav = BotFindNextStaticEntity(trav, BOTSTATICENTITY_OBJECTIVE_INFO)) { + // is it disabled? + if (trav->aiInactive & (1<sess.sessionTeam)) continue; + // + if (BotNumTeamMatesWithTarget( bs, trav->s.number, NULL, 0 ) >= (int)ceil(f*numTeammates)) { + if (bestPriority >= 2) continue; // we already have an important goal + targetPriority = 1; + } else { + targetPriority = 2; + } + // + // only guard objectives that are attached to constructibles or destructibles + // + useObjective = qfalse; + // + if (trav->target_ent) { + if (trav->target_ent->s.eType == ET_CONSTRUCTIBLE) { + // has it been built? + //if (BotIsConstructible( bs->sess.sessionTeam, trav->s.number )) { + useObjective = qtrue; + //} + } else if (trav->target_ent->s.eType == ET_EXPLOSIVE) { + // is it still valid? + if (trav->target_ent->r.linked) { + useObjective = qtrue; + } + } + // + if (!useObjective) continue; // ignore this objective + // + // GO GET IT + // do we have a route to the objective? + if (!BotGetReachableEntityArea( bs, trav->s.number, &secondary )) continue; // not reachable + // + t = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, secondary.areanum, bs->tfl); + if (t) { + if (!gotTarget || (targetPriority > bestPriority) || (oldest != -1 ? trav->s.number == oldest : t < targetTime)) { + if (BotGoalWithinMovementAutonomy( bs, &secondary, BGU_LOW )) { + targetTime = t; + gotTarget = qtrue; + defendTarget = qtrue; + bestTarget = secondary; + bestPriority = targetPriority; + } + } + } + } + } + }*/ + + //================================================================================================ + // PRIMARY TARGETS() + + // FLAGS + if ( level.captureFlagMode ) { + + if ( bs->sess.sessionTeam == level.attackingTeam ) { + // ATTACKING + + // do we have the flag? + if ( BotCarryingFlag( bs->client ) ) { // we have it!! + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !ent ) { + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( ent ) { + vec3_t bestAreaCenter; + // + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + // find the best goal area + numList = trap_AAS_BBoxAreas( ent->r.absmin, ent->r.absmax, list, 32 ); + if ( numList ) { + oldestTime = -1; + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + VectorCopy( center, bestAreaCenter ); + } + } + } + } + if ( bestDist > 0 ) { + if ( bs->target_goal.entitynum == ent->s.number && bs->ainode == AINode_MP_TouchTarget + && bs->target_goal.areanum == oldest ) { + return qfalse; + } + // + BotClearGoal( &target ); + // use this as the goal origin + VectorCopy( bestAreaCenter, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = ent->s.number; + target.flags = GFL_NOSLOWAPPROACH; + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + // + bs->target_goal = target; + // + // if we are close to the static flag, then look for an alternate route + BotFlagAtBase( level.attackingTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS, &ent ); + if ( ent && VectorDistanceSquared( bs->origin, ent->r.currentOrigin ) < ( 384 * 384 ) ) { + // check for an alternate route + if ( ( t > 1000 ) && ( numList = trap_AAS_AlternativeRouteGoals( bs->origin, bestAreaCenter, bs->tfl, altroutegoals, 40, 0 ) ) ) { + //pick one at random + i = rand() % numList; + //make this the altrouetgoal + bs->alt_goal = bs->target_goal; + trap_AAS_AreaWaypoint( altroutegoals[i].areanum, bs->alt_goal.origin ); + bs->alt_goal.areanum = trap_AAS_PointAreaNum( bs->alt_goal.origin ); + bs->alt_goal.number = level.time; + bs->target_goal.number = level.time; + } + } + // + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } else { + // can't get to the destination point + if ( bs->ainode == AINode_MP_Stand ) { + return qfalse; + } + AIEnter_MP_Stand( bs ); + return qfalse; + } + } + } + + // SNIPER AI + // note: snipers only operate if flag is at base + if ( BotCanSnipe( bs, qtrue ) ) { + // already sniping + if ( bs->ainode == AINode_MP_SniperSpot ) { + return qfalse; + } + // + c = BotBestSniperSpot( bs ); + if ( c > -1 ) { + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_SniperSpot( bs ); + return qtrue; + } + } + } + + // MG42 AI + // note: mg42 only operate if flag is at base + if ( bs->sess.playerType == PC_SOLDIER ) { + // + c = BotBestMG42Spot( bs, !gotTarget ); + if ( c > -1 ) { + // are we already mounting it? + if ( ( bs->target_goal.entitynum == c ) && ( bs->ainode == AINode_MP_MG42Mount || bs->ainode == AINode_MP_MG42Scan ) ) { + return qfalse; + } + // + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_MG42Mount( bs ); + return qtrue; + } + } + } + + // is the enemy flag at base? + if ( BotFlagAtBase( level.attackingTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS, &ent ) == qtrue ) { + // GO GET IT + // do we have a route to the enemy flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->r.currentOrigin, center ); + center[2] += 30; + target.areanum = trap_AAS_PointAreaNum( center ); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( ent->r.mins, target.mins ); + VectorCopy( ent->r.maxs, target.maxs ); + VectorCopy( center, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( t && ( ( t < 500 ) || !gotTarget || defendTarget || ( ( t / 2 ) < targetTime ) ) ) { + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + // + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = target; + // + // check for an alternate route + if ( t > 600 && ( numList = trap_AAS_AlternativeRouteGoals( bs->origin, bs->target_goal.origin, bs->tfl, altroutegoals, 40, 0 ) ) ) { + //pick one at random + i = rand() % numList; + //make this the altrouetgoal + bs->alt_goal = bs->target_goal; + trap_AAS_AreaWaypoint( altroutegoals[i].areanum, bs->alt_goal.origin ); + bs->alt_goal.areanum = trap_AAS_PointAreaNum( bs->alt_goal.origin ); + bs->alt_goal.number = level.time; + bs->target_goal.number = level.time; + } + // + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } + } else { + qboolean protect = qtrue; + + // who has it? + BotClearGoal( &target ); + target.entitynum = BotGetTeamFlagCarrier( bs ); + // + if ( ( target.entitynum > -1 && target.entitynum != bs->client ) && + ( VectorDistanceSquared( g_entities[target.entitynum].r.currentOrigin, bs->origin ) < ( 1600 * 1600 ) ) /*&& + (trap_InPVS(g_entities[target.entitynum].r.currentOrigin, bs->origin))*/) { + + // TODO: if there are too many defenders, and we are the furthest away, stop defending + // !!! + if ( ( numList = BotNumTeamMatesWithTarget( bs, target.entitynum, list, MAX_CLIENTS ) ) >= BOT_FLAG_CARRIER_DEFENDERS ) { + ourDist = VectorDistanceSquared( bs->origin, g_entities[target.entitynum].r.currentOrigin ); + //if (!trap_InPVS( bs->origin, g_entities[target.entitynum].r.currentOrigin )) ourDist += 2048; + distances = BotSortPlayersByDistance( g_entities[target.entitynum].r.currentOrigin, list, numList ); + if ( distances[numList - 1] < ourDist ) { + // we are the furthest + protect = qfalse; + } + } + + if ( protect ) { + // protect the carrier + ent = BotGetEntity( target.entitynum ); + VectorCopy( ent->r.currentOrigin, center ); + target.areanum = BotPointAreaNum( target.entitynum, center ); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( ent->r.mins, target.mins ); + VectorCopy( ent->r.maxs, target.maxs ); + VectorCopy( ent->r.currentOrigin, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( !t ) { + t = BotFindSparseDefendArea( bs, &target, qfalse ); + } + if ( t && ( !gotTarget || defendTarget || t < targetTime ) ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + } + + // head to the flag destination? + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !ent ) { + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( target.entitynum != bs->client && ent && ( BotNumTeamMatesWithTarget( bs, ent->s.number, NULL, 0 ) < (int)ceil( 0.3 * numTeammates ) ) ) { + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + // find the best goal area + numList = trap_AAS_BBoxAreas( ent->r.absmin, ent->r.absmax, list, 32 ); + if ( numList ) { + oldestTime = -1; + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + } + if ( bestDist > 0 ) { + BotClearGoal( &target ); + // use this as the goal origin + VectorCopy( center, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = ent->s.number; + target.flags = GFL_NOSLOWAPPROACH; + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == ent->s.number && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + + // HEAD FOR IT, THEN WAIT FOR IT + BotFlagAtBase( level.attackingTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS, &ent ); + // do we have a route to the enemy flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->r.currentOrigin, center ); + center[2] += 30; + target.areanum = trap_AAS_PointAreaNum( center ); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( ent->r.mins, target.mins ); + VectorCopy( ent->r.maxs, target.maxs ); + VectorCopy( ent->r.currentOrigin, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( !t ) { + t = BotFindSparseDefendArea( bs, &target, qfalse ); + } + if ( t && ( !gotTarget || defendTarget || t < targetTime ) ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + + } else { // ================================================================ + // DEFENDING() + + // SNIPER AI + // note: snipers only operate if flag is at base + if ( BotCanSnipe( bs, qtrue ) ) { + // already sniping + if ( bs->ainode == AINode_MP_SniperSpot ) { + return qfalse; + } + + c = BotBestSniperSpot( bs ); + if ( c > -1 ) { + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_SniperSpot( bs ); + return qtrue; + } + } + } + + // MG42 AI + // note: mg42 only operate if flag is at base + if ( ( bs->sess.playerType == PC_SOLDIER ) && ( BotFlagAtBase( bs->sess.sessionTeam, &ent ) == qtrue ) ) { + // already sniping + if ( bs->ainode == AINode_MP_MG42Mount || bs->ainode == AINode_MP_MG42Scan ) { + return qfalse; + } + // + c = BotBestMG42Spot( bs, !gotTarget ); + if ( c > -1 ) { + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_MG42Mount( bs ); + return qtrue; + } + } + } + + if ( !gotTarget || !level.explosiveTargets[bs->sess.sessionTeam == TEAM_AXIS ? 0 : 1] || ( level.explosiveTargets[bs->sess.sessionTeam == TEAM_AXIS ? 0 : 1] - BotGetTargetExplosives( bs->sess.sessionTeam, NULL, 0, qtrue ) > 0 ) ) { + // at least one target has been breached, so start defending objective + + // defend the objective! + if ( BotFlagAtBase( bs->sess.sessionTeam, &ent ) == qtrue ) { + if ( !gotTarget || BotNumTeamMatesWithTarget( bs, ent->s.number, NULL, 0 ) < (int)ceil( 0.4 * numTeammates ) ) { + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->r.currentOrigin, center ); + center[2] += 30; + target.areanum = trap_AAS_PointAreaNum( center ); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( ent->r.mins, target.mins ); + VectorCopy( ent->r.maxs, target.maxs ); + VectorCopy( ent->r.currentOrigin, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( t && ( !gotTarget || defendTarget || t < targetTime ) ) { + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + // + BotFindSparseDefendArea( bs, &target, qtrue ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + // we are on defense + BotVoiceChatAfterIdleTime( bs->client, "OnDefense", SAY_TEAM, 1000, qfalse, 10000 + rand() % 5000, qfalse ); + bs->target_goal = target; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + } + } else { // defend the enemy destination + + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !ent ) { + ent = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( ent /*&& (BotNumTeamMatesWithTarget( bs, ent->s.number, NULL, 0 ) < (int)ceil(0.5*numTeammates))*/ ) { + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + bestDist = -1; + // find the best goal area + numList = trap_AAS_BBoxAreas( ent->r.absmin, ent->r.absmax, list, 32 ); + if ( numList ) { + oldestTime = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, list[i], bs->tfl ); + if ( t > 0 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + } + if ( bestDist > 0 ) { + BotClearGoal( &target ); + // use this as the goal origin + VectorCopy( center, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + target.areanum = oldest; + target.entitynum = ent->s.number; + target.flags = GFL_NOSLOWAPPROACH; + // + BotFindSparseDefendArea( bs, &target, qtrue ); + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + if ( bs->target_goal.entitynum == ent->s.number && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + } + // + // go to the flag room + BotFlagAtBase( bs->sess.sessionTeam, &ent ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->r.currentOrigin, center ); + center[2] += 30; + target.areanum = trap_AAS_PointAreaNum( center ); + target.flags = GFL_NOSLOWAPPROACH; + VectorCopy( ent->r.mins, target.mins ); + VectorCopy( ent->r.maxs, target.maxs ); + VectorCopy( ent->r.currentOrigin, target.origin ); + // + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, target.areanum, bs->tfl ); + if ( t && ( !gotTarget || defendTarget || t < targetTime ) ) { + BotFindSparseDefendArea( bs, &target, qtrue ); + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_MEDIUM ) ) { + if ( bs->target_goal.entitynum == target.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = target; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } + + } + + } + + } + + } else { // NOT CTF MODE + + // SNIPER AI + // note: snipers only operate if flag is at base + if ( BotCanSnipe( bs, qtrue ) ) { + // already sniping + if ( bs->ainode == AINode_MP_SniperSpot ) { + return qfalse; + } + // + c = BotBestSniperSpot( bs ); + if ( c > -1 ) { + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_SniperSpot( bs ); + return qtrue; + } + } + } + + // MG42 AI + // note: mg42 only operate if flag is at base + if ( qtrue ) { //bs->sess.playerType == PC_SOLDIER) { + // already sniping + if ( bs->ainode == AINode_MP_MG42Mount || bs->ainode == AINode_MP_MG42Scan ) { + return qfalse; + } + // + c = BotBestMG42Spot( bs, !gotTarget ); + if ( c > -1 ) { + ent = BotGetEntity( c ); + // do we have a route to the flag? + BotClearGoal( &target ); + target.entitynum = ent->s.number; + VectorCopy( ent->s.origin, target.origin ); + target.areanum = BotPointAreaNum( -1, target.origin ); + VectorCopy( bs->cur_ps.mins, target.mins ); + VectorCopy( bs->cur_ps.maxs, target.maxs ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + bs->target_goal = target; + AIEnter_MP_MG42Mount( bs ); + return qtrue; + } + } + } + + } + + //================================================================================================ + +secondaryTarget: + + // if we found a secondary target (checkpoint), go for it + if ( gotTarget ) { + if ( defendTarget ) { + BotFindSparseDefendArea( bs, &bestTarget, qtrue ); + if ( BotGoalWithinMovementAutonomy( bs, &bestTarget, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_DefendTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_DefendTarget( bs ); + return qtrue; + } + } else { + if ( BotGoalWithinMovementAutonomy( bs, &bestTarget, BGU_LOW ) ) { + if ( bs->target_goal.entitynum == bestTarget.entitynum && bs->ainode == AINode_MP_TouchTarget ) { + return qfalse; + } + bs->target_goal = bestTarget; + AIEnter_MP_TouchTarget( bs ); + return qtrue; + } + } + } + + return qfalse; +} diff --git a/src/botai/ai_dmgoal_mp.h b/src/botai/ai_dmgoal_mp.h new file mode 100644 index 0000000..69238fb --- /dev/null +++ b/src/botai/ai_dmgoal_mp.h @@ -0,0 +1,46 @@ +/* +=========================================================================== + +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: ai_dmgoal_mp.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +// +// MULTIPLAYER GOAL AI +// + +qboolean BotMP_CheckEmergencyGoals( bot_state_t *bs ); +qboolean BotMP_FindGoal( bot_state_t *bs ); +// Gordon: new version +qboolean BotMP_FindGoal_New( bot_state_t *bs ); + diff --git a/src/botai/ai_dmnet_mp.c b/src/botai/ai_dmnet_mp.c new file mode 100644 index 0000000..bbdce4e --- /dev/null +++ b/src/botai/ai_dmnet_mp.c @@ -0,0 +1,6696 @@ +/* +=========================================================================== + +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: ai_dmnet_mp.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +#include "../game/g_local.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" +// +#include "ai_main.h" +#include "ai_team.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +#include "ai_dmnet_mp.h" + +//data file headers +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + +/* +================== +BotMP_MoveToGoal +================== +*/ +void BotMP_MoveToGoal( bot_state_t *bs, bot_moveresult_t * result, int movestate, bot_goal_t * goal, int travelflags ) { +#ifdef _DEBUG + if ( bot_debug.integer == 3 && level.clients[0].sess.spectatorClient == bs->client ) { + goal->flags |= GFL_DEBUGPATH; + } else { + goal->flags &= ~GFL_DEBUGPATH; + } +#endif // _DEBUG + + trap_BotMoveToGoal( result, movestate, goal, travelflags ); +} + +/* +================== +AIEnter_MP_Intermission() +================== +*/ +void AIEnter_MP_Intermission( bot_state_t *bs ) { + //reset the bot state + BotResetState( bs ); + bs->ainode = AINode_MP_Intermission; + bs->ainodeText = "AINode_MP_Intermission"; +} + +/* +================== +AINode_MP_Intermission() +================== +*/ +int AINode_MP_Intermission( bot_state_t *bs ) { + //if the intermission ended + if ( !BotIntermission( bs ) ) { + AIEnter_MP_Stand( bs ); + } + return qtrue; +} + +/* +================== +AIEnter_MP_Observer() +================== +*/ +void AIEnter_MP_Observer( bot_state_t *bs ) { + //reset the bot state + BotResetState( bs ); + bs->ainode = AINode_MP_Observer; + bs->ainodeText = "AINode_MP_Observer"; +} + +/* +================== +AINode_MP_Observer() +================== +*/ +int AINode_MP_Observer( bot_state_t *bs ) { + //if the bot left observer mode + if ( !BotIsObserver( bs ) ) { + AIEnter_MP_Stand( bs ); + } + return qtrue; +} + +/* +================== +AIEnter_MP_Stand() +================== +*/ +void AIEnter_MP_Stand( bot_state_t *bs ) { + //bs->standfindenemy_time = trap_AAS_Time() + 1; + bs->respawn_time = trap_AAS_Time() + 20; // after this long just standing around, suicide + bs->ignore_specialgoal_time = 0; + bs->ainode = AINode_MP_Stand; + bs->ainodeText = "AINode_MP_Stand"; +} + +/* +================== +AINode_MP_Stand() +================== +*/ +int AINode_MP_Stand( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t pos; + + + // Gordon: pow, so just stand around till scripted + if ( BotIsPOW( bs ) ) { + return qtrue; + } + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + if ( bs->standfindenemy_time < trap_AAS_Time() ) { + if ( BotFindEnemyMP( bs, -1, qfalse ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + //bs->standfindenemy_time = trap_AAS_Time() + 1; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + if ( bs->stand_time < trap_AAS_Time() ) { + //trap_BotEnterChat(bs->cs, bs->client, bs->chatto); + //bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + if ( bs->ainode != AINode_MP_Stand ) { + return qfalse; + } else if ( !bs->areanum ) { + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } else { + // stand for a bit longer + bs->stand_time = trap_AAS_Time() + 0.4 + 0.4 * random(); + } + } else { + // look for health/ammo packs + if ( BotFindNearbyGoal( bs ) ) { + AIEnter_MP_Seek_NBG( bs ); + return qfalse; + } + } + // check for dangerous elements + VectorCopy( bs->origin, goal.origin ); + goal.areanum = bs->areanum; + goal.entitynum = bs->client; + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // if we are outside HALF autonomy range, get back there + if ( BotGetMovementAutonomyPos( bs, pos ) ) { + float halfDist = 0.5 * BotGetMovementAutonomyRange( bs, NULL ); + if ( VectorDistanceSquared( bs->origin, pos ) > ( halfDist * halfDist ) ) { + AIEnter_MP_MoveToAutonomyRange( bs ); + return qfalse; + } + } + // if we have been standing for too long + if ( bs->respawn_time < trap_AAS_Time() ) { + Cmd_Kill_f( &g_entities[bs->client] ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_Respawn() +================== +*/ +void AIEnter_MP_Respawn( bot_state_t *bs ) { + //reset some states + trap_BotResetMoveState( bs->ms ); + trap_BotResetGoalState( bs->gs ); + trap_BotResetAvoidGoals( bs->gs ); + trap_BotResetAvoidReach( bs->ms ); + bs->respawn_time = trap_AAS_Time() + 1 + random(); + bs->respawnchat_time = 0; + // + bs->flags &= ~BFL_MISCFLAG; + bs->lastClassCheck = 0; + //set respawn state + bs->respawn_wait = qfalse; + bs->ainode = AINode_MP_Respawn; + bs->ainodeText = "AINode_MP_Respawn"; +} + +/* +================== +AINode_MP_Respawn() +================== +*/ +int AINode_MP_Respawn( bot_state_t *bs ) { + qboolean do_respawn = qfalse; + gentity_t *ent; + int testtime; + // RF, only hit jump if reinforcement time is about to run out + ent = BotGetEntity( bs->entitynum ); +// disabled, if medic has troubles finding us, we'll be waiting forever +// if (ent->missionLevel < level.time) { // else medic is heading to us to revive + if ( ent->client->sess.sessionTeam == TEAM_AXIS ) { + testtime = level.time % g_redlimbotime.integer; + if ( testtime > g_redlimbotime.integer - 2000 ) { + do_respawn = qtrue; + } + } else if ( ent->client->sess.sessionTeam == TEAM_ALLIES ) { + testtime = level.time % g_bluelimbotime.integer; + if ( testtime > g_bluelimbotime.integer - 2000 ) { + do_respawn = qtrue; + } + } +// } + // + if ( bs->lastClassCheck < level.time - 4000 ) { // check for a better class + bs->mpClass = BotSuggestClass( bs, bs->mpTeam ); + ent->client->sess.latchPlayerType = bs->mpClass; + if ( bs->mpClass != ent->client->sess.playerType ) { + bs->flags |= BFL_MISCFLAG; + } + bs->lastClassCheck = level.time + rand() % 1000; + // sometimes when we die, we should re-evaluate our weapon selection + if ( ( bs->flags & BFL_MISCFLAG ) || ( random() < 0.3 ) ) { + bs->mpWeapon = BotSuggestWeapon( bs, bs->sess.sessionTeam ); + ent->client->sess.latchPlayerWeapon = bs->mpWeapon; + } + } + if ( bs->respawn_wait ) { + if ( !BotIsDead( bs ) ) { + // perhaps we should tell everyone who we are + if ( bs->flags & BFL_MISCFLAG ) { + static int lastCall; + if ( lastCall > level.time || lastCall < level.time - 2000 ) { + lastCall = level.time; + switch ( bs->mpClass ) { + case PC_SOLDIER: + BotVoiceChatAfterIdleTime( bs->client, "IamSoldier", SAY_TEAM, 1000 + rand() % 5000, BOT_SHOWTEXT, 20000, qfalse ); + break; + case PC_MEDIC: + BotVoiceChatAfterIdleTime( bs->client, "IamMedic", SAY_TEAM, 1000 + rand() % 5000, BOT_SHOWTEXT, 20000, qfalse ); + break; + case PC_FIELDOPS: + BotVoiceChatAfterIdleTime( bs->client, "IamLieutenant", SAY_TEAM, 1000 + rand() % 5000, BOT_SHOWTEXT, 20000, qfalse ); + break; + case PC_ENGINEER: + BotVoiceChatAfterIdleTime( bs->client, "IamEngineer", SAY_TEAM, 1000 + rand() % 5000, BOT_SHOWTEXT, 20000, qfalse ); + break; + } + } + } else if ( bs->sess.sessionTeam == level.attackingTeam ) { + if ( rand() % 2 ) { + BotVoiceChatAfterIdleTime( bs->client, "LetsGo", SAY_TEAM, 1000 + rand() % 2000, qfalse, 20000, qfalse ); + } + } + // + BotDefaultNode( bs ); + } else { + trap_EA_Respawn( bs->client ); + // RF, Wolf uses jump + if ( do_respawn ) { + trap_EA_Jump( bs->client ); + } + } + } else if ( bs->respawn_time < trap_AAS_Time() ) { + //wait until respawned + bs->respawn_wait = qtrue; + //elementary action respawn + trap_EA_Respawn( bs->client ); + // RF, Wolf uses jump + if ( do_respawn ) { + trap_EA_Jump( bs->client ); + } + // + if ( bs->respawnchat_time ) { + //trap_BotEnterChat(bs->cs, bs->client, bs->chatto); + bs->enemy = -1; + } + } + if ( bs->respawnchat_time && bs->respawnchat_time < trap_AAS_Time() - 0.5 ) { + trap_EA_Talk( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_Seek_ActivateEntity() +================== +*/ +void AIEnter_MP_Seek_ActivateEntity( bot_state_t *bs ) { + bs->ainode = AINode_MP_Seek_ActivateEntity; + bs->ainodeText = "AINode_MP_Seek_ActivateEntity"; +} + +/* +================== +AINode_MP_Seek_Activate_Entity() +================== +*/ +int AINode_MP_Seek_ActivateEntity( bot_state_t *bs ) { + bot_goal_t *goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // + if ( bot_grapple.integer ) { + bs->tfl |= TFL_GRAPPLEHOOK; + } + //if in lava or slime the bot should be able to get out + if ( BotInLava( bs ) ) { + bs->tfl |= TFL_LAVA; + } + if ( BotInSlime( bs ) ) { + bs->tfl |= TFL_SLIME; + } + + //no enemy + bs->enemy = -1; + // + goal = &bs->activategoal; + //if the bot has no goal + if ( !goal ) { + bs->activate_time = 0; + } + //if the bot touches the current goal + else if ( trap_BotTouchingGoal( bs->origin, goal ) ) { + BotChooseWeapon( bs ); +#ifdef DEBUG + BotAI_Print( PRT_MESSAGE, "touched button or trigger\n" ); +#endif //DEBUG + bs->activate_time = 0; + } + // + if ( bs->activate_time < trap_AAS_Time() ) { + AIEnter_MP_Seek_NBG( bs ); + return qfalse; + } + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + + } + //check if the bot is blocked + BotAIBlocked( bs, &moveresult, qtrue ); + // + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else if ( !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( trap_BotMovementViewTarget( bs->ms, goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else { + //vectoangles(moveresult.movedir, bs->ideal_viewangles); + } + bs->ideal_viewangles[2] *= 0.5; + } + //if the weapon is used for the bot movement + if ( moveresult.flags & MOVERESULT_MOVEMENTWEAPON ) { + bs->weaponnum = moveresult.weapon; + } + //if there is an enemy + if ( BotFindEnemyMP( bs, -1, qfalse ) ) { + if ( BotWantsToRetreat( bs ) ) { + //keep the current long term goal and retreat + // !!! TODO + } else { + trap_BotResetLastAvoidReach( bs->ms ); + //empty the goal stack + trap_BotEmptyGoalStack( bs->gs ); + //go fight + AIEnter_MP_Battle_Fight( bs ); + } + } + return qtrue; +} + +/* +================== +AIEnter_MP_Seek_NBG() +================== +*/ +void AIEnter_MP_Seek_NBG( bot_state_t *bs ) { +//level.clients[0].sess.spectatorClient = bs->client; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_Seek_NBG; + bs->ainodeText = "AINode_MP_Seek_NBG"; +} + +/* +================== +AINode_MP_Seek_NBG() +================== +*/ +int AINode_MP_Seek_NBG( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + // + goal = bs->nearbygoal; + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // + // should we stop pursuing this target? + ent = BotGetEntity( goal.entitynum ); + if ( !ent->inuse ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } else if ( ent->s.eType == ET_SUPPLIER ) { + if ( !ClientNeedsAmmo( bs->client ) ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } else if ( ent->s.eType == ET_HEALER ) { + if ( BotHealthScale( bs->client ) >= 1.0 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } else if ( ent->s.eType == ET_ITEM && ent->touch ) { + if ( /*trap_BotTouchingGoal( bs->origin, &goal ) ||*/ ( ent->r.svFlags & SVF_NOCLIENT ) ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + // jump randomly + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_AvoidDanger() +================== +*/ +void AIEnter_MP_AvoidDanger( bot_state_t *bs ) { + int bestarea; + + // if this is dynamite + if ( g_entities[bs->avoid_goal.entitynum].s.eType == ET_MISSILE && g_entities[bs->avoid_goal.entitynum].methodOfDeath == MOD_DYNAMITE ) { + if ( !( rand() % 3 ) ) { + BotVoiceChatAfterIdleTime( bs->client, "FireInTheHole", SAY_TEAM, 500, qfalse, 3000, qfalse ); + } + } + + bs->flags &= ~BFL_MISCFLAG; + if ( !( bestarea = trap_AAS_AvoidDangerArea( bs->origin, bs->areanum, bs->avoid_goal.origin, BotPointAreaNum( -1, bs->avoid_goal.origin ), bs->avoid_goal.number + 100, bs->tfl ) ) ) { // no hiding spot, ignore it + bs->flags |= BFL_MISCFLAG; + } else { + trap_AAS_AreaWaypoint( bestarea, bs->avoid_goal.origin ); + bs->avoid_goal.areanum = bestarea; + } + + bs->ainode = AINode_MP_AvoidDanger; + bs->ainodeText = "AINode_MP_AvoidDanger"; +} + +/* +================== +AINode_MP_AvoidDanger() +================== +*/ +int AINode_MP_AvoidDanger( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + int bestarea; + qboolean moved = qfalse; + + memset( &moveresult, 0, sizeof( moveresult ) ); + goal = bs->avoid_goal; + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + //if the target has gone + trav = &g_entities[goal.entitynum]; + if ( !trav->inuse ) { + // just look for a goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } else if ( trav->client && trav->health <= 0 ) { + // just look for a goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } else if ( trav->s.eType == ET_CONSTRUCTIBLE ) { + if ( ( g_entities[bs->client].client->lastConstructibleBlockingWarnEnt != goal.entitynum ) || + ( ( level.time - g_entities[bs->client].client->lastConstructibleBlockingWarnTime ) > 5000 ) ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } + + // if the avoid entity has changed + if ( bs->avoid_spawnCount != trav->spawnCount ) { + // just look for a goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // if the thing we're avoiding is a landmine, then we don't really need to run away from it, just take a few steps + // don't need to move, is this a landmine? + if ( ( trav->methodOfDeath == MOD_LANDMINE ) && VectorDistanceSquared( bs->origin, trav->r.currentOrigin ) > SQR( 256 ) ) { + // we're done running + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + + // make sure the current goal origin is still safe + // is this entity dangerous? + if ( trav->client ) { + // is this player dangerous? + if ( !( trav->client->ps.weapon == WP_PANZERFAUST && trav->client->ps.weaponDelay ) ) { + // not dangerous + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + VectorCopy( trav->r.currentOrigin, target ); + } else if ( trav->s.eType == ET_CONSTRUCTIBLE ) { + } else { + if ( trav->s.eType == ET_MISSILE && trav->s.weapon == WP_DYNAMITE ) { + VectorCopy( trav->r.currentOrigin, target ); + } else { + if ( !G_PredictMissile( trav, trav->nextthink - level.time, target, ( trav->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) ) { + // not dangerous + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } + if ( ( bs->last_avoiddangerarea < level.time - 200 ) && ( VectorDistanceSquared( target, goal.origin ) < ( SQR( trav->splashRadius + 100 ) ) ) ) { + bs->last_avoiddangerarea = level.time + rand() % 200; + if ( !( bestarea = trap_AAS_AvoidDangerArea( bs->origin, bs->areanum, target, BotPointAreaNum( -1, target ), trav->splashRadius + 100, bs->tfl ) ) ) { + // move away from the danger + bs->flags |= BFL_MISCFLAG; + } else { + trap_AAS_AreaWaypoint( bestarea, bs->avoid_goal.origin ); + bs->avoid_goal.areanum = bestarea; + goal = bs->avoid_goal; + } + } + } + //update goal information + + // check for emergency targets (flags, etc) + //if (BotCheckEmergencyTargets( bs )) { + // return qfalse; + //} + if ( bs->flags & BFL_MISCFLAG ) { + moved = qtrue; + //initialize the movement state + BotSetupForMovement( bs ); + // move away from danger + VectorSubtract( target, bs->origin, dir ); + VectorNormalize( dir ); + trap_EA_Move( bs->client, dir, 400 ); + // randomly strafe also + if ( level.time % 2000 < 1000 ) { + trap_EA_MoveLeft( bs->client ); + } else { trap_EA_MoveRight( bs->client );} + } else { + // are we close enough to the goal? + if ( VectorDistanceSquared( bs->origin, goal.origin ) > ( 24 * 24 ) ) { + // MOVEMENT REQUIRED + // + moved = qtrue; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + } + } + // + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( !moved ) { + VectorSubtract( g_entities[goal.entitynum].r.currentOrigin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_GiveAmmo() +================== +*/ +void AIEnter_MP_GiveAmmo( bot_state_t *bs ) { +//level.clients[0].sess.spectatorClient = bs->client; + bs->give_health_time = 0; + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_GiveAmmo; + bs->ainodeText = "AINode_MP_GiveAmmo"; +} + +/* +================== +AINode_MP_GiveAmmo() +================== +*/ +int AINode_MP_GiveAmmo( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + + goal = bs->target_goal; + //if we have changed class + if ( bs->sess.playerType != PC_FIELDOPS ) { + BotDefaultNode( bs ); + return qfalse; + } + //if we have to wait + // Gordon: FIXME: this looks wrong + if ( bs->cur_ps.classWeaponTime > level.time - 8000 ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + //if the target is dead + trav = BotGetEntity( goal.entitynum ); + + // FIXME: temp hack in dealing with NULL returns from BotGetEntity (??) + if ( trav == NULL ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + + if ( !trav->inuse || + !trav->client || + ( trav->client->ps.pm_type != PM_NORMAL ) ) { + // let them roam again + trav->awaitingHelpTime = 0; + // just look for a goal + BotDefaultNode( bs ); + return qfalse; + } + // do they have enough ammo now? + if ( !ClientNeedsAmmo( trav->s.number ) ) { + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 72 ) ) { + // we just helped them + bs->last_helped_client = trav->s.number; + bs->last_helped_time = level.time; + // they should thank us + if ( trav->r.svFlags & SVF_BOT ) { + BotVoiceChatAfterIdleTime( trav->s.number, "Thanks", SAY_TEAM, 1000 + rand() % 2000, qfalse, 3000 + rand() % 2000, qfalse ); + } + } + // let them roam again + trav->awaitingHelpTime = 0; + // just look for a goal + BotDefaultNode( bs ); + return qfalse; + } + //update goal information + VectorCopy( trav->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.areanum = BotPointAreaNum( trav->s.number, trav->r.currentOrigin ); + if ( !bs->target_goal.areanum ) { + BotDefaultNode( bs ); + return qfalse; + } + goal = bs->target_goal; + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 100 ) && BotEntityVisible( bs->client, bs->eye, bs->viewangles, 360, trav->s.number, NULL ) ) { + // make sure other bots dont head for this target also + trav->awaitingHelpTime = level.time + 1500; + } + + // are we close enough to the goal? + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 72 ) ) { + if ( !bs->give_health_time ) { + bs->give_health_time = level.time + 8000; + } else if ( bs->give_health_time < level.time ) { + BotDefaultNode( bs ); + return qfalse; + } + // make sure other bots dont head for this target also + trav->awaitingHelpTime = level.time + 1500; + trav->botIgnoreAmmoTime = level.time + 1000; + // switch to regen and pump away + bs->weaponnum = WP_AMMO; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // aim directly at the dynamite + VectorCopy( bs->origin, target ); + VectorSubtract( trav->r.currentOrigin, target, dir ); + dir[2] *= 0.5; + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + if ( bs->cur_ps.weapon == WP_AMMO && BotWeaponCharged( bs, WP_AMMO ) ) { + trap_EA_Attack( bs->client ); + } + // + return qtrue; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // MOVEMENT REQUIRED + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] + < (int)( 0.8 * ( GetAmmoTableData( bs->cur_ps.weapon ) )->maxclip ) ) + && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_MedicGiveHealth() +================== +*/ +void AIEnter_MP_MedicGiveHealth( bot_state_t *bs ) { + bs->give_health_time = 0; + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_MedicGiveHealth; + bs->ainodeText = "AINode_MP_MedicGiveHealth"; +} + +/* +================== +AINode_MP_MedicGiveHealth() +================== +*/ +int AINode_MP_MedicGiveHealth( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + + goal = bs->target_goal; + + //if we have changed class + if ( bs->sess.playerType != PC_MEDIC ) { + BotDefaultNode( bs ); + return qfalse; + } + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + //if the target has full health + trav = &g_entities[goal.entitynum]; + + if ( !trav->inuse || !trav->client || ( trav->client->ps.pm_type != PM_NORMAL ) || ( trav->client->ps.pm_flags & PMF_LIMBO ) ) { + // let them roam again + trav->awaitingHelpTime = 0; + + // just look for a goal + BotDefaultNode( bs ); + return qfalse; + } + + if ( BotHealthScale( trav->s.number ) >= 1.0 ) { + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 72 ) ) { + // we just helped them + bs->last_helped_client = trav->s.number; + bs->last_helped_time = level.time; + + // they should thank us + if ( trav->r.svFlags & SVF_BOT ) { + BotVoiceChatAfterIdleTime( trav->s.number, "Thanks", SAY_TEAM, 1000 + rand() % 2000, qfalse, 3000 + rand() % 2000, qfalse ); + } + } + + // let them roam again + trav->awaitingHelpTime = 0; + + // just look for a goal + BotDefaultNode( bs ); + return qfalse; + } + + //update goal information + VectorCopy( trav->r.currentOrigin, BotGetOrigin( trav->s.number ) ); + bs->target_goal.areanum = BotGetArea( trav->s.number ); + if ( !bs->target_goal.areanum ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + goal = bs->target_goal; + + // are we close enough to the goal? + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 72 ) ) { + if ( !bs->give_health_time ) { + bs->give_health_time = level.time + 8000; + } else if ( bs->give_health_time < level.time ) { + BotDefaultNode( bs ); + return qfalse; + } + // make sure other bots dont head for this target also + trav->awaitingHelpTime = level.time + 1500; + trav->botIgnoreHealthTime = level.time + 1000; + + // switch to regen and pump away + bs->weaponnum = WP_MEDKIT; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + + // aim directly at the player + VectorCopy( bs->origin, target ); + VectorSubtract( trav->r.currentOrigin, target, dir ); + dir[2] *= 0.5; + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + + // hold fire + if ( bs->cur_ps.weapon == WP_MEDKIT && BotWeaponCharged( bs, WP_MEDKIT ) ) { + trap_EA_Attack( bs->client ); + } + + return qtrue; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // MOVEMENT REQUIRED + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + //initialize the movement state + BotSetupForMovement( bs ); + + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_MedicRevive() +================== +*/ +void AIEnter_MP_MedicRevive( bot_state_t *bs ) { + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_MedicRevive; + bs->ainodeText = "AINode_MP_MedicRevive"; +} + +/* +================== +AINode_MP_MedicRevive() +================== +*/ +int AINode_MP_MedicRevive( bot_state_t *bs ) { + bot_goal_t goal, target; + vec3_t targetpos, dir; + bot_moveresult_t moveresult; + int range; + gentity_t *trav; + + goal = bs->target_goal; + //if we have changed class + if ( bs->sess.playerType != PC_MEDIC ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + //if the target is not dead or is in limbo + trav = BotGetEntity( goal.entitynum ); + + if ( + // START Gordon changes, 23/8/2002 + !trav || + // END Gordon changes, 23/8/2002 + !trav->inuse || + !trav->client || + ( trav->client->ps.pm_flags & PMF_LIMBO ) ) { + // just look for a goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // if someone is close to them, only they should continue reviving + if ( g_entities[goal.entitynum].botIgnoreHealthTime >= level.time ) { + int list[MAX_CLIENTS], numList; + float ourDist, *distances; + // + // if there are too many defenders, and we are the furthest away, stop defending + if ( ( numList = BotNumTeamMatesWithTarget( bs, goal.entitynum, list, MAX_CLIENTS ) ) > 0 ) { + ourDist = VectorDistanceSquared( bs->origin, g_entities[goal.entitynum].r.currentOrigin ); + if ( !trap_InPVS( bs->origin, g_entities[goal.entitynum].r.currentOrigin ) ) { + ourDist += ( 2048 * 2048 ); + } + distances = BotSortPlayersByDistance( g_entities[goal.entitynum].r.currentOrigin, list, numList ); + if ( distances[numList - 1] < ourDist ) { + // we are the furthest + bs->ignore_specialgoal_time = 0; + bs->leader = -1; + BotDefaultNode( bs ); + return qfalse; + } + } + } + // if they are alive + if ( trav->client->ps.pm_type != PM_DEAD ) { + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 72 ) ) { + // we just helped them + bs->last_helped_client = trav->s.number; + bs->last_helped_time = level.time; + // they should thank us + if ( trav->r.svFlags & SVF_BOT ) { + BotVoiceChatAfterIdleTime( trav->s.number, "Thanks", SAY_TEAM, 1000 + rand() % 2000, qfalse, 4000 + rand() % 2000, qfalse ); + } + } + // look for someone to heal (like the person we just revived) + if ( BotClass_MedicCheckGiveHealth( bs, 200, &target ) ) { + bs->target_goal = target; + AIEnter_MP_MedicGiveHealth( bs ); + return qtrue; + } + // just look for a goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + //update goal information + VectorCopy( trav->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.areanum = BotPointAreaNum( trav->s.number, trav->r.currentOrigin ); + if ( !bs->target_goal.areanum ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + goal = bs->target_goal; + //look for closer revives + range = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, goal.areanum, bs->tfl ); + // + if ( range < 200 && bs->enemy < 0 ) { + // switch to regen + bs->weaponnum = WP_MEDIC_SYRINGE; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // make sure other bots dont head for this target also + g_entities[goal.entitynum].botIgnoreHealthTime = level.time + 500; + } + + // are we close enough to the goal? + if ( VectorDistanceSquared( bs->eye, goal.origin ) < SQR( 42 ) ) { + // make sure other bots dont head for this target also + g_entities[goal.entitynum].botIgnoreHealthTime = level.time + 1500; + // crouch down + trap_EA_Crouch( bs->client ); + // switch to regen and pump away + bs->weaponnum = WP_MEDIC_SYRINGE; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // aim directly at the dynamite + VectorCopy( bs->origin, targetpos ); + targetpos[2] += bs->cur_ps.viewheight; + VectorSubtract( trav->r.currentOrigin, targetpos, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + if ( bs->cur_ps.weapon == WP_MEDIC_SYRINGE ) { + trap_EA_Attack( bs->client ); + } + // + return qtrue; + } + // + if ( range > 200 ) { + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + } + // + // MOVEMENT REQUIRED + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, targetpos ); + VectorSubtract( targetpos, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, targetpos ) ) { + VectorSubtract( targetpos, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, targetpos ); + VectorSubtract( targetpos, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_PanzerTarget() +================== +*/ +void AIEnter_MP_PanzerTarget( bot_state_t *bs ) { + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_PanzerTarget; + bs->ainodeText = "AINode_MP_PanzerTarget"; + bs->enemy = ENTITYNUM_WORLD; // fast view +} + +/* +================== +AINode_MP_PanzerTarget() +================== +*/ +int AINode_MP_PanzerTarget( bot_state_t *bs ) { + vec3_t vec; + // + if ( BotIsDead( bs ) ) { + bs->enemy = -1; + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + // + if ( !BotWeaponWantScale( bs, WP_PANZERFAUST ) ) { + bs->enemy = -1; + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + + bs->weaponnum = WP_PANZERFAUST; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + + // + // have we fired already? + if ( !BotWeaponCharged( bs, bs->weaponnum ) ) { + bs->enemy = -1; + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + + // look at the target + VectorSubtract( bs->target_goal.origin, bs->eye, vec ); + VectorNormalize( vec ); + vectoangles( vec, bs->ideal_viewangles ); + // + if ( ( bs->cur_ps.weapon == bs->weaponnum ) + && ( AngleDifference( bs->ideal_viewangles[YAW], bs->viewangles[YAW] ) < 0.5 ) + && ( AngleDifference( bs->ideal_viewangles[PITCH], bs->viewangles[PITCH] ) < 0.5 ) ) { + trap_EA_Attack( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_AttackTarget() +================== +*/ +void AIEnter_MP_AttackTarget( bot_state_t *bs ) { + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_AttackTarget; + bs->ainodeText = "AINode_MP_AttackTarget"; + bs->enemy = bs->target_goal.entitynum; // fast view +} + +/* +================== +AINode_MP_AttackTarget() +================== +*/ +int AINode_MP_AttackTarget( bot_state_t *bs ) { + vec3_t vec; + bot_goal_t goal; + gentity_t *check; + // + goal = bs->target_goal; + bs->weaponnum = BotBestTargetWeapon( bs, goal.entitynum ); + if ( bs->weaponnum == WP_NONE ) { + bs->enemy = -1; + BotDefaultNode( bs ); + return qfalse; + } + // + if ( BotIsDead( bs ) ) { + bs->enemy = -1; + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + bs->enemy = -1; + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + bs->enemy = -1; + AIEnter_MP_Intermission( bs ); + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + bs->enemy = -1; + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // + check = BotGetVisibleDamagableScriptMover( bs ); + if ( !check || ( check->s.number != goal.entitynum ) ) { + bs->enemy = -1; + BotDefaultNode( bs ); + return qfalse; + } + // + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // look at the target + VectorSubtract( bs->target_goal.origin, bs->eye, vec ); + VectorNormalize( vec ); + vectoangles( vec, bs->ideal_viewangles ); + // + if ( ( bs->cur_ps.weapon == bs->weaponnum ) + && ( AngleDifference( bs->ideal_viewangles[YAW], bs->viewangles[YAW] ) < 0.5 ) + && ( AngleDifference( bs->ideal_viewangles[PITCH], bs->viewangles[PITCH] ) < 0.5 ) ) { + if ( bs->cur_ps.weapon == WP_GRENADE_LAUNCHER || bs->cur_ps.weapon == WP_GRENADE_PINEAPPLE ) { + if ( BotSinglePlayer() || BotCoop() ) { + // release immediately in single player + } else if ( bs->cur_ps.grenadeTimeLeft ) { + // release grenade + } else { + // hold onto it + trap_EA_Attack( bs->client ); + } + } else { + trap_EA_Attack( bs->client ); + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_FixMG42() +================== +*/ +void AIEnter_MP_FixMG42( bot_state_t *bs ) { +//level.clients[0].sess.spectatorClient = bs->client; + bs->arrive_time = level.time; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_FixMG42; + bs->ainodeText = "AINode_MP_FixMG42"; +} + +/* +================== +AINode_MP_FixMG42() +================== +*/ +int AINode_MP_FixMG42( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + trace_t tr; + // + goal = bs->target_goal; + ent = BotGetEntity( bs->target_goal.entitynum ); + // return to this sniper spot if we go off temporarily + ent->botIgnoreTime = 0; + // + if ( ent->melee->takedamage ) { + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + ent->botIgnoreTime = level.time + 5000; // other bots should avoid this spot + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + // return to this sniper spot after we're done + ent->botIgnoreTime = 0; + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + // return to this sniper spot after we're done + ent->botIgnoreTime = 0; + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // have we been waiting here for too long? + if ( bs->arrive_time < level.time - 40000 ) { + ent->botIgnoreTime = level.time + 5000; // other bots should avoid this spot + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + // + // other bots should avoid this spot + ent->botIgnoreTime = level.time + 5000; + + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 100 ) { + dir[2] = 0; + } + + // is the destination blocked? + if ( VectorLengthSquared( dir ) < SQR( 64 ) ) { + trap_Trace( &tr, ent->s.origin, NULL, NULL, ent->s.origin, bs->client, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + } + + if ( VectorLengthSquared( dir ) > SQR( 8 ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + bs->arrive_time = level.time; // wait a bit longer + + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + + BotAIBlocked( bs, &moveresult, qtrue ); + + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { //if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + if ( VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 32 ) ) { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( VectorLengthSquared( moveresult.movedir ) ) { //FIXME: look at cluster portals? + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } + } + + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 16 ) ) { + // We are at the spot, so start fixing + bs->weaponnum = WP_PLIERS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + if ( VectorDistanceSquared( bs->origin, bs->enemyorigin ) < SQR( 512 ) ) { + // if they are real close, abort sniper mode + if ( VectorDistanceSquared( bs->origin, g_entities[bs->enemy].r.currentOrigin ) < SQR( 1024 ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } else { + bs->enemy = -1; + } + } + } else { + // face the mg42 and start repairing + VectorSubtract( ent->melee->r.currentOrigin, bs->eye, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + trap_EA_Attack( bs->client ); + // dont abort until finished + bs->arrive_time = level.time; + } + } else if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { // reload? + trap_EA_Reload( bs->client ); + } + + return qtrue; +} + +/* +================== +AIEnter_MP_Battle_MobileMG42 +================== +*/ +void AIEnter_MP_Battle_MobileMG42( bot_state_t *bs ) { + BotDebugViewClient( bs->client ); + // + bs->arrive_time = level.time; + bs->lasthealth = g_entities[bs->client].health; + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_Battle_MobileMG42; + bs->ainodeText = "AINode_MP_Battle_MobileMG42"; +} + +/* +================== +AINode_MP_Battle_MobileMG42() +================== +*/ +int AINode_MP_Battle_MobileMG42( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t dir, ang; + int tookDamage = 0; + + bs->weaponnum = WP_MOBILE_MG42; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + bs->mobileMG42ProneTime = level.time; + + // if our health has dropped, abort + tookDamage = bs->lasthealth - g_entities[bs->client].health; +// bs->lasthealth = g_entities[bs->client].health; + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + // if we have run out of ammo + if ( !BotGotEnoughAmmoForWeapon( bs, bs->weaponnum ) ) { + BotDefaultNode( bs ); + return qtrue; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // look for something better to do + if ( level.captureFlagMode && BotFlagAtBase( bs->sess.sessionTeam, NULL ) == qfalse ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + // have we been waiting here for too long? or been hit too much? + if ( tookDamage > 20 || g_entities[bs->client].health < 40 || bs->arrive_time < level.time - 5000 ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + BotAimAtEnemy( bs ); + // wait until we are facing them correctly before going prone + if ( !( bs->cur_ps.eFlags & EF_PRONE ) && ( fabs( AngleDifference( bs->ideal_viewangles[YAW], bs->viewangles[YAW] ) ) > 5.0f ) ) { + return qtrue; + } + + // stay prone + trap_EA_Prone( bs->client ); + + // look for enemies + // if we have an enemy, make sure they are still within view limits + if ( bs->enemy >= 0 ) { + if ( !BotEntityWithinView( bs, bs->enemy ) ) { + bs->enemy = -1; + } + } + + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + + if ( bs->enemy < 0 ) { + // if we still have no enemy, then after a slight pause, allow us to escape prone mode if we find an + // enemy outside our view + if ( bs->arrive_time < level.time - 1500 ) { + BotFindEnemyMP( bs, -1, qtrue ); + // if we found one this time, escape prone mode + if ( bs->enemy >= 0 ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } + } + + // if we took damage, check that there isn't a closer enemy we can attack + if ( tookDamage > 0 ) { + int oldEnemy; + // + oldEnemy = bs->enemy; + bs->enemy = -1; + BotFindEnemyMP( bs, -1, qtrue ); + if ( bs->enemy >= 0 && bs->enemy != oldEnemy ) { + // found someone else to attack + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + // otherwise continue + bs->enemy = oldEnemy; + } + + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + + if ( bs->enemy >= 0 ) { + VectorSubtract( entinfo.origin, bs->eye, dir ); + VectorNormalize( dir ); + vectoangles( dir, ang ); + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 120, bs->enemy, NULL ) ) { + // if they are real close, abort mg42 mode + if ( VectorDistanceSquared( bs->origin, g_entities[bs->enemy].r.currentOrigin ) < SQR( 400 ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + // + bs->arrive_time = level.time; // wait a bit longer + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + trap_EA_Attack( bs->client ); + } else { + bs->enemy = -1; + } + } + } else { +// int spotNum; + // + // TODO: cycle through visible enemy sniper spots + // NOTE: remember last visible enemy positions, so we can stay on them longer than usual + if ( bs->enemyposition_time > trap_AAS_Time() - 5.0 ) { + VectorSubtract( bs->enemyorigin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( bs->viewchangetime > level.time ) { + // use same angles +/* } else if ((spotNum = BotGetRandomVisibleSniperSpot( bs )) > -1) { + // look at new spot + VectorSubtract( g_entities[spotNum].s.origin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // + bs->viewchangetime = level.time + 1000 + rand()%1500; +*/ } else { + // use mg42 angles + VectorCopy( level.clients[bs->client].pmext.mountedWeaponAngles, bs->ideal_viewangles ); + // add some random angle + bs->ideal_viewangles[YAW] += crandom() * 20.f * 0.45; + bs->ideal_viewangles[PITCH] += crandom() * 20.f * 0.15; + // + bs->viewchangetime = level.time + 1000 + rand() % 1500; + } + } + + // + // stay mounted + bs->flags &= ~BFL_DISMOUNT_MG42; + + return qtrue; +} + +/* +================== +AIEnter_MP_MG42Scan() +================== +*/ +void AIEnter_MP_MG42Scan( bot_state_t *bs ) { +//level.clients[0].sess.spectatorClient = bs->client; + bs->arrive_time = level.time; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->lasthealth = g_entities[bs->client].health; + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_MG42Scan; + bs->ainodeText = "AINode_MP_MG42Scan"; +} + +/* +================== +AINode_MP_MG42Scan() +================== +*/ +int AINode_MP_MG42Scan( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t dir, ang; + gentity_t *ent, *mg42; + // + // set the dismount flag. clear it if we want to stay on + bs->flags |= BFL_DISMOUNT_MG42; + // + goal = bs->target_goal; + ent = BotGetEntity( bs->target_goal.entitynum ); + mg42 = ent->melee; + // + ent->botIgnoreTime = 0; // set this now, only reset it to ignore if we want to ignore it + // + // if it's the wrong one + if ( VectorDistanceSquared( bs->origin, ent->r.currentOrigin ) > SQR( 64 ) ) { + trap_EA_Activate( bs->client ); + return qfalse; + } + // return to this sniper spot if we go off temporarily + ent->botIgnoreTime = 0; + // if our health has dropped, abort + if ( bs->lasthealth > g_entities[bs->client].health + 40 ) { + ent->botIgnoreTime = level.time + 5000; + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // if we are not mounted on an mg42 + if ( !( g_entities[bs->client].s.eFlags & EF_MG42_ACTIVE ) ) { + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + // return to this sniper spot after we're done + ent->botIgnoreTime = 0; + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + // return to this sniper spot after we're done + ent->botIgnoreTime = 0; + // + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } +/* // check for special class actions + if (BotCheckClassActions(bs)) { + // return to this sniper spot after we're done + ent->missionLevel = 0; + return qfalse; + } +*/ // look for something better to do + if ( BotFlagAtBase( bs->sess.sessionTeam, NULL ) == qfalse ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // have we been waiting here for too long? + if ( bs->arrive_time < level.time - 40000 ) { + ent->missionLevel = level.time; // other bots should avoid this spot + ent->botIgnoreTime = level.time + 20000; // we should ignore it for a while now + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // if this spot is disabled now + if ( mg42->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // + // other bots should avoid this spot + if ( ent->botIgnoreTime < level.time + 5000 ) { + ent->botIgnoreTime = level.time + 5000; + } + // + // look for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + VectorSubtract( entinfo.origin, mg42->r.currentOrigin, dir ); + VectorNormalize( dir ); + vectoangles( dir, ang ); + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 120, bs->enemy, NULL ) ) { + + if ( ( fabs( AngleDifference( ang[PITCH], mg42->s.angles[PITCH] ) ) >= mg42->varc ) + || ( fabs( AngleDifference( ang[YAW], mg42->s.angles[YAW] ) ) >= mg42->harc ) ) { + ent->botIgnoreTime = level.time + 5000; + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + + // if they are real close, abort mg42 mode + if ( VectorDistanceSquared( bs->origin, g_entities[bs->enemy].r.currentOrigin ) < SQR( 512 ) ) { + ent->botIgnoreTime = level.time + 5000; + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + + // + bs->arrive_time = level.time; // wait a bit longer + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + trap_EA_Attack( bs->client ); + } else { + bs->enemy = -1; + } + } + } else { +// int spotNum; + // + // TODO: cycle through visible enemy sniper spots + // NOTE: remember last visible enemy positions, so we can stay on them longer than usual + if ( bs->enemyposition_time > trap_AAS_Time() - 5.0 ) { + VectorSubtract( bs->enemyorigin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( bs->viewchangetime > level.time ) { + // use same angles +/* } else if ((spotNum = BotGetRandomVisibleSniperSpot( bs )) > -1) { + // look at new spot + VectorSubtract( g_entities[spotNum].s.origin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // + bs->viewchangetime = level.time + 1000 + rand()%1500; +*/ } else { + vec3_t start, end; + trace_t tr; + // use mg42 angles + VectorCopy( mg42->s.angles, bs->ideal_viewangles ); + // add some random angle + bs->ideal_viewangles[YAW] += crandom() * mg42->harc * 0.45; + bs->ideal_viewangles[PITCH] += crandom() * mg42->varc * 0.15; + // + // trace out to get the ground + AngleVectors( bs->ideal_viewangles, dir, NULL, NULL ); + VectorMA( mg42->r.currentOrigin, 48, dir, start ); + VectorMA( start, 4096, dir, end ); + trap_Trace( &tr, start, NULL, NULL, end, bs->client, MASK_SHOT ); + if ( tr.fraction > 0.2 ) { + VectorCopy( tr.endpos, start ); + VectorCopy( tr.endpos, end ); + end[2] -= 1024 * tr.fraction; + trap_Trace( &tr, start, NULL, NULL, end, bs->client, MASK_SHOT ); + VectorSubtract( tr.endpos, bs->eye, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + // + bs->viewchangetime = level.time + 1000 + rand() % 1500; + } + } + // + // stay mounted + bs->flags &= ~BFL_DISMOUNT_MG42; + // + return qtrue; +} + +/* +================== +AIEnter_MP_MG42Mount() +================== +*/ +void AIEnter_MP_MG42Mount( bot_state_t *bs ) { +//level.clients[0].sess.spectatorClient = bs->client; + bs->arrive_time = level.time; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_MG42Mount; + bs->ainodeText = "AINode_MP_MG42Mount"; +} + +/* +================== +AINode_MP_MG42Mount() +================== +*/ +int AINode_MP_MG42Mount( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + trace_t tr; + + goal = bs->target_goal; + ent = BotGetEntity( bs->target_goal.entitynum ); + // return to this sniper spot if we go off temporarily + ent->botIgnoreTime = 0; + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // if we have mounted an mg42 + if ( g_entities[bs->client].s.eFlags & EF_MG42_ACTIVE ) { + // if it's the wrong one + if ( VectorDistanceSquared( bs->origin, ent->r.currentOrigin ) > SQR( 64 ) ) { + trap_EA_Activate( bs->client ); + return qfalse; + } + AIEnter_MP_MG42Scan( bs ); + return qfalse; + } + // if the mg42 is broken, or being used + if ( ent->melee->health <= 0 || ( ent->melee->entstate != STATE_DEFAULT ) || ent->melee->active ) { + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + // + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } +/* // check for special class actions + if (BotCheckClassActions(bs)) { + return qfalse; + } +*/ // look for something better to do + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + // have we been waiting here for too long? + if ( bs->arrive_time < level.time - 40000 ) { + ent->botIgnoreTime = level.time + 15000; // other bots should avoid this spot + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // + // other bots should avoid this spot + ent->botIgnoreTime = level.time + 5000; + // + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 100 ) { + dir[2] = 0; + } + // is the destination blocked? + if ( VectorLengthSquared( dir ) < SQR( 64 ) ) { + trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, ent->r.currentOrigin, bs->client, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + } + if ( VectorLengthSquared( dir ) > SQR( 8 ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + bs->arrive_time = level.time; // wait a bit longer + + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + if ( VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 32 ) ) { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + if ( VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 128 ) || + VectorDistanceSquared( bs->origin, g_entities[bs->enemy].r.currentOrigin ) > SQR( 256 ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { // go fight them + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } + + } + // + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 64 ) ) { + vec3_t forward, right, up, offset; + // look at the gun + AngleVectors( bs->viewangles, forward, right, up ); + CalcMuzzlePointForActivate( &g_entities[bs->client], forward, right, up, offset ); + VectorSubtract( ent->melee->r.currentOrigin, offset, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + if ( bs->arrive_time < level.time - 500 ) { + // randomize the angles a bit to solve some times when a direct sight isn't "enough" + if ( rand() % 4 == 0 ) { + bs->ideal_viewangles[YAW] += crandom() * 10.0; + bs->ideal_viewangles[PITCH] += crandom() * 10.0; + } + // if we have been waiting longer, move backwards slowly + if ( bs->arrive_time < level.time - 2000 ) { + VectorInverse( dir ); + dir[2] = 0; + VectorNormalize( dir ); + trap_EA_Move( bs->client, dir, 40 ); + } + } + // hit activate so we mount it + if ( rand() % 2 ) { + trap_EA_Activate( bs->client ); + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_ScanForLandmines() +================== +*/ +void AIEnter_MP_ScanForLandmines( bot_state_t *bs ) { + bs->arrive_time = level.time; + bs->altenemy = 0; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_ScanForLandmines; + bs->ainodeText = "AINode_MP_ScanForLandmines"; +} + +#define DETECT_RADIUS 225 + +/* +================== +AINode_MP_ScanForLandmines() +================== +*/ +int AINode_MP_ScanForLandmines( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + trace_t tr; + + goal = bs->target_goal; + ent = &g_entities[bs->target_goal.entitynum]; + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + ent->missionLevel = level.time; + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + ent->missionLevel = level.time; + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + ent->missionLevel = level.time; + return qfalse; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + ent->missionLevel = level.time; + return qfalse; + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &bs->target_goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + ent->missionLevel = level.time; + return qfalse; + } + + // is this spot disabled now? + if ( ent->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + if ( BotFindSpecialGoals( bs ) ) { + ent->missionLevel = level.time; + return qfalse; + } + } + + // is the destination blocked? + trap_Trace( &tr, ent->s.origin, NULL, NULL, ent->s.origin, bs->client, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + if ( BotFindSpecialGoals( bs ) ) { + ent->missionLevel = level.time; + return qfalse; + } + } + + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 16 ) { + dir[2] = 0; + } + + if ( VectorLengthSquared( dir ) > SQR( 32 ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + bs->arrive_time = level.time; // wait a bit longer + + //initialize the movement state + BotSetupForMovement( bs ); + + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + + BotAIBlocked( bs, &moveresult, qtrue ); + + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { //if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + if ( VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 32 ) ) { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( VectorLengthSquared( moveresult.movedir ) ) { //FIXME: look at cluster portals? + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } + } + + if ( VectorLengthSquared( dir ) < SQR( 32 ) ) { + // start zooming if we aren't already + bs->weaponnum = WP_BINOCULARS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + + if ( bs->altenemy <= 0 ) { + vec3_t target; + + if ( bs->target_goal.number >= 3 ) { + BotChooseWeapon( bs ); + BotDefaultNode( bs ); + ent->missionLevel = level.time; + return qfalse; + } + + // we're going to modify the point we're looking at, based on the current counter number + VectorCopy( ent->s.origin2, target ); + + // if our index is 0 or 2, we move it left + switch ( bs->target_goal.number ) { + case 0: + target[1] += DETECT_RADIUS; + break; + case 1: + target[0] += DETECT_RADIUS; + break; + case 2: + target[0] -= DETECT_RADIUS; + target[1] -= DETECT_RADIUS; + break; + } + + // look at the proper spot + VectorSubtract( target, bs->eye, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + + bs->altenemy = AngleNormalize360( RAD2DEG( 2 * tan( DETECT_RADIUS / VectorDistance( bs->eye, target ) ) ) ); + + // set the time of our next view update + bs->viewchangetime = level.time + 100 + rand() % 150; + + // next target! + bs->target_goal.number++; + } else if ( bs->viewchangetime < level.time ) { + // we're doing a sweep - on the 2nd pass we sweep the reverse direction of the other 2 passes + + // move viewangle by a bit + switch ( bs->target_goal.number ) { + case 2: + bs->ideal_viewangles[1]--; + break; + default: + bs->ideal_viewangles[1]++; + break; + } + + bs->altenemy--; + + // set the time of our next view update + bs->viewchangetime = level.time + 100 + rand() % 150; + } + } + + return qtrue; +} + +/* +================== +AIEnter_MP_SniperSpot() +================== +*/ +void AIEnter_MP_SniperSpot( bot_state_t *bs ) { + bs->arrive_time = level.time; + bs->enemyposition_time = 0; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_SniperSpot; + bs->ainodeText = "AINode_MP_SniperSpot"; +} + +/* +================== +AINode_MP_SniperSpot() +================== +*/ +int AINode_MP_SniperSpot( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + trace_t tr; + + goal = bs->target_goal; + ent = &g_entities[bs->target_goal.entitynum]; + + // return to this sniper spot if we go off temporarily + ent->missionLevel = 0; + bs->flags |= BFL_SNIPING; + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + ent->missionLevel = level.time; // other bots should avoid this spot + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + // return to this sniper spot after we're done + ent->missionLevel = 0; + return qfalse; + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + // return to this sniper spot after we're done + ent->missionLevel = 0; + // + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // look for something better to do + if ( !BotFlagAtBase( bs->sess.sessionTeam, NULL ) ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + // have we got enough ammo? + if ( !BotCanSnipe( bs, qfalse ) ) { + if ( !BotFindSpecialGoals( bs ) ) { + BotDefaultNode( bs ); + } + return qfalse; + } + + // is this spot disabled now? + if ( ent->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + // + // other bots should avoid this spot + ent->missionLevel = level.time; + + // is the destination blocked? + trap_Trace( &tr, ent->s.origin, NULL, NULL, ent->s.origin, bs->client, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 100 ) { + dir[2] = 0; + } + + if ( VectorLengthSquared( dir ) > SQR( 32 ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + bs->arrive_time = level.time; // wait a bit longer + + //initialize the movement state + BotSetupForMovement( bs ); + + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + + BotAIBlocked( bs, &moveresult, qtrue ); + + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { //if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + if ( VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 32 ) ) { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + bs->enemyposition_time = 0; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( VectorLengthSquared( moveresult.movedir ) ) { //FIXME: look at cluster portals? + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } + } + + if ( VectorLengthSquared( dir ) < SQR( 32 ) ) { + // have we been waiting here for too long? + if ( bs->arrive_time < level.time - 40000 ) { + ent->missionLevel = level.time; // other bots should avoid this spot + if ( !BotFindSpecialGoals( bs ) ) { + BotDefaultNode( bs ); + } + return qfalse; + } + + // Gordon: crouching spot + if ( ent->spawnflags & 2 ) { + trap_EA_Crouch( bs->client ); + } + + // We are at the sniper spot, so start sniping + bs->weaponnum = BotCanSnipe( bs, qfalse ); + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + + //if the enemy is dead + if ( EntityIsDead( &entinfo ) ) { + bs->enemy = -1; + } + + if ( bs->enemy >= 0 ) { + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 50, bs->enemy, NULL ) ) { + // if they are real close, abort sniper mode + if ( VectorDistanceSquared( bs->origin, g_entities[bs->enemy].r.currentOrigin ) < SQR( 512 ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + + bs->arrive_time = level.time; // wait a bit longer + + // remember this position + bs->enemyposition_time = trap_AAS_Time(); + VectorCopy( BotGetOrigin( bs->enemy ), bs->enemyorigin ); + + //aim at the enemy + BotAimAtEnemy( bs ); + + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon && BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 5, bs->enemy, NULL ) ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } else { + int spotNum; + + if ( bs->arrive_time < level.time - 20000 ) { + // if not suggesting sniper, suicide so we can change + if ( !BG_IsScopedWeapon( BotSuggestWeapon( bs, bs->sess.sessionTeam ) ) ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + } + + // NOTE: remember last visible enemy positions, so we can stay on them longer than usual + if ( bs->enemyposition_time > trap_AAS_Time() - 5.0 ) { + VectorSubtract( bs->enemyorigin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( bs->viewchangetime > level.time ) { + // + // use same angles + // + } else if ( !( rand() % ( 1 + BotGetNumVisibleSniperSpots( bs ) ) ) && ( spotNum = BotGetRandomVisibleSniperSpot( bs ) ) > -1 ) { + // look at new spot + VectorSubtract( g_entities[spotNum].s.origin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // + bs->viewchangetime = level.time + 1500 + rand() % 3000; + } else if ( rand() % 2 && bs->enemyposition_time && ( bs->enemyposition_time > trap_AAS_Time() - 60.0 ) ) { + // look at last enemy pos + VectorSubtract( bs->enemyorigin, bs->origin, dir ); + // if it's not too far away, use non-zoomed view + if ( VectorNormalize( dir ) < 3000 ) { + // switch to non-zoomed view + if ( bs->weaponnum >= WP_BEGINSECONDARY && bs->weaponnum <= WP_LASTSECONDARY ) { + bs->weaponnum = weapAlts[bs->weaponnum]; + } + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + } + vectoangles( dir, bs->ideal_viewangles ); + + bs->viewchangetime = level.time + 2000 + rand() % 2000; + } else if ( !VectorCompare( ent->s.angles, vec3_origin ) ) { + // just face direction of sniper spot + // switch to non-zoomed view + if ( bs->weaponnum >= WP_BEGINSECONDARY && bs->weaponnum <= WP_LASTSECONDARY ) { + bs->weaponnum = weapAlts[bs->weaponnum]; + } + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + VectorCopy( ent->s.angles, bs->ideal_viewangles ); + // + bs->viewchangetime = level.time + 2500 + rand() % 3000; + } + } + } + + // reload? + if ( ( level.time - bs->last_fire > 2000 ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + + return qtrue; +} + +/* +================== +AIEnter_MP_DefendTarget() +================== +*/ +void AIEnter_MP_DefendTarget( bot_state_t *bs ) { + //VectorCopy( bs->origin, bs->aimtarget ); + //choose the best weapon to fight with + bs->arrive_time = 0; + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_DefendTarget; + bs->ainodeText = "AINode_MP_DefendTarget"; +} + +/* +================== +AINode_MP_DefendTarget() +================== +*/ +int AINode_MP_DefendTarget( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent, *flag; + qboolean move; + trace_t tr; + + goal = bs->target_goal; + bs->defendgoal = bs->target_goal; + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + +/* // check for special class actions + if (BotCheckClassActions(bs)) { + return qfalse; + } +*/ + + // look for health/ammo packs + if ( BotFindNearbyGoal( bs ) ) { + AIEnter_MP_Seek_NBG( bs ); + return qfalse; + } + + + if ( goal.entitynum < 0 ) { + BotDefaultNode( bs ); + return qfalse; + } + + // look for better goals + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + + + // if we are defending a leader, and they are not valid anymore, then stop + if ( goal.flags & GFL_LEADER ) { + // make sure our leader is still valid + if ( bs->leader > -1 ) { + if ( !g_entities[bs->leader].inuse || + !g_entities[bs->leader].client || + ( g_entities[bs->leader].client->ps.pm_flags & PMF_LIMBO ) ) { + bs->leader = -1; + BotDefaultNode( bs ); + return qfalse; + } else if ( bs->sess.playerType == PC_MEDIC ) { + if ( g_entities[bs->leader].health <= 0 && trap_AAS_PointAreaNum( g_entities[bs->leader].r.currentOrigin ) && + BotGoalForEntity( bs, bs->leader, &bs->target_goal, BGU_MEDIUM ) ) { + // revive + g_entities[bs->target_goal.entitynum].missionLevel = level.time + 3000; + AIEnter_MP_MedicRevive( bs ); + return qfalse; + } else if ( BotHealthScale( bs->leader ) <= 0.7 && trap_AAS_PointAreaNum( g_entities[bs->leader].r.currentOrigin ) && + BotGoalForEntity( bs, bs->leader, &bs->target_goal, BGU_MEDIUM ) ) { // health stock? + // make this our goal + g_entities[bs->target_goal.entitynum].missionLevel = level.time + 3000; + AIEnter_MP_MedicGiveHealth( bs ); + return qfalse; + } + } else if ( ( VectorLengthSquared( g_entities[bs->leader].client->ps.velocity ) < SQR( 10 ) ) && ( VectorLengthSquared( bs->cur_ps.velocity ) < SQR( 10 ) ) ) { + if ( !( g_entities[bs->leader].r.svFlags & SVF_BOT ) ) { + BotVoiceChatAfterIdleTime( bs->client, "WhereTo", SAY_BUDDY, 1000 + rand() % 3000, BOT_SHOWTEXT, 12000, qfalse ); + } + } + // + if ( !g_entities[bs->leader].inuse || + g_entities[bs->leader].health <= 0 || + VectorDistanceSquared( g_entities[bs->leader].r.currentOrigin, bs->origin ) > SQR( MAX_BOTLEADER_DIST ) ) { + bs->leader = -1; + } + } + // + if ( bs->leader == -1 ) { + BotDefaultNode( bs ); + return qfalse; + } + } + + + // + //if there is an enemy + if ( BotFindEnemyMP( bs, -1, qfalse ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // if we made it to the destination, let us roam to fight people + if ( BotCarryingFlag( bs->enemy ) || ( !( goal.flags & GFL_LEADER ) && ( bs->flags & BFL_MISCFLAG ) ) ) { + if ( !BotCarryingFlag( bs->enemy ) && VectorLengthSquared( bs->cur_ps.velocity ) && BotWantsToRetreat( bs ) ) { + //keep the current long term goal and retreat + } else { + trap_BotResetLastAvoidReach( bs->ms ); + //go fight + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } + } + + + // if the target has been cleared + if ( goal.entitynum < 0 ) { + BotDefaultNode( bs ); + return qfalse; + } + + // should we stop pursuing this target? + ent = &g_entities[bs->target_goal.entitynum]; + switch ( ent->s.eType ) { + case ET_TRIGGER_FLAGONLY: + if ( BotFlagAtBase( bs->sess.sessionTeam, &flag ) == qtrue ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + break; + case ET_TRIGGER_FLAGONLY_MULTIPLE: + if ( !BotEnemyCarryingFlag( bs->client ) ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + break; + case ET_ITEM: + if ( !Q_stricmp( ent->classname, "team_CTF_redflag" ) && !( ent->flags & FL_DROPPED_ITEM ) ) { + if ( bs->sess.sessionTeam != TEAM_AXIS ) { + // this is the enemy flag, so abort "defending" it when it shows up + if ( BotFlagAtBase( TEAM_AXIS, NULL ) == qtrue ) { + BotDefaultNode( bs ); + return qfalse; + } else if ( BotCarryingFlag( bs->client ) ) { // we have it! + BotDefaultNode( bs ); + return qfalse; + } + } + } else if ( !Q_stricmp( ent->classname, "team_CTF_blueflag" ) && !( ent->flags & FL_DROPPED_ITEM ) ) { + if ( bs->sess.sessionTeam != TEAM_ALLIES ) { + // this is the enemy flag, so abort "defending" it when it shows up + if ( BotFlagAtBase( TEAM_ALLIES, NULL ) == qtrue ) { + BotDefaultNode( bs ); + return qfalse; + } else if ( BotCarryingFlag( bs->client ) ) { // we have it! + BotDefaultNode( bs ); + return qfalse; + } + } + } else if ( ent->touch ) { + if ( ent->r.svFlags & SVF_NOCLIENT ) { + BotDefaultNode( bs ); + return qfalse; + } + } + break; + case ET_TRAP: + if ( !Q_stricmp( ent->classname, "team_WOLF_checkpoint" ) ) { + if ( ent->count == ( bs->sess.sessionTeam == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS ) ) { + // the enemy is controlling this checkpoint now + BotDefaultNode( bs ); + return qfalse; + } + } + break; + case ET_TRIGGER_MULTIPLE: + // if we are within range, stop here + if ( bs->arrive_time < level.time - 5000 ) { + if ( ( VectorDistanceSquared( bs->origin, BotGetOrigin( ent->s.number ) ) < SQR( 600 ) ) + && trap_InPVS( bs->origin, BotGetOrigin( ent->s.number ) ) ) { + // check the trace + trap_Trace( &tr, bs->eye, vec3_origin, vec3_origin, BotGetOrigin( ent->s.number ), -1, (MASK_SHOT) &~( CONTENTS_BODY | CONTENTS_CORPSE ) ); + if ( tr.entityNum != ENTITYNUM_WORLD ) { + VectorCopy( bs->origin, bs->target_goal.origin ); + goal = bs->target_goal; + bs->arrive_time = level.time; + } + } + } + break; + case ET_PLAYER: + if ( ent->client && ent->health <= 0 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + break; + case ET_ATTRACTOR_HINT: + default: // rain + break; + } + + + // if we are defending a player, and they have moved, get a new sparse defend area + if ( ent->client && ( bs->target_goal.number < level.time - 300 ) && !VectorCompare( ent->r.currentOrigin, bs->defendgoal_origin ) ) { + int list[MAX_CLIENTS], numList; + float ourDist, *distances; + + // + // if there are too many defenders, and we are the furthest away, stop defending + if ( BotCarryingFlag( bs->target_goal.entitynum ) ) { + if ( ( numList = BotNumTeamMatesWithTarget( bs, goal.entitynum, list, MAX_CLIENTS ) ) > BOT_FLAG_CARRIER_DEFENDERS ) { + ourDist = VectorDistanceSquared( bs->origin, g_entities[goal.entitynum].r.currentOrigin ); + if ( !trap_InPVS( bs->origin, g_entities[goal.entitynum].r.currentOrigin ) ) { + ourDist += 2048 * 2048; + } + distances = BotSortPlayersByDistance( g_entities[goal.entitynum].r.currentOrigin, list, numList ); + if ( distances[numList - 1] < ourDist ) { + // we are the furthest + bs->ignore_specialgoal_time = 0; + bs->leader = -1; + BotDefaultNode( bs ); + return qfalse; + } + } + } + + // look for a new defend pos + VectorCopy( ent->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.areanum = BotPointAreaNum( ent->s.number, ent->r.currentOrigin ); +// BotFindSparseDefendArea( bs, &bs->target_goal, qtrue ); + VectorCopy( ent->r.currentOrigin, bs->defendgoal_origin ); + bs->target_goal.number = level.time + rand() % 100; + goal = bs->target_goal; + bs->defendgoal = bs->target_goal; + } + + // + // remember the last position we couldnt see the DESTINATION from, so we can look there once we arrive + if ( !trap_InPVS( bs->origin, goal.origin ) ) { + VectorCopy( bs->origin, bs->aimtarget ); + } + + // + // do we need to get closer to the goal? + // + move = qfalse; + if ( !( bs->target_goal.flags & GFL_DEFEND_CLOSE ) ) { + if ( !move && VectorDistanceSquared( bs->origin, goal.origin ) > SQR( 384 ) ) { + move = qtrue; + } + if ( !move && trap_InPVS( bs->origin, goal.origin ) ) { + trace_t tr; + trap_Trace( &tr, bs->origin, NULL, NULL, goal.origin, -1, MASK_SHOT & ~( CONTENTS_BODY | CONTENTS_CORPSE ) ); + if ( tr.startsolid || tr.allsolid || tr.fraction < 1.0 ) { + move = qtrue; + } + } + } else { + if ( !move && VectorDistanceSquared( bs->origin, goal.origin ) > ( ( goal.flags & GFL_LEADER ) ? SQR( 80 ) : SQR( 32 ) ) ) { + move = qtrue; + } + } + + if ( move ) { + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + + //if the movement failed + if ( moveresult.failure ) { + if ( goal.flags & GFL_LEADER ) { + bs->leader = -1; + } + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + //trap_EA_Jump(bs->client); + //trap_EA_Move(bs->client, tv(crandom(), crandom(), crandom()), 100+random()*200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + + if ( bs->blockentTime > level.time - 500 && bs->blockent < level.maxclients ) { + // if we are within range, then stop here + if ( goal.flags & GFL_LEADER ) { + if ( ( BotPointWithinMovementAutonomy( bs, &goal, bs->origin ) ) + && ( VectorDistanceSquared( bs->origin, g_entities[bs->leader].r.currentOrigin ) < SQR( 512 ) ) ) { + VectorCopy( bs->origin, bs->target_goal.origin ); + bs->target_goal.areanum = bs->areanum; + } + } + } + + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { //if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + // under special circumstances, we should go into battle mode + if ( !BotCarryingFlag( bs->target_goal.entitynum ) && + ( ( bs->weaponnum == WP_LUGER ) || ( bs->weaponnum == WP_COLT ) || ( bs->weaponnum == WP_MOBILE_MG42 ) ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } else { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } + } else { + bs->enemy = -1; + } + } + } + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( VectorLengthSquared( moveresult.movedir ) ) { //FIXME: look at cluster portals? + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + // check for giving ourselves some ammo + if ( bs->sess.playerType == PC_FIELDOPS && ClientNeedsAmmo( bs->client ) ) { + // switch to regen and pump away + bs->weaponnum = WP_AMMO; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_AMMO && BotWeaponCharged( bs, WP_AMMO ) ) { + trap_EA_Attack( bs->client ); + } + } + // check for giving ourselves some health + if ( bs->sess.playerType == PC_MEDIC && BotHealthScale( bs->client ) < 1.0 ) { + // switch to regen and pump away + bs->weaponnum = WP_MEDKIT; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_MEDKIT && BotWeaponCharged( bs, WP_MEDKIT ) ) { + trap_EA_Attack( bs->client ); + } + } + } + } + + } else { + gentity_t *objtarg; + // NOTE: dont bother looking for enemies, since we have reached the destination, so we are free to + // pursue enemies now + // + // call for an engineer (if required) + if ( ( ent->s.eType == ET_OID_TRIGGER ) && ( objtarg = ent->target_ent ) ) { + if ( objtarg->spawnflags & 64 ) { + if ( ent->lastHintCheckTime < level.time && rand() % 2 ) { + // if there is no dynamite planted here + int list[10], numList, i; + // + numList = BotGetTargetExplosives( bs->sess.sessionTeam, list, 10, qfalse ); + for ( i = 0; i < numList; i++ ) { + if ( list[i] == ent->s.number ) { + break; + } + } + // if this objective doesn ont have armed dynamite + if ( numList && i < numList ) { + BotVoiceChatAfterIdleTime( bs->client, "NeedEngineer", SAY_TEAM, 500 + rand() % 4000, qfalse, 5000 + rand() % 5000, qfalse ); + } + } + // if we are an engineer, throw out an air-strike + if ( bs->sess.playerType == PC_FIELDOPS && ( level.time - bs->cur_ps.classWeaponTime > ( level.lieutenantChargeTime[bs->sess.sessionTeam - 1] * 0.5f ) ) ) { + // select smoke grenade + bs->weaponnum = WP_SMOKE_MARKER; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // look upwards + bs->ideal_viewangles[PITCH] = -70; + if ( bs->cur_ps.weapon == bs->weaponnum && bs->viewangles[PITCH] < -60 ) { + trap_EA_Attack( bs->client ); + } + return qtrue; + } + } + } + // set flag so we know that we made it to the destination + bs->flags |= BFL_MISCFLAG; + // + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } else { + if ( bs->viewchangetime < level.time ) { + // look for a better pos + if ( !bs->arrive_time && random() < 0.1 ) { + if ( bs->target_goal.entitynum ) { + BotFindSparseDefendArea( bs, &bs->target_goal, qfalse ); + } + } + if ( ++bs->viewtype > 1 ) { + bs->viewtype = 0; + } + bs->viewchangetime = level.time + 500 + rand() % 3000; + } + // if guarding a carrier, look towards the flag goal + if ( goal.entitynum > -1 && goal.entitynum < level.maxclients && BotCarryingFlag( goal.entitynum ) ) { + bot_goal_t fgoal; + gentity_t *flaggoal; + qboolean setdir = qfalse; + // + flaggoal = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !flaggoal ) { + flaggoal = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( flaggoal ) { + if ( BotGoalForEntity( NULL, flaggoal->s.number, &fgoal, BGU_HIGH ) ) { + if ( trap_BotMovementViewTarget( bs->ms, &fgoal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + setdir = qtrue; + } + } + } + + if ( !setdir ) { + // look away from the defense object + VectorAdd( g_entities[bs->target_goal.entitynum].r.absmax, g_entities[bs->target_goal.entitynum].r.absmin, target ); + VectorScale( target, 0.5, target ); + VectorSubtract( target, bs->origin, dir ); + VectorNormalize( dir ); + VectorInverse( dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[PITCH] = 0; + } + + } else if ( bs->viewtype == 0 ) { + // face the current aimtarget + VectorSubtract( bs->aimtarget, bs->origin, dir ); + if ( VectorNormalize( dir ) ) { + if ( fabs( dir[2] ) < 0.8 ) { + vectoangles( dir, bs->ideal_viewangles ); + } + bs->ideal_viewangles[PITCH] = 0; + } + } else if ( bs->target_goal.entitynum > 0 ) { + // look at the defense object + VectorAdd( g_entities[bs->target_goal.entitynum].r.absmax, g_entities[bs->target_goal.entitynum].r.absmin, target ); + VectorScale( target, 0.5, target ); + VectorSubtract( target, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[PITCH] = 0; + } + } + } + + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + + return qtrue; +} + +/* +================== +AIEnter_MP_TouchTarget() +================== +*/ +void AIEnter_MP_TouchTarget( bot_state_t *bs ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_TouchTarget; + bs->ainodeText = "AINode_MP_TouchTarget"; +} + +/* +================== +AINode_MP_TouchTarget() +================== +*/ +int AINode_MP_TouchTarget( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *ent; + qboolean altroute = qfalse; + vec3_t mins, maxs; + + goal = bs->target_goal; + if ( bs->alt_goal.number && bs->alt_goal.entitynum == goal.entitynum && bs->alt_goal.number == goal.number ) { + if ( ( bs->alt_goal.number < level.time - 12000 ) || ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ) < 700 ) ) { + // stop pursuing altgoal after some time + BotClearGoal( &bs->alt_goal ); + bs->target_goal.number = 0; + goal = bs->target_goal; + } else { + goal = bs->alt_goal; + altroute = qtrue; + } + } + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + +/* // check for special class actions + if (BotCheckClassActions(bs)) { + return qfalse; + } +*/ + + // look for health/ammo packs + if ( BotFindNearbyGoal( bs ) ) { + AIEnter_MP_Seek_NBG( bs ); + return qfalse; + } + + // + // should we stop pursuing this target? + ent = &g_entities[bs->target_goal.entitynum]; + if ( ent->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + if ( !BotFindSpecialGoals( bs ) ) { + BotDefaultNode( bs ); + } + return qfalse; + } + + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + + if ( ent->s.eType == ET_TRAP && !Q_stricmp( ent->classname, "team_WOLF_checkpoint" ) ) { + if ( ent->count == bs->sess.sessionTeam ) { + // we have captured it + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } else if ( ent->s.eType == ET_ITEM && ent->touch ) { + if ( ent->r.svFlags & SVF_NOCLIENT ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + } else { + if ( ent->s.eType == ET_TRIGGER_MULTIPLE ) { + // + if ( !ent->r.linked || ent->nextthink > level.time + 1000 ) { + BotDefaultNode( bs ); + return qfalse; + } + + // if we are currently touching the target, then use this as our spot + if ( bs->arrive_time + 3000 < level.time ) { + VectorAdd( bs->origin, bs->cur_ps.mins, mins ); + VectorAdd( bs->origin, bs->cur_ps.maxs, maxs ); + if ( trap_EntityContactCapsule( mins, maxs, ent ) ) { + VectorCopy( bs->origin, goal.origin ); + bs->arrive_time = level.time; + } + } + + // if the current destination is no longer touching the brush, get a new position + VectorAdd( goal.origin, bs->cur_ps.mins, mins ); + VectorAdd( goal.origin, bs->cur_ps.maxs, maxs ); + + if ( !trap_EntityContactCapsule( mins, maxs, ent ) || ( rand() % 50 == 0 ) ) { + // need a new position + if ( !BotGetReachableEntityArea( bs, ent->s.number, &goal ) ) { + BotDefaultNode( bs ); + return qfalse; + } + // check this position + VectorAdd( goal.origin, bs->cur_ps.mins, mins ); + VectorAdd( goal.origin, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, ent ) ) { + BotDefaultNode( bs ); + return qfalse; + } + // this is the new goal + bs->target_goal = goal; + } + } + if ( !Q_stricmp( ent->classname, "trigger_flagonly" ) ) { + // if we dont have the flag anymore, then stop + if ( !BotCarryingFlag( bs->client ) ) { + BotDefaultNode( bs ); + return qfalse; + } + } + } + // + VectorCopy( goal.origin, target ); + if ( fabs( target[2] - bs->origin[2] ) < 80 ) { + target[2] = bs->origin[2]; + } + if ( altroute && VectorDistanceSquared( goal.origin, bs->origin ) < SQR( 80 ) ) { + // made it to the altroute + BotClearGoal( &bs->alt_goal ); + bs->target_goal.number = 0; + goal = bs->target_goal; + } + + // + // if we have an enemy, then we should look for an alternate route away from them + if ( ( BotCarryingFlag( bs->client ) ) && ( goal.number < level.time - 8000 ) && + ( BotNumTeamMatesWithTarget( bs, bs->client, NULL, -1 ) < 2 ) && + ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ) > 1500 ) ) { + if ( bs->enemy > -1 ) { + aas_altroutegoal_t altroutegoals[40]; + int numList, i; + bot_goal_t tempgoal; + vec3_t edir; + // + // get the vector towards the enemy + VectorSubtract( g_entities[bs->enemy].r.currentOrigin, bs->origin, edir ); + VectorNormalize( edir ); + // if we are running towards them + if ( DotProduct( edir, bs->cur_ps.velocity ) > 0 ) { + // dont do this check again for a while + bs->alt_goal.number = level.time; + bs->target_goal.number = level.time; + //initialize the movement state + BotSetupForMovement( bs ); + // check for an alternate route + if ( ( numList = trap_AAS_AlternativeRouteGoals( bs->origin, bs->target_goal.origin, bs->tfl, altroutegoals, 40, 0 ) ) ) { + for ( i = 0; i < numList; i++ ) { + BotClearGoal( &tempgoal ); + tempgoal.areanum = altroutegoals[i].areanum; + VectorCopy( altroutegoals[i].origin, tempgoal.origin ); + if ( qtrue ) { //trap_BotMovementViewTarget(bs->ms, &goal, bs->tfl, 300, target)) { + //VectorSubtract(target, bs->origin, dir); + VectorSubtract( tempgoal.origin, bs->origin, dir ); + VectorNormalize( dir ); + if ( DotProduct( dir, edir ) < 0 ) { // moving away from the enemy + //make this the altroutegoal + bs->alt_goal = bs->target_goal; + trap_AAS_AreaWaypoint( altroutegoals[i].areanum, bs->alt_goal.origin ); + bs->alt_goal.areanum = trap_AAS_PointAreaNum( bs->alt_goal.origin ); + break; + } + } + } + } + } + } else if ( bs->enemy < 0 ) { + int list[MAX_CLIENTS], numList; + int i, t; + bot_state_t *tbs; + // if we have defenders, stop every now and then to let them get infront of us + if ( ( bs->stand_time < level.time - 3000 ) && ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ) > 600 ) && + ( numList = BotNumTeamMatesWithTarget( bs, bs->client, list, MAX_CLIENTS ) ) ) { + // if one of them has a goal infront of us, and they are not near it, we should pause for a bit + for ( i = 0; i < numList; i++ ) { + tbs = &botstates[list[i]]; + // + if ( ( VectorDistanceSquared( tbs->origin, tbs->defendgoal.origin ) < SQR( 1500 ) ) && + ( VectorDistanceSquared( tbs->origin, bs->origin ) < SQR( 1024 ) ) && + ( trap_InPVS( tbs->origin, bs->origin ) ) && + ( trap_AAS_AreaTravelTimeToGoalArea( tbs->defendgoal.areanum, tbs->defendgoal.origin, goal.areanum, bs->tfl ) < trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ) ) && + ( trap_AAS_AreaTravelTimeToGoalArea( tbs->areanum, tbs->origin, goal.areanum, bs->tfl ) - trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, goal.areanum, bs->tfl ) ) ) { + t = trap_AAS_AreaTravelTimeToGoalArea( tbs->areanum, tbs->origin, tbs->defendgoal.areanum, bs->tfl ); + if ( t > 300 ) { + t = 300; + } + if ( t > 100 ) { + bs->stand_time = level.time + 1000 + 5 * t; + } + } + } + } + } + } + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + + //initialize the movement state + BotSetupForMovement( bs ); + // + if ( bs->stand_time < level.time ) { + qboolean move = qtrue; + //move towards the goal + if ( ent->s.eType == ET_TRAP && !Q_stricmp( ent->classname, "team_WOLF_checkpoint" ) + && ( VectorDistanceSquared( goal.origin, bs->origin ) < SQR( 128 ) ) ) { + // move straight torward it + VectorAdd( ent->r.absmin, ent->r.absmax, target ); + VectorScale( target, 0.5, target ); + VectorSubtract( target, bs->origin, dir ); + dir[2] = 0; + VectorNormalize( dir ); + trap_EA_Move( bs->client, dir, 300 ); + // randomly jump + if ( rand() % 10 == 0 ) { + trap_EA_Jump( bs->client ); + } + move = qfalse; + } else if ( ent->s.eType == ET_TRIGGER_MULTIPLE ) { + int i; + // if we are touching the brush, we dont need to move + VectorAdd( bs->origin, bs->cur_ps.mins, mins ); + VectorAdd( bs->origin, bs->cur_ps.maxs, maxs ); + // make them a bit smaller to be safe + for ( i = 0; i < 3; i++ ) { + mins[i] += 2; + maxs[i] -= 2; + } + + if ( trap_EntityContactCapsule( mins, maxs, ent ) ) { + move = qfalse; + } + } + + if ( move ) { + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + // fail + bs->ainode = NULL; + bs->ainodeText = "NULL"; + return qtrue; + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + } + } + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { +/* // if we are not going for an urgent goal, go into battle mode + if (!BotCarryingFlag(bs->client) && bs->ignore_specialgoal_time < level.time && BotWantsToChase(bs) && + (VectorDistanceSquared( goal.origin, bs->origin ) > SQR(384)) && + (VectorDistanceSquared( bs->enemyorigin, bs->origin ) < SQR(2048)) && + Q_stricmp(g_entities[goal.entitynum].classname, "team_CTF_redflag") && + Q_stricmp(g_entities[goal.entitynum].classname, "team_CTF_blueflag")) { + AIEnter_MP_Battle_Fight(bs); + return qfalse; + } +*/ + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( ent->s.eType == ET_TRIGGER_MULTIPLE && trap_InPVS( bs->origin, goal.origin ) && Distance( bs->origin, goal.origin ) < 512 ) { + VectorSubtract( ent->r.currentOrigin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + // check for giving ourselves some ammo + if ( bs->sess.playerType == PC_FIELDOPS && ClientNeedsAmmo( bs->client ) ) { + // switch to regen and pump away + bs->weaponnum = WP_AMMO; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_AMMO && BotWeaponCharged( bs, WP_AMMO ) ) { + trap_EA_Attack( bs->client ); + } + } + // check for giving ourselves some health + if ( bs->sess.playerType == PC_MEDIC && BotHealthScale( bs->client ) < 1.0 ) { + // switch to regen and pump away + bs->weaponnum = WP_MEDKIT; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_MEDKIT && BotWeaponCharged( bs, WP_MEDKIT ) ) { + trap_EA_Attack( bs->client ); + } + } + } + } + + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + + return qtrue; +} + +/* +================== +AIEnter_MP_SatchelChargeTarget +================== +*/ +void AIEnter_MP_SatchelChargeTarget( bot_state_t *bs ) { + bs->target_goal.flags &= ~GFL_NOSLOWAPPROACH; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_SatchelChargeTarget; + bs->ainodeText = "AINode_MP_SatchelChargeTarget"; +} + +/* +================== +AINode_MP_SatchelChargeTarget +================== +*/ +int AINode_MP_SatchelChargeTarget( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + int list[10], numList, i; + float goalDist; + aas_entityinfo_t entinfo; + + goal = bs->target_goal; + //if we have changed class + if ( bs->sess.playerType != PC_COVERTOPS ) { + BotDefaultNode( bs ); + return qfalse; + } + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + //if there are 2 bots going for this goal, then we should abort if we are further away + goalDist = VectorDistanceSquared( bs->origin, goal.origin ); + if ( ( numList = BotNumTeamMatesWithTargetByClass( bs, goal.entitynum, list, 10, PC_ENGINEER ) ) ) { + if ( goalDist > SQR( 256 ) ) { + goalDist = SQR( 256 ); // only abort if one of our teammates is close to the goal + } + for ( i = 0; i < numList; i++ ) { + if ( botstates[list[i]].ainode == AINode_MP_SatchelChargeTarget ) { + if ( VectorDistanceSquared( botstates[list[i]].origin, goal.origin ) < goalDist ) { + // make sure other bots dont head for this target also + g_entities[goal.entitynum].lastHintCheckTime = level.time + 5000; + BotDefaultNode( bs ); + return qfalse; + } + } + } + } + + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 128 ) { + dir[2] = 0; + } + + goalDist = VectorLengthSquared( dir ); + // are we close enough to the goal? + if ( goalDist < SQR( 32 ) ) { + // have we recently dropped some dynamite? + for ( trav = G_FindSatchels( NULL ); trav; trav = G_FindSatchels( trav ) ) { + if ( !trav->parent || trav->parent->s.number != bs->client ) { + continue; + } + if ( VectorDistanceSquared( bs->origin, trav->r.currentOrigin ) > SQR( 64 ) ) { + continue; + } + + // found some! + bs->flags |= BFL_MISCFLAG; + VectorCopy( trav->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.origin[2] += 24; + break; + } + + if ( !trav ) { + // no dynamite found, keep trying to plant some + if ( bs->flags & BFL_MISCFLAG ) { + // it's gone, so reset goal + BotDefaultNode( bs ); + return qfalse; + } + if ( BotWeaponCharged( bs, WP_SATCHEL ) ) { + // VOICE: cover me! + BotVoiceChatAfterIdleTime( bs->client, "CoverMe", SAY_TEAM, 500 + rand() % 1000, qfalse, 10000, qfalse ); + // make sure other bots dont head for this target also + g_entities[bs->target_goal.entitynum].lastHintCheckTime = level.time + 2000; + // crouch down + trap_EA_Crouch( bs->client ); + // look downwards + bs->ideal_viewangles[PITCH] = 70; + // select the dynamite + bs->weaponnum = WP_SATCHEL; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // hold fire + if ( !VectorLengthSquared( bs->velocity ) && fabs( bs->viewangles[PITCH] - bs->ideal_viewangles[PITCH] ) < 2 && bs->cur_ps.weapon == WP_SATCHEL && !bs->cur_ps.grenadeTimeLeft ) { + trap_EA_Attack( bs->client ); + } + return qtrue; + } + } else if ( ( trav = G_FindSatchel( &g_entities[bs->client] ) ) ) { + +/* // check for emergency targets (flags, etc) + if (BotCheckEmergencyTargets( bs )) { + return qfalse; + } + // check for dangerous elements + if (BotDangerousGoal(bs, &goal)) { + AIEnter_MP_AvoidDanger(bs); // avoid this danger until it passes + return qfalse; + } + + // just look for a goal + BotDefaultNode(bs); + return qfalse;*/ + } + } + + if ( !( bs->flags & BFL_MISCFLAG ) ) { + if ( goalDist > SQR( 128 ) ) { + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + // are we close enough to the goal? + if ( goalDist >= SQR( 32 ) ) { + // MOVEMENT REQUIRED + // + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_DynamiteTarget() +================== +*/ +void AIEnter_MP_DynamiteTarget( bot_state_t *bs ) { + bs->toggleHidingTime = level.time; + bs->target_goal.flags &= ~GFL_NOSLOWAPPROACH; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_DynamiteTarget; + bs->ainodeText = "AINode_MP_DynamiteTarget"; +} + +/* +================== +AINode_MP_DynamiteTarget() +================== +*/ +int AINode_MP_DynamiteTarget( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + int list[10], numList, i; + float goalDist; + aas_entityinfo_t entinfo; + + goal = bs->target_goal; + //if we have changed class + if ( bs->sess.playerType != PC_ENGINEER ) { + BotDefaultNode( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + //if there are 2 bots going for this goal, then we should abort if we are further away + goalDist = VectorDistanceSquared( bs->origin, goal.origin ); + if ( ( numList = BotNumTeamMatesWithTargetByClass( bs, goal.entitynum, list, 10, PC_ENGINEER ) ) ) { + if ( goalDist > SQR( 256 ) ) { + goalDist = SQR( 256 ); // only abort if one of our teammates is close to the goal + } + for ( i = 0; i < numList; i++ ) { + if ( list[i] == bs->client ) { + continue; + } + if ( botstates[list[i]].ainode == AINode_MP_DynamiteTarget ) { + if ( VectorDistanceSquared( botstates[list[i]].origin, goal.origin ) < goalDist ) { + // make sure other bots dont head for this target also + g_entities[goal.entitynum].lastHintCheckTime = level.time + 5000; + BotDefaultNode( bs ); + return qfalse; + } + } + } + } + // + //map specific code + BotMapScripts( bs ); + // + VectorSubtract( bs->origin, goal.origin, dir ); + if ( fabs( dir[2] ) < 128 ) { + dir[2] = 0; + } + goalDist = VectorLengthSquared( dir ); + // are we close enough to the goal? + if ( goalDist < SQR( 32 ) ) { + // have we recently dropped some dynamite? + for ( trav = G_FindDynamite( NULL ); trav; trav = G_FindDynamite( trav ) ) { + if ( !trav->parent || trav->parent->s.number != bs->client ) { + continue; + } + if ( VectorDistanceSquared( bs->origin, trav->r.currentOrigin ) > SQR( 100 ) ) { + continue; + } + if ( trav->spawnTime < bs->toggleHidingTime ) { + // armed before we started this node + continue; + } + + // found some! + bs->flags |= BFL_MISCFLAG; + VectorCopy( trav->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.origin[2] += 24; + break; + } + + if ( !trav ) { + // no dynamite found, keep trying to plant some + if ( bs->flags & BFL_MISCFLAG ) { + // it's gone, so reset goal + BotDefaultNode( bs ); + return qfalse; + } + if ( BotWeaponCharged( bs, WP_DYNAMITE ) ) { + // VOICE: cover me! + BotVoiceChatAfterIdleTime( bs->client, "CoverMe", SAY_TEAM, 500 + rand() % 1000, qfalse, 10000, qfalse ); + // make sure other bots dont head for this target also + g_entities[bs->target_goal.entitynum].lastHintCheckTime = level.time + 2000; + // crouch down + trap_EA_Crouch( bs->client ); + // look downwards + bs->ideal_viewangles[PITCH] = 70; + // select the dynamite + bs->weaponnum = WP_DYNAMITE; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // hold fire + if ( rand() % 2 && !VectorLengthSquared( bs->velocity ) && fabs( bs->viewangles[PITCH] - bs->ideal_viewangles[PITCH] ) < 2 && bs->cur_ps.weapon == WP_DYNAMITE && !bs->cur_ps.grenadeTimeLeft ) { + trap_EA_Attack( bs->client ); + } + return qtrue; + } + } else if ( trav->s.teamNum >= 4 ) { // the dynamite is not armed + // switch to pliers and arm the dynamite + bs->weaponnum = WP_PLIERS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // aim directly at the dynamite + VectorCopy( bs->origin, target ); + target[2] += bs->cur_ps.viewheight; + VectorSubtract( trav->r.currentOrigin, target, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + if ( bs->cur_ps.weapon == WP_PLIERS ) { + trap_EA_Attack( bs->client ); + } + return qtrue; + } else { // the dynamite is armed, get out of here! + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // just look for a goal + BotDefaultNode( bs ); + + // if we are still in dynamite mode, then we may be trying to plant more dynamite at same place + bs->toggleHidingTime = level.time; + + return qfalse; + } + } + // + if ( !( bs->flags & BFL_MISCFLAG ) ) { + if ( goalDist > SQR( 128 ) ) { + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + // are we close enough to the goal? + if ( goalDist >= SQR( 32 ) ) { + // MOVEMENT REQUIRED + // + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + } + // + return qtrue; +} + + +/* +================== +AIEnter_MP_ConstructibleTarget() +================== +*/ +void AIEnter_MP_ConstructibleTarget( bot_state_t *bs ) { + bs->target_goal.flags &= ~GFL_NOSLOWAPPROACH; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_ConstructibleTarget; + bs->ainodeText = "AINode_MP_ConstructibleTarget"; +} + +/* +================== +AINode_MP_ConstructibleTarget() +================== +*/ +int AINode_MP_ConstructibleTarget( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav, *ent, *constructible; + int list[10], numList, i; + float goalDist; + + ent = &g_entities[bs->client]; + trav = &g_entities[bs->target_goal.entitynum]; + + constructible = G_ConstructionForTeam( trav, bs->sess.sessionTeam ); + goal = bs->target_goal; + +/* if(bot_profile.integer == 2) { + // update this in case it is moving + if (bs->lastLeaderMoveTime < level.time - 500) { + BotGoalForEntity( bs, goal.entitynum, &goal, BGU_HIGH ); + bs->lastLeaderMoveTime = level.time; + } + }*/ + + //if we have changed class + if ( bs->sess.playerType != PC_ENGINEER ) { + BotDefaultNode( bs ); + return qfalse; + } + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + + // check for emergency targets + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + + // is this constructible finished building? + if ( !BotIsConstructible( bs->sess.sessionTeam, goal.entitynum ) ) { + // finished building + BotDefaultNode( bs ); + return qfalse; + } + + //if there are 2 bots going for this goal, then we should abort if we are further away + if ( ( numList = BotNumTeamMatesWithTarget( bs, goal.entitynum, list, 10 ) ) > botgoalMaxCloser[BFG_CONSTRUCT] ) { + goalDist = VectorDistanceSquared( bs->origin, goal.origin ); + if ( goalDist > SQR( 1024 ) ) { + goalDist = SQR( 1024 ); // only abort if one of our teammates is close to the goal + } + + for ( i = 0; i < numList; i++ ) { + if ( botstates[list[i]].ainode == AINode_MP_ConstructibleTarget ) { + if ( VectorDistanceSquared( botstates[list[i]].origin, goal.origin ) < goalDist ) { + // only abort if there is something else to do + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + } + } + } + + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + + // are we close enough to the goal? + if ( ent->client->touchingTOI && ent->client->touchingTOI == trav ) { + // if the construction has changed level, we should reset the construction flag so we wait for full power again + if ( ( bs->flags & BFL_MISCFLAG ) && constructible->count2 && !constructible->s.angles2[0] ) { + bs->flags &= ~BFL_MISCFLAG; + } + + // if we haven't started yet, make sure we wait until we have reasonable power before starting + if ( !( bs->flags & BFL_MISCFLAG ) ) { + if ( bs->cur_ps.classWeaponTime > ( level.time - level.engineerChargeTime[bs->sess.sessionTeam - 1] / 4 ) ) { + // just crouch, and look ahead + trap_EA_Crouch( bs->client ); + bs->ideal_viewangles[PITCH] = 0; + // look for things to attack + BotFindAndAttackEnemy( bs ); + return qtrue; + } + } + + // we have started + // if we are out of power, wait until it's full before starting again + if ( !ReadyToConstruct( ent, constructible, qfalse ) ) { + bs->flags &= ~BFL_MISCFLAG; + return qtrue; + } + + bs->flags |= BFL_MISCFLAG; + // VOICE: cover me! + BotVoiceChatAfterIdleTime( bs->client, "CoverMe", SAY_TEAM, 500 + rand() % 1000, qfalse, 10000, qfalse ); + // look downwards + bs->ideal_viewangles[PITCH] = 70; + // select pliers (the all-in-one handyman tool) + bs->weaponnum = WP_PLIERS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // hold fire + if ( bs->cur_ps.weapon == WP_PLIERS ) { + trap_EA_Attack( bs->client ); + } + return qtrue; + } + + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + // + // MOVEMENT REQUIRED + // + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + + //if the movement failed + goalDist = VectorDistanceSquared( bs->origin, goal.origin ); + if ( moveresult.failure || ( VectorLengthSquared( bs->cur_ps.velocity ) < SQR( 3 ) && goalDist < ( 128 * 128 ) ) ) { // allow for boxes at construction site that may get in the way + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { // if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_PlantMine() +================== +*/ +void AIEnter_MP_PlantMine( bot_state_t *bs ) { + bs->target_goal.flags &= ~GFL_NOSLOWAPPROACH; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_PlantMine; + bs->ainodeText = "AINode_MP_PlantMine"; +} + +/* +================== +AINode_MP_PlantMine() +================== +*/ +int AINode_MP_PlantMine( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + + goal = bs->target_goal; + + //if we have changed class + if ( bs->sess.playerType != PC_ENGINEER ) { + BotDefaultNode( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // if I have no landmines, I can't do this + if ( !BotGotEnoughAmmoForWeapon( bs, WP_LANDMINE ) ) { + BotDefaultNode( bs ); + return qfalse; + } + + // are we close enough to the goal? + if ( VectorDistanceSquared( bs->origin, goal.origin ) < SQR( 32 ) ) { + // VOICE: cover me! + BotVoiceChatAfterIdleTime( bs->client, "CoverMe", SAY_TEAM, 500 + rand() % 1000, qfalse, 60000, qfalse ); + // crouch down + trap_EA_Crouch( bs->client ); + // look downwards + bs->ideal_viewangles[PITCH] = 70; + // have we recently dropped a land mine? + trav = NULL; + while ( ( trav = G_FindLandmine( trav ) ) ) { + if ( !trav->parent || trav->parent->s.number != bs->client ) { + continue; + } + if ( VectorDistanceSquared( bs->target_goal.origin, trav->r.currentOrigin ) > SQR( 100 ) ) { + continue; + } + if ( trav->awaitingHelpTime < level.time - 5000 ) { + continue; + } + if ( G_LandmineArmed( trav ) ) { + bs->last_dangerousgoal = 0; // Gordon: force a reset + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + } + // found some! + bs->flags |= BFL_MISCFLAG; + VectorCopy( trav->r.currentOrigin, bs->target_goal.origin ); + bs->target_goal.origin[2] += 24; + break; + } + if ( !trav ) { + // select the land mine + bs->weaponnum = WP_LANDMINE; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // hold fire + if ( !VectorLengthSquared( bs->velocity ) && fabs( bs->viewangles[PITCH] - bs->ideal_viewangles[PITCH] ) < 2 && bs->cur_ps.weapon == bs->weaponnum && !bs->cur_ps.grenadeTimeLeft ) { + trap_EA_Attack( bs->client ); + } + } else if ( trav->s.teamNum >= 4 ) { // the dynamite is not armed + // switch to pliers and arm the dynamite + bs->weaponnum = WP_PLIERS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // aim directly at the dynamite + VectorCopy( bs->origin, target ); + target[2] += bs->cur_ps.viewheight; + VectorSubtract( trav->r.currentOrigin, target, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + if ( bs->cur_ps.weapon == WP_PLIERS ) { + trap_EA_Attack( bs->client ); + } + } else { + // the dynamite is armed, get out of here! + BotDefaultNode( bs ); + return qfalse; + } + return qtrue; + } + + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + // MOVEMENT REQUIRED + // + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + BotDefaultNode( bs ); + return qfalse; + } + + BotAIBlocked( bs, &moveresult, qtrue ); + + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( moveresult.flags & MOVERESULT_WAITING ) { // if waiting for something + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + BotFindAndAttackEnemy( bs ); + BotUpdateViewAngles( bs, &goal, moveresult ); + } + + return qtrue; +} + +/* +================== +AIEnter_MP_DisarmDynamite() +================== +*/ +void AIEnter_MP_DisarmDynamite( bot_state_t *bs ) { + // VOICE: cover me! + BotVoiceChatAfterIdleTime( bs->client, "CoverMe", SAY_TEAM, 500 + rand() % 1000, qfalse, 4000, qfalse ); + bs->target_goal.flags &= ~GFL_NOSLOWAPPROACH; + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_DisarmDynamite; + bs->ainodeText = "AINode_MP_DisarmDynamite"; +} + +/* +================== +AINode_MP_DisarmDynamite() +================== +*/ +int AINode_MP_DisarmDynamite( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + gentity_t *trav; + float goalDist; + + goal = bs->target_goal; + //if we have changed class + if ( bs->sess.playerType != PC_ENGINEER ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + //if there are 2 bots going for this goal, then we should abort if we are further away + goalDist = VectorDistanceSquared( bs->origin, goal.origin ); +/* if (numList = BotNumTeamMatesWithTarget( bs, goal.entitynum, list, 10 )) { + for (i=0; iclient) continue; + if (botstates[list[i]].ainode == AINode_MP_DisarmDynamite) { + if (VectorDistanceSquared( botstates[list[i]].origin, goal.origin ) < goalDist) { + // make sure other bots dont head for this target also + bs->ignore_specialgoal_time = 0; + g_entities[goal.entitynum].missionLevel = level.time + 5000; + BotDefaultNode( bs ); + return qfalse; + } + } + } + } +*/ // + trav = BotGetEntity( goal.entitynum ); + + // FIXME: temp hack in dealing with NULL returns from BotGetEntity (??) + if ( trav == NULL ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + + if ( !trav->inuse || trav->s.eType != ET_MISSILE || trav->s.weapon != WP_DYNAMITE ) { + // it's gone, so reset goal + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + if ( trav->s.teamNum >= 4 ) { // the dynamite is not armed + // make sure other bots dont head for this target also + bs->ignore_specialgoal_time = 0; + g_entities[goal.entitynum].missionLevel = level.time + 5000; + BotDefaultNode( bs ); + return qfalse; + } + // make sure other bots dont head for this target also + trav->missionLevel = level.time + 1000; + // + + //map specific code + BotMapScripts( bs ); + // + // are we close enough to the goal? + if ( goalDist < SQR( 32 ) ) { + // crouch down + trap_EA_Crouch( bs->client ); + // + // switch to pliers and disarm the dynamite + bs->weaponnum = WP_PLIERS; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + // aim directly at the dynamite + VectorCopy( bs->origin, target ); + target[2] += bs->cur_ps.viewheight; + VectorSubtract( trav->r.currentOrigin, target, dir ); + VectorNormalize( dir ); + vectoangles( dir, bs->ideal_viewangles ); + // hold fire + if ( bs->cur_ps.weapon == WP_PLIERS ) { + trap_EA_Attack( bs->client ); + } + // + return qtrue; + } + + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + // MOVEMENT REQUIRED + // + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // jump randomly? + trap_EA_Jump( bs->client ); + trap_EA_Move( bs->client, tv( crandom(), crandom(), crandom() ), 100 + random() * 200 ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_Battle_Fight() +================== +*/ +void AIEnter_MP_Battle_Fight( bot_state_t *bs ) { + BotClearGoal( &bs->target_goal ); + trap_BotResetLastAvoidReach( bs->ms ); + bs->ainode = AINode_MP_Battle_Fight; + bs->ainodeText = "AINode_MP_Battle_Fight"; +} + +/* +================== +AINode_MP_Battle_Fight() +================== +*/ +int AINode_MP_Battle_Fight( bot_state_t *bs ) { + int areanum; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + bot_goal_t goal; + float enemydist; + + memset( &moveresult, 0, sizeof( moveresult ) ); + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + //if no enemy + if ( bs->enemy < 0 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // if using MOBILE MG42, conditionally go into special prone mode + if ( bs->weaponnum == WP_MOBILE_MG42 ) { + if ( VectorDistanceSquared( bs->origin, BotGetOrigin( bs->enemy ) ) > SQR( 700 ) ) { + vec3_t dir, ang, mountedWeaponAngles; + trace_t tr; + vec3_t end; + // make sure they are within pitch range + VectorSubtract( BotGetOrigin( bs->enemy ), bs->eye, dir ); + VectorNormalize( dir ); + vectoangles( dir, ang ); + // + VectorCopy( bs->origin, end ); + end[2] -= 40; + trap_Trace( &tr, bs->origin, vec3_origin, vec3_origin, end, bs->client, MASK_PLAYERSOLID ); + if ( tr.fraction < 1.f ) { + vec3_t axis[3], forward, right; + + AngleVectors( bs->viewangles, forward, right, NULL ); + forward[2] = 0; + right[2] = 0; + PM_ClipVelocity( forward, tr.plane.normal, forward, OVERCLIP ); + PM_ClipVelocity( right, tr.plane.normal, right, OVERCLIP ); + // + VectorNormalize( forward ); + VectorNormalize( right ); + // + VectorCopy( forward, axis[0] ); + VectorCopy( right, axis[2] ); + CrossProduct( axis[0], axis[2], axis[1] ); + AxisToAngles( axis, mountedWeaponAngles ); + + // make sure these angles are within view limits towards the enemy + if ( fabs( AngleDifference( mountedWeaponAngles[PITCH], ang[PITCH] ) ) < 15.f ) { + int oldviewheight; + // check for obstruction at feet + oldviewheight = level.clients[bs->client].ps.viewheight; + level.clients[bs->client].ps.viewheight = PRONE_VIEWHEIGHT; + if ( BotVisibleFromPos( bs->origin, bs->client, BotGetOrigin( bs->enemy ), bs->enemy, qtrue ) ) { + AIEnter_MP_Battle_MobileMG42( bs ); + return qfalse; + } + } + } + } + } + // check for dangerous elements + VectorCopy( bs->origin, goal.origin ); + goal.areanum = bs->areanum; + goal.entitynum = bs->client; + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + // + BotEntityInfo( bs->enemy, &entinfo ); + enemydist = VectorDistanceSquared( bs->origin, entinfo.origin ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->ignore_specialgoal_time = 0; + bs->enemydeath_time = 0; + + bs->enemy = -1; + BotDefaultNode( bs ); + return qfalse; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + //if the enemy is invisible and not shooting the bot looses track easily + if ( EntityIsInvisible( &entinfo ) && !EntityIsShooting( &entinfo ) ) { + if ( random() < 0.2 ) { + BotDefaultNode( bs ); + return qfalse; + } + } + //update the reachability area and origin if possible + areanum = BotGetArea( bs->enemy ); //BotPointAreaNum(entinfo.number, entinfo.origin); + if ( areanum ) { + VectorCopy( entinfo.origin, bs->lastenemyorigin ); + bs->lastenemyareanum = areanum; + } + //update the attack inventory values + BotUpdateBattleInventory( bs, bs->enemy ); + // get in real close to enemy flag carriers + if ( BotCarryingFlag( bs->enemy ) && VectorDistanceSquared( bs->origin, bs->lastenemyorigin ) > SQR( 72 ) ) { + if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->lastenemyareanum, bs->tfl ) ) { + AIEnter_MP_Battle_Chase( bs ); + return qfalse; + } + } + //if the enemy is not visible, or is too far away + if ( ( bs->weaponnum == WP_KNIFE ) || ( enemydist > SQR( BotWeaponRange( bs, bs->weaponnum ) ) ) || !BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + if ( BotWantsToChase( bs ) ) { + AIEnter_MP_Battle_Chase( bs ); + return qfalse; + } else { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qtrue; // wait until next frame, incase this triggers a loop + } + } + //use holdable items + BotBattleUseItems( bs ); + // + + if ( bot_grapple.integer ) { + bs->tfl |= TFL_GRAPPLEHOOK; + } + //if in lava or slime the bot should be able to get out + if ( BotInLava( bs ) ) { + bs->tfl |= TFL_LAVA; + } + if ( BotInSlime( bs ) ) { + bs->tfl |= TFL_SLIME; + } + // + if ( BotCanAndWantsToRocketJump( bs ) ) { + bs->tfl |= TFL_ROCKETJUMP; + } + //choose the best weapon to fight with + BotChooseWeapon( bs ); + if ( BotMoveWhileFiring( bs->weaponnum ) ) { + //do attack movements + moveresult = BotAttackMove( bs, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + } + // + BotAIBlocked( bs, &moveresult, qfalse ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( !BotCheckAttack( bs ) ) { + if ( BotWantsToChase( bs ) ) { + AIEnter_MP_Battle_Chase( bs ); + return qfalse; // chase them immediately + } else { + BotDefaultNode( bs ); + return qtrue; // prevent loop + } + } + //if the bot wants to retreat + if ( BotWantsToRetreat( bs ) ) { + BotFindSpecialGoals( bs ); + return qtrue; + } + return qtrue; +} + +/* +================== +AIEnter_MP_Battle_Chase() +================== +*/ +void AIEnter_MP_Battle_Chase( bot_state_t *bs ) { + bs->altenemy = -1; + BotClearGoal( &bs->target_goal ); + bs->chase_time = trap_AAS_Time(); + bs->ainode = AINode_MP_Battle_Chase; + bs->ainodeText = "AINode_MP_Battle_Chase"; +} + +/* +================== +AINode_MP_Battle_Chase() +================== +*/ +int AINode_MP_Battle_Chase( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + aas_entityinfo_t entinfo; + int oldenemy; + float enemydist; + + memset( &moveresult, 0, sizeof( moveresult ) ); + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // RF, if we have a leader, go back to protecting them + if ( ( bs->leader > -1 ) && !BotCarryingFlag( bs->enemy ) ) { + AIEnter_MP_DefendTarget( bs ); + return qfalse; + } + //if no enemy + if ( bs->enemy < 0 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( !BotCarryingFlag( bs->enemy ) && BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + //if the enemy is visible + BotEntityInfo( bs->enemy, &entinfo ); + VectorCopy( entinfo.origin, bs->lastenemyorigin ); + enemydist = VectorDistanceSquared( bs->origin, bs->lastenemyorigin ); + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + // update chase org + bs->lastenemyareanum = BotPointAreaNum( bs->enemy, entinfo.origin ); + bs->chase_time = trap_AAS_Time() + 5.0; + // RF, if we are in the air, wait until we land (we may be climbing a ladder) + if ( bs->cur_ps.groundEntityNum != ENTITYNUM_NONE || bs->cur_ps.velocity[2] < 0 ) { + // get real close to flag carrier + if ( ( bs->weaponnum != WP_KNIFE ) && ( ( !BotCarryingFlag( bs->enemy ) && ( enemydist < SQR( BotWeaponRange( bs, bs->weaponnum ) ) ) ) || ( enemydist < SQR( 64 ) ) ) ) { + // make sure it is safe to attack + if ( BotCheckAttack( bs ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } else { // attack and keep moving towards them + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } + } + } + // + if ( !BotCarryingFlag( bs->enemy ) ) { + int oldEnemy; + //if there is another enemy + oldEnemy = bs->enemy; + if ( ( bs->weaponnum != WP_KNIFE ) && BotFindEnemyMP( bs, -1, qfalse ) && ( bs->enemy != oldEnemy ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } + //there is no last enemy area + if ( ( bs->weaponnum != WP_KNIFE ) && !bs->lastenemyareanum ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + + if ( bot_grapple.integer ) { + bs->tfl |= TFL_GRAPPLEHOOK; + } + //if in lava or slime the bot should be able to get out + if ( BotInLava( bs ) ) { + bs->tfl |= TFL_LAVA; + } + if ( BotInSlime( bs ) ) { + bs->tfl |= TFL_SLIME; + } + // + if ( BotCanAndWantsToRocketJump( bs ) ) { + bs->tfl |= TFL_ROCKETJUMP; + } + //map specific code + BotMapScripts( bs ); + //create the chase goal + goal.entitynum = bs->enemy; + goal.areanum = bs->lastenemyareanum; + VectorCopy( bs->lastenemyorigin, goal.origin ); + VectorSet( goal.mins, -8, -8, -8 ); + VectorSet( goal.maxs, 8, 8, 8 ); + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + //if the last seen enemy spot is reached the enemy could not be found + if ( ( bs->weaponnum != WP_KNIFE ) && trap_BotTouchingGoal( bs->origin, &goal ) ) { + bs->chase_time = 0; + } + //if there's no chase time left + if ( !bs->chase_time || bs->chase_time < trap_AAS_Time() - 10 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + BotUpdateBattleInventory( bs, bs->enemy ); + //initialize the movement state + BotSetupForMovement( bs ); + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + bs->enemy = -1; + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype); + + // try and find something else to do + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + BotAIBlocked( bs, &moveresult, qfalse ); + // + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else { + // forget about our current enemy temporarily, so we can shoot at other while pursuing the flag carrier + oldenemy = bs->enemy; + if ( BotCarryingFlag( oldenemy ) ) { + // check for enemies + bs->enemy = -1; + if ( bs->altenemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } else { + bs->enemy = bs->altenemy; + } + if ( bs->enemy > -1 ) { + bs->altenemy = bs->enemy; + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->altenemy = -1; + } + } else { + bs->altenemy = -1; + } + } + // + if ( ( bs->altenemy < 0 || !BotCarryingFlag( oldenemy ) ) && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } + // restore the enemy + bs->enemy = oldenemy; + } + //if the weapon is used for the bot movement + if ( moveresult.flags & MOVERESULT_MOVEMENTWEAPON ) { + bs->weaponnum = moveresult.weapon; + } + //if the bot is in the area the enemy was last seen in + if ( bs->areanum == bs->lastenemyareanum ) { + bs->chase_time = 0; + } + //if the bot wants to retreat (the bot could have been damage during the chase) + if ( BotWantsToRetreat( bs ) ) { + AIEnter_MP_Battle_Retreat( bs ); + return qtrue; + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_Battle_Retreat() +================== +*/ +void AIEnter_MP_Battle_Retreat( bot_state_t *bs ) { + bs->ainode = AINode_MP_Battle_Retreat; + bs->ainodeText = "AINode_MP_Battle_Retreat"; +} + +/* +================== +AINode_MP_Battle_Retreat() +================== +*/ +int AINode_MP_Battle_Retreat( bot_state_t *bs ) { + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + int areanum; + + memset( &moveresult, 0, sizeof( moveresult ) ); + + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + //if no enemy + if ( bs->enemy < 0 ) { + BotDefaultNode( bs ); + return qfalse; + } + // + BotEntityInfo( bs->enemy, &entinfo ); + if ( EntityIsDead( &entinfo ) ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + + if ( bot_grapple.integer ) { + bs->tfl |= TFL_GRAPPLEHOOK; + } + //if in lava or slime the bot should be able to get out + if ( BotInLava( bs ) ) { + bs->tfl |= TFL_LAVA; + } + if ( BotInSlime( bs ) ) { + bs->tfl |= TFL_SLIME; + } + //map specific code + BotMapScripts( bs ); + //update the attack inventory values + BotUpdateBattleInventory( bs, bs->enemy ); + //if the bot doesn't want to retreat anymore... probably picked up some nice items + if ( BotWantsToChase( bs ) ) { + //empty the goal stack, when chasing, only the enemy is the goal + trap_BotEmptyGoalStack( bs->gs ); + //go chase the enemy + AIEnter_MP_Battle_Chase( bs ); + return qfalse; + } + //update the last time the enemy was visible + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + bs->enemyvisible_time = trap_AAS_Time(); + //update the reachability area and origin if possible + areanum = BotPointAreaNum( entinfo.number, entinfo.origin ); + if ( areanum && trap_AAS_AreaReachability( areanum ) ) { + VectorCopy( entinfo.origin, bs->lastenemyorigin ); + bs->lastenemyareanum = areanum; + } + } + //if the enemy is NOT visible for 4 seconds + if ( bs->enemyvisible_time < trap_AAS_Time() - 4 ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + //else if the enemy is NOT visible + else if ( bs->enemyvisible_time < trap_AAS_Time() ) { + //if there is another enemy + if ( BotFindEnemyMP( bs, -1, qfalse ) ) { + AIEnter_MP_Battle_Fight( bs ); + return qfalse; + } + } + // + //use holdable items + BotBattleUseItems( bs ); + // + // !!! RF, we should try to move somewhere safe here + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //if the view is fixed for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEW + //|MOVERESULT_SWIMVIEW + ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } else if ( !( moveresult.flags & MOVERESULT_MOVEMENTVIEWSET ) + && !( bs->flags & BFL_IDEALVIEWSET ) ) { + BotAimAtEnemy( bs ); + } + //if the weapon is used for the bot movement + if ( moveresult.flags & MOVERESULT_MOVEMENTWEAPON ) { + bs->weaponnum = moveresult.weapon; + } + //attack the enemy if possible + BotCheckAttack( bs ); + // + return qtrue; +} + +/* +================== +AIEnter_MP_Script_MoveToMarker() +================== +*/ +void AIEnter_MP_Script_MoveToMarker( bot_state_t *bs ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_Script_MoveToMarker; + bs->ainodeText = "AINode_MP_Script_MoveToMarker"; +} + +/* +================== +AINode_MP_Script_MoveToMarker() +================== +*/ +int AINode_MP_Script_MoveToMarker( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + g_serverEntity_t *marker; + // + goal = bs->target_goal; + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( !BotIsPOW( bs ) ) { + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + } + // check for dangerous elements + if ( !BotIsPOW( bs ) ) { + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } + } +/* // check for special class actions + if (BotCheckClassActions(bs)) { + return qfalse; + } +*/ // look for health/ammo packs + if ( !BotIsPOW( bs ) ) { + if ( BotFindNearbyGoal( bs ) ) { + AIEnter_MP_Seek_NBG( bs ); + return qfalse; + } + } + if ( !BotIsPOW( bs ) ) { + if ( BotFindSpecialGoals( bs ) ) { + return qfalse; + } + } + // + // should we stop pursuing this target? + marker = GetServerEntity( bs->target_goal.entitynum ); + if ( !marker || !marker->inuse ) { + bs->ignore_specialgoal_time = 0; + BotDefaultNode( bs ); + return qfalse; + } + // + VectorCopy( goal.origin, target ); + if ( fabs( target[2] - bs->origin[2] ) < 80 ) { + target[2] = bs->origin[2]; + } + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + + //map specific code + BotMapScripts( bs ); + //initialize the movement state + BotSetupForMovement( bs ); + // + memset( &moveresult, 0, sizeof( moveresult ) ); + // + if ( VectorDistanceSquared( bs->origin, target ) >= SQR( 12 ) ) { + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + if ( !( bs->flags & BFL_MISCFLAG ) ) { + Bot_ScriptLog_Entry( bs, qfalse, NULL, "movement failure" ); + bs->flags |= BFL_MISCFLAG; + } + return qtrue; + } else { + if ( bs->flags & BFL_MISCFLAG ) { + Bot_ScriptLog_Entry( bs, qfalse, NULL, "resumed movement" ); + bs->flags &= ~BFL_MISCFLAG; + } + } + // set the movement type + if ( bs->script.moveType == BSMT_WALKING ) { + trap_EA_Walk( bs->client ); + } else if ( bs->script.moveType == BSMT_CROUCHING ) { + trap_EA_Crouch( bs->client ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + } else { + // Gordon: TEMP: i doubt this should be here + if ( BotIsPOW( bs ) ) { + BotDefaultNode( bs ); + return qfalse; + } + } + + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + // check for giving ourselves some ammo + if ( bs->sess.playerType == PC_FIELDOPS && ClientNeedsAmmo( bs->client ) ) { + // switch to regen and pump away + bs->weaponnum = WP_AMMO; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_AMMO && BotWeaponCharged( bs, WP_AMMO ) ) { + trap_EA_Attack( bs->client ); + } + } + // check for giving ourselves some health + if ( bs->sess.playerType == PC_MEDIC && BotHealthScale( bs->client ) < 1.0 ) { + // switch to regen and pump away + bs->weaponnum = WP_MEDKIT; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_MEDKIT && BotWeaponCharged( bs, WP_MEDKIT ) ) { + trap_EA_Attack( bs->client ); + } + } + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_MoveToAutonomyRange() +================== +*/ +void AIEnter_MP_MoveToAutonomyRange( bot_state_t *bs ) { + vec3_t pos; + // + if ( !BotGetMovementAutonomyPos( bs, pos ) ) { + if ( g_developer.integer ) { + G_Printf( "AIEnter_MP_MoveToAutonomyRange: autonomy pos unknown\n" ); + } + } + // make the current goal our autonomy spot + BotClearGoal( &bs->target_goal ); + VectorCopy( pos, bs->target_goal.origin ); + bs->target_goal.areanum = BotPointAreaNum( bs->client, pos ); + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->ainode = AINode_MP_MoveToAutonomyRange; + bs->ainodeText = "AINode_MP_MoveToAutonomyRange"; +} + +/* +================== +AINode_MP_MoveToAutonomyRange() +================== +*/ +int AINode_MP_MoveToAutonomyRange( bot_state_t *bs ) { + bot_goal_t goal; + vec3_t target, dir; + bot_moveresult_t moveresult; + // + goal = bs->target_goal; + // + if ( BotIsObserver( bs ) ) { + AIEnter_MP_Observer( bs ); + return qfalse; + } + //if in the intermission + if ( BotIntermission( bs ) ) { + AIEnter_MP_Intermission( bs ); + return qfalse; + } + //respawn if dead + if ( BotIsDead( bs ) ) { + AIEnter_MP_Respawn( bs ); + return qfalse; + } + // check for emergency targets (flags, etc) + if ( BotCheckEmergencyTargets( bs ) ) { + return qfalse; + } + // check for dangerous elements + if ( BotDangerousGoal( bs, &goal ) ) { + AIEnter_MP_AvoidDanger( bs ); // avoid this danger until it passes + return qfalse; + } +/* // check for special class actions + if (BotCheckClassActions(bs)) { + return qfalse; + } +*/ // look for health/ammo packs + //if (BotFindNearbyGoal( bs )) { + // AIEnter_MP_Seek_NBG( bs ); + // return qfalse; + //} + //if (BotFindSpecialGoals(bs)) { + // return qfalse; + //} + // + // should we stop pursuing this target? + //ent = BotGetEntity( bs->target_goal.entitynum ); + //if (!ent->inuse) { + // bs->ignore_specialgoal_time = 0; + // BotDefaultNode(bs); + // return qfalse; + //} + // + VectorCopy( goal.origin, target ); + if ( fabs( target[2] - bs->origin[2] ) < 80 ) { + target[2] = bs->origin[2]; + } + // + //choose the best weapon to fight with + BotChooseWeapon( bs ); + // + + //map specific code + BotMapScripts( bs ); + //initialize the movement state + BotSetupForMovement( bs ); + // + memset( &moveresult, 0, sizeof( moveresult ) ); + // + { + float halfRange = ( 0.5 * BotGetMovementAutonomyRange( bs, NULL ) ); + + if ( VectorDistanceSquared( bs->origin, target ) <= SQR( halfRange ) ) { + // go back to standing + AIEnter_MP_Stand( bs ); + return qfalse; + } + } + // + //try a direct movement + if ( !BotDirectMoveToGoal( bs, &goal, &moveresult ) ) { + //move towards the goal + BotMP_MoveToGoal( bs, &moveresult, bs->ms, &goal, bs->tfl ); + } + //if the movement failed + if ( moveresult.failure ) { + //reset the avoid reach, otherwise bot is stuck in current area + trap_BotResetAvoidReach( bs->ms ); + if ( !( bs->flags & BFL_MISCFLAG ) ) { + Bot_ScriptLog_Entry( bs, qfalse, NULL, "movement failure" ); + bs->flags |= BFL_MISCFLAG; + } + return qtrue; + } else { + if ( bs->flags & BFL_MISCFLAG ) { + Bot_ScriptLog_Entry( bs, qfalse, NULL, "resumed movement" ); + bs->flags &= ~BFL_MISCFLAG; + } + } + // set the movement type + if ( bs->script.moveType == BSMT_WALKING ) { + trap_EA_Walk( bs->client ); + } else if ( bs->script.moveType == BSMT_CROUCHING ) { + trap_EA_Crouch( bs->client ); + } + // + BotAIBlocked( bs, &moveresult, qtrue ); + // + //if the viewangles are used for the movement + if ( moveresult.flags & ( MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW ) ) { + VectorCopy( moveresult.ideal_viewangles, bs->ideal_viewangles ); + } + //if waiting for something + else if ( moveresult.flags & MOVERESULT_WAITING ) { + if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + } else { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + // + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + if ( moveresult.flags & MOVERESULT_DIRECTMOVE ) { + VectorSubtract( goal.origin, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } + //FIXME: look at cluster portals? + else if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + // check for giving ourselves some ammo + if ( bs->sess.playerType == PC_FIELDOPS && ClientNeedsAmmo( bs->client ) ) { + // switch to regen and pump away + bs->weaponnum = WP_AMMO; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_AMMO && BotWeaponCharged( bs, WP_AMMO ) ) { + trap_EA_Attack( bs->client ); + } + } + // check for giving ourselves some health + if ( bs->sess.playerType == PC_MEDIC && BotHealthScale( bs->client ) < 1.0 ) { + // switch to regen and pump away + bs->weaponnum = WP_MEDKIT; + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + if ( bs->cur_ps.weapon == WP_MEDKIT && BotWeaponCharged( bs, WP_MEDKIT ) ) { + trap_EA_Attack( bs->client ); + } + } + } + } + // reload? + if ( ( bs->last_fire != level.time ) && ( bs->cur_ps.ammoclip[BG_FindClipForWeapon( bs->cur_ps.weapon )] < (int)( 0.8 * GetAmmoTableData( bs->cur_ps.weapon )->maxclip ) ) && bs->cur_ps.ammo[BG_FindAmmoForWeapon( bs->cur_ps.weapon )] ) { + trap_EA_Reload( bs->client ); + } + // + return qtrue; +} + +/* +================== +AIEnter_MP_NavigateFromVoid() +================== +*/ +void AIEnter_MP_NavigateFromVoid( bot_state_t *bs ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + bs->flags &= ~BFL_MISCFLAG; + bs->last_SparseDefense = 0; + bs->ainode = AINode_MP_NavigateFromVoid; + bs->ainodeText = "AINode_MP_NavigateFromVoid"; +} + +/* +================== +AINode_MP_NavigateFromVoid() +================== +*/ +int AINode_MP_NavigateFromVoid( bot_state_t *bs ) { + const int checkFrames = 40; + const float checkFrametime = 0.05; + const int randomChecksPerSlice = 5; + int i; + vec3_t dir, angles; + aas_clientmove_t move; + qboolean success; + float bestDist; + vec3_t bestDir; + + // are we out of the void? + if ( bs->areanum ) { + BotDefaultNode( bs ); + return qfalse; + } + + // try to navigate out of the void + + // if we have a current direction, validate it + + if ( bs->flags & BFL_MISCFLAG ) { + + // we are currently heading in a direction, verify to make sure it's still valid + if ( bs->last_attackShout < level.time - 250 ) { + VectorCopy( bs->lastLeaderPosition, dir ); + success = qfalse; + if ( trap_AAS_PredictClientMovement( &move, bs->client, bs->origin, -1, qfalse, dir, vec3_origin, checkFrames, 0, checkFrametime, SE_HITGROUNDDAMAGE | SE_HITGROUNDAREA | SE_STUCK | SE_GAP, -1, qfalse ) ) { + switch ( move.stopevent ) { + case SE_HITGROUNDAREA: + // success! + success = qtrue; + // keep it valid + bs->last_SparseDefense = level.time + 500; + break; + } + } + // + if ( !success ) { + bs->flags &= ~BFL_MISCFLAG; + } + } + + } + + // do we need a new direction? + + if ( !( bs->flags & BFL_MISCFLAG ) ) { + + bestDist = -1.0; + for ( i = 0; i < randomChecksPerSlice; i++ ) { + // choose a random direction + VectorClear( angles ); + angles[YAW] = rand() % 360; + AngleVectors( angles, dir, NULL, NULL ); + + // how far will this direction get us? + success = qfalse; + if ( trap_AAS_PredictClientMovement( &move, bs->client, bs->origin, -1, qfalse, dir, vec3_origin, checkFrames, 0, checkFrametime, SE_HITGROUNDDAMAGE | SE_HITGROUNDAREA | SE_STUCK | SE_GAP, -1, qfalse ) ) { + switch ( move.stopevent ) { + case SE_HITGROUNDAREA: + // success! + success = qtrue; + break; + } + } + // + if ( success ) { + bs->flags |= BFL_MISCFLAG; + bs->last_attackShout = level.time; + bs->last_SparseDefense = level.time + 200; + VectorCopy( dir, bs->lastLeaderPosition ); + break; + } else { + switch ( move.stopevent ) { + case SE_HITGROUNDDAMAGE: + case SE_GAP: + // bad direction + break; + default: + // Gordon: OPT: bleh.... + if ( bestDist < 0 || VectorDistanceSquared( bs->origin, move.endpos ) < SQR( bestDist ) ) { + bestDist = VectorDistance( bs->origin, move.endpos ); + VectorCopy( dir, bestDir ); + } + } + } + } + + if ( !( bs->flags & BFL_MISCFLAG ) && ( bestDist > 0.0 ) ) { + bs->last_SparseDefense = level.time + 200; + VectorCopy( bestDir, bs->lastLeaderPosition ); + } + + } + + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + //aim at the enemy + BotAimAtEnemy( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } + + // do we have a current direction? + if ( bs->last_SparseDefense > level.time ) { + // continue in that direction + trap_EA_Move( bs->client, bs->lastLeaderPosition, 400 ); + if ( bs->enemy < 0 ) { + vectoangles( bs->lastLeaderPosition, angles ); + trap_EA_View( bs->client, angles ); + } + } + + return qtrue; +} diff --git a/src/botai/ai_dmnet_mp.h b/src/botai/ai_dmnet_mp.h new file mode 100644 index 0000000..ed6c1b5 --- /dev/null +++ b/src/botai/ai_dmnet_mp.h @@ -0,0 +1,102 @@ +/* +=========================================================================== + +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: ai_dmnet_mp.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +void AIEnter_MP_PlantMine( bot_state_t *bs ); +void AIEnter_MP_Intermission( bot_state_t *bs ); +void AIEnter_MP_Observer( bot_state_t *bs ); +void AIEnter_MP_Respawn( bot_state_t *bs ); +void AIEnter_MP_Stand( bot_state_t *bs ); +void AIEnter_MP_Seek_ActivateEntity( bot_state_t *bs ); +void AIEnter_MP_Seek_NBG( bot_state_t *bs ); +void AIEnter_MP_Seek_LTG( bot_state_t *bs ); +void AIEnter_MP_Seek_Camp( bot_state_t *bs ); +void AIEnter_MP_Battle_Fight( bot_state_t *bs ); +void AIEnter_MP_Battle_Chase( bot_state_t *bs ); +void AIEnter_MP_Battle_Retreat( bot_state_t *bs ); +void AIEnter_MP_DynamiteTarget( bot_state_t *bs ); +void AIEnter_MP_SatchelChargeTarget( bot_state_t *bs ); +void AIEnter_MP_ConstructibleTarget( bot_state_t *bs ); +void AIEnter_MP_TouchTarget( bot_state_t *bs ); +void AIEnter_MP_DefendTarget( bot_state_t *bs ); +void AIEnter_MP_MedicRevive( bot_state_t *bs ); +void AIEnter_MP_MedicGiveHealth( bot_state_t *bs ); +void AIEnter_MP_DisarmDynamite( bot_state_t *bs ); +void AIEnter_MP_AvoidDanger( bot_state_t *bs ); +void AIEnter_MP_GiveAmmo( bot_state_t *bs ); +void AIEnter_MP_PanzerTarget( bot_state_t *bs ); +void AIEnter_MP_SniperSpot( bot_state_t *bs ); +void AIEnter_MP_ScanForLandmines( bot_state_t *bs ); +void AIEnter_MP_FixMG42( bot_state_t *bs ); +void AIEnter_MP_MG42Scan( bot_state_t *bs ); +void AIEnter_MP_MG42Mount( bot_state_t *bs ); +void AIEnter_MP_Script_MoveToMarker( bot_state_t *bs ); +void AIEnter_MP_MoveToAutonomyRange( bot_state_t *bs ); +void AIEnter_MP_PanzerTarget( bot_state_t *bs ); +void AIEnter_MP_AttackTarget( bot_state_t *bs ); +void AIEnter_MP_NavigateFromVoid( bot_state_t *bs ); +void AIEnter_MP_Battle_MobileMG42( bot_state_t *bs ); +int AINode_MP_PlantMine( bot_state_t *bs ); +int AINode_MP_Intermission( bot_state_t *bs ); +int AINode_MP_Observer( bot_state_t *bs ); +int AINode_MP_Respawn( bot_state_t *bs ); +int AINode_MP_Stand( bot_state_t *bs ); +int AINode_MP_Seek_ActivateEntity( bot_state_t *bs ); +int AINode_MP_Seek_NBG( bot_state_t *bs ); +int AINode_MP_Battle_Fight( bot_state_t *bs ); +int AINode_MP_Battle_Chase( bot_state_t *bs ); +int AINode_MP_Battle_Retreat( bot_state_t *bs ); +int AINode_MP_DynamiteTarget( bot_state_t *bs ); +int AINode_MP_SatchelChargeTarget( bot_state_t *bs ); +int AINode_MP_ConstructibleTarget( bot_state_t *bs ); +int AINode_MP_TouchTarget( bot_state_t *bs ); +int AINode_MP_DefendTarget( bot_state_t *bs ); +int AINode_MP_MedicRevive( bot_state_t *bs ); +int AINode_MP_MedicGiveHealth( bot_state_t *bs ); +int AINode_MP_DisarmDynamite( bot_state_t *bs ); +int AINode_MP_AvoidDanger( bot_state_t *bs ); +int AINode_MP_GiveAmmo( bot_state_t *bs ); +int AINode_MP_PanzerTarget( bot_state_t *bs ); +int AINode_MP_SniperSpot( bot_state_t *bs ); +int AINode_MP_FixMG42( bot_state_t *bs ); +int AINode_MP_MG42Scan( bot_state_t *bs ); +int AINode_MP_MG42Mount( bot_state_t *bs ); +int AINode_MP_ScanForLandmines( bot_state_t *bs ); +int AINode_MP_Script_MoveToMarker( bot_state_t *bs ); +int AINode_MP_MoveToAutonomyRange( bot_state_t *bs ); +int AINode_MP_AttackTarget( bot_state_t *bs ); +int AINode_MP_NavigateFromVoid( bot_state_t *bs ); +int AINode_MP_Battle_MobileMG42( bot_state_t *bs ); diff --git a/src/botai/ai_dmq3.c b/src/botai/ai_dmq3.c new file mode 100644 index 0000000..5802a72 --- /dev/null +++ b/src/botai/ai_dmq3.c @@ -0,0 +1,7572 @@ +/* +=========================================================================== + +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: ai_dmq3.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + + +#include "../game/g_local.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +#include "ai_team.h" +#include "ai_dmnet_mp.h" +#include "ai_dmgoal_mp.h" +#include "ai_matrix.h" +#include "ai_distances.h" +// +#include "chars.h" //characteristics +#include "inv.h" //indexes into the inventory +#include "syn.h" //synonyms +#include "match.h" //string matching types and vars + +#define IDEAL_ATTACKDIST 140 +//#define WEAPONINDEX_MACHINEGUN 2 + +#define DONT_PRINT_REPEATED_AI_ERRORS + +// BOT MOVEMENT AUTONOMY +float movementAutonomyRange[NUM_BMA] = +{ + 256, // LOW + 1024, // MEDIUM + 99999, // HIGH +}; + +// BOT MOVEMENT AUTONOMY for Single Player +float movementAutonomyRangeSP[NUM_BMA] = +{ + 200, // LOW + 350, // MEDIUM + 700, // HIGH +}; + +/////////////////////// +// +// COMBAT CONSTANTS +// +/////////////////////// + + +////////////////// +// from aasfile.h +//#define AREACONTENTS_MOVER 1024 +//#define AREACONTENTS_MODELNUMSHIFT 24 +//#define AREACONTENTS_MAXMODELNUM 0xFF +//#define AREACONTENTS_MODELNUM (AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT) +////////////////// +// +bot_waypoint_t botai_waypoints[MAX_BOTAIWAYPOINTS]; +bot_waypoint_t *botai_freewaypoints; + +//NOTE: not using a cvar which can be updated because the game should be reloaded anyway +int gametype; //game type + +// Rafael gameskill +//int gameskill; + +vmCvar_t bot_grapple; +vmCvar_t bot_rocketjump; +vmCvar_t bot_fastchat; +vmCvar_t bot_nochat; +vmCvar_t bot_testrchat; + +vec3_t lastteleport_origin; +float lastteleport_time; +//true when the map changed +int max_bspmodelindex; //maximum BSP model index + +//CTF flag goals +bot_goal_t ctf_redflag; +bot_goal_t ctf_blueflag; + +/* +================== +BotCarryingFlag +================== +*/ +qboolean BotCarryingFlag( int client ) { + if ( gametype < GT_WOLF ) { + return qfalse; + } + + if ( g_entities[client].health <= 0 ) { + return qfalse; + } + + if ( level.clients[client].ps.powerups[PW_REDFLAG] || level.clients[client].ps.powerups[PW_BLUEFLAG] ) { + return qtrue; + } + + return qfalse; +} + +extern vec3_t playerMins; +extern vec3_t playerMaxs; + +byte botCheckedAreas[65536]; + +/* +================== +BotFirstReachabilityArea +================== +*/ +int BotFirstReachabilityArea( int entnum, vec3_t origin, int *areas, int numareas, qboolean distCheck ) { + int i, best = 0; + trace_t tr; + vec3_t center; + float bestDist, dist; + vec3_t mins, maxs; + // + if ( entnum >= 0 && entnum < level.maxclients ) { + VectorCopy( playerMins, mins ); + mins[2] += 18; // STEPSIZE + VectorCopy( playerMaxs, maxs ); + } else { + VectorCopy( vec3_origin, mins ); + VectorCopy( vec3_origin, maxs ); + } + bestDist = 999999; + for ( i = 0; i < numareas; i++ ) { + if ( botCheckedAreas[areas[i]] ) { + continue; + } + botCheckedAreas[areas[i]] = 1; + if ( trap_AAS_AreaReachability( areas[i] ) ) { + // make sure this area is visible + if ( !trap_AAS_AreaWaypoint( areas[i], center ) ) { + trap_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 ) { + trap_Trace( &tr, origin, mins, maxs, center, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + if ( tr.fraction > .99f || tr.startsolid ) { // if we start in solid, ignore trace test + best = areas[i]; + bestDist = dist; + //if (dist < 128) { + // return best; + //} + } + } + } else { + trap_Trace( &tr, origin, mins, maxs, center, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + if ( tr.fraction > .99f || tr.startsolid ) { // if we start in solid, ignore trace test + return areas[i]; + } + } + } + } + // + return best; +} + +/* +================== +BotFirstLadderArea +================== +*/ +int BotFirstLadderArea( int entnum, int *areas, int numareas ) { + int i; + // + for ( i = 0; i < numareas; i++ ) { + if ( trap_AAS_AreaLadder( areas[i] ) ) { + return areas[i]; + } + } + // + return 0; +} + +/* +================== +BotPointAreaNum +================== +*/ +int BotPointAreaNum( int entnum, vec3_t origin ) { + int areanum, numareas, areas[50], bestarea = 0, i; + vec3_t end, start, ofs, mins, maxs; + float f; + gentity_t *ent = NULL; + #define BOTAREA_BOX_DIST 256 + #define BOTAREA_JIGGLE_DIST 32 + // + if ( entnum >= 0 && VectorCompare( origin, g_entities[entnum].botAreaPos ) ) { + return g_entities[entnum].botAreaNum; + } + + memset( botCheckedAreas, 0, sizeof( botCheckedAreas ) ); + + if ( entnum >= 0 ) { + ent = &g_entities[entnum]; + } + + // if this is a bot, and it's touching a ladder, do special handling + if ( ent && ent->client && ent->client->ps.pm_flags & PMF_LADDER ) { + // use the point only if its a ladder area + areanum = trap_AAS_PointAreaNum( origin ); + if ( areanum && !trap_AAS_AreaLadder( areanum ) ) { + areanum = 0; + } + if ( areanum ) { + bestarea = areanum; + goto done; + } + + // try a small box, and take a ladder area as preference + maxs[0] = 8; + maxs[1] = 8; + maxs[2] = 4; + VectorSubtract( origin, maxs, mins ); + VectorAdd( origin, maxs, maxs ); + numareas = trap_AAS_BBoxAreas( mins, maxs, areas, 50 ); + if ( numareas > 0 ) { + bestarea = BotFirstLadderArea( entnum, areas, numareas ); + } + if ( bestarea ) { + goto done; + } + + // try the actual point + areanum = trap_AAS_PointAreaNum( origin ); + if ( areanum && !trap_AAS_AreaReachability( areanum ) ) { + areanum = 0; + } + if ( areanum ) { + bestarea = areanum; + goto done; + } + } else { + // + areanum = trap_AAS_PointAreaNum( origin ); + if ( areanum && !trap_AAS_AreaReachability( areanum ) ) { + areanum = 0; + } + if ( areanum ) { + bestarea = areanum; + goto done; + } + // trace a line from below us, upwards, finding the first area the line touches + VectorCopy( origin, start ); + VectorCopy( origin, end ); + if ( ( entnum >= 0 ) && g_entities[entnum].inuse && g_entities[entnum].client ) { + end[2] += g_entities[entnum].client->ps.viewheight; + } + start[2] -= 30; + numareas = trap_AAS_TraceAreas( start, end, areas, NULL, 50 ); + if ( numareas > 0 ) { + bestarea = BotFirstReachabilityArea( entnum, origin, areas, numareas, qfalse ); + } + if ( bestarea ) { + goto done; + } + + // 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 = trap_AAS_BBoxAreas( mins, maxs, areas, 50 ); + if ( numareas > 0 ) { + bestarea = BotFirstReachabilityArea( entnum, origin, areas, numareas, qtrue ); + } + if ( bestarea ) { + goto done; + } + } + + // try using the players bounding box + if ( ( entnum >= 0 ) && g_entities[entnum].inuse && g_entities[entnum].client ) { + numareas = trap_AAS_BBoxAreas( g_entities[entnum].r.absmin, g_entities[entnum].r.absmax, areas, 50 ); + if ( numareas > 0 ) { + bestarea = BotFirstReachabilityArea( entnum, origin, areas, numareas, qtrue ); + } + if ( bestarea ) { + goto done; + } + } + + //@TODO. The following code seems to often cause bogus areanums to be returned. They are offset + // from the real areas, and this causes all sorts of bot stickiness. + + // 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_BOX_DIST * f; + for ( i = 0; i < 2; i++ ) ofs[i] -= BOTAREA_BOX_DIST * f; + // + numareas = trap_AAS_BBoxAreas( ofs, end, areas, 50 ); + if ( numareas > 0 ) { + bestarea = BotFirstReachabilityArea( entnum, origin, areas, numareas, qtrue ); + } + if ( bestarea ) { + goto done; + } + } + // +done: + if ( entnum >= 0 ) { + VectorCopy( origin, g_entities[entnum].botAreaPos ); + g_entities[entnum].botAreaNum = bestarea; + } + return bestarea; +} + +/* +=================== +BotReachableBBoxAreaNum +=================== +*/ +int BotReachableBBoxAreaNum( bot_state_t *bs, vec3_t absmin, vec3_t absmax ) { + int numareas, areas[64], sorted[64], bestarea = 0, i, j; + vec3_t center, v; + float dists[200], bestdist; + // + // find the area that is reachable from bs, and closest to the center of the box + numareas = trap_AAS_BBoxAreas( absmin, absmax, areas, 64 ); + // sort them by distance from center + VectorAdd( absmin, absmax, center ); + VectorScale( center, 0.5, center ); + + for ( i = 0; i < numareas; i++ ) { + trap_AAS_AreaWaypoint( areas[i], v ); + dists[i] = VectorDistanceSquared( center, v ); + } + + for ( i = 0; i < numareas; i++ ) { + bestdist = -1; + for ( j = 0; j < numareas; j++ ) { + if ( dists[j] > 0 && ( bestdist < 0 || dists[j] < bestdist ) ) { + bestdist = dists[j]; + bestarea = j; + } + } + dists[bestarea] = -1; + sorted[i] = areas[bestarea]; + } + + // now take the first area that we can reach + for ( i = 0; i < numareas; i++ ) { + if ( trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, sorted[i], bs->tfl ) ) { + return sorted[i]; + } + } + + return 0; +} + +/* +================== +BotFindNearbyGoal +================== +*/ + +static qboolean sDoNearbyGoalCheck( bot_state_t *bs, vec3_t loc, gentity_t *target ) { + int t, areanum; + bot_goal_t goal; + + #define MAX_NEARBY_DIST 512 + #define MAX_NEARBY_TIME 1500 + + // if it's close enough + if ( VectorDistanceSquared( bs->origin, loc ) > SQR( MAX_NEARBY_DIST ) ) { + return qfalse; + } + + // if it's not within travel time + areanum = trap_AAS_PointAreaNum( loc ); + if ( areanum || !trap_AAS_AreaReachability( areanum ) ) { + areanum = BotPointAreaNum( -1, loc ); + } + if ( !areanum ) { + return qfalse; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ); + + if ( !t || ( t > MAX_NEARBY_TIME ) ) { + return qfalse; + } + + // found one! + BotClearGoal( &goal ); + goal.areanum = areanum; + goal.entitynum = target->s.number; + VectorCopy( target->r.mins, goal.mins ); + VectorCopy( target->r.maxs, goal.maxs ); + VectorCopy( loc, goal.origin ); + + // always get it if it's close + if ( t > 200 && !BotGoalWithinMovementAutonomy( bs, &goal, BGU_LOW ) ) { + return qfalse; + } + + bs->nearbygoal = goal; + return qtrue; + +} + +// TAT 11/21/2002 +// Look for ammo and health triggers +qboolean BotFindNearbyTriggerGoal( bot_state_t *bs ) { + gentity_t *trav; + vec3_t loc; + int i; + char *goalnames[] = {"trigger_ammo", "trigger_heal", NULL}; + + for ( i = 0; goalnames[i]; i++ ) + { + // ammo + if ( i == 0 ) { + // does the bot need ammo? also checks if they need ammo and health PACKS + if ( !ClientNeedsAmmo( bs->client ) ) { + continue; + } + } + // health + else if ( i == 1 ) { + if ( BotHealthScale( bs->client ) >= 1.0 ) { + continue; + } + } + // + trav = NULL; + while ( ( trav = G_Find( trav, FOFS( classname ), goalnames[i] ) ) ) + { + // triggers don't have a location, they just have bounds + // so see how far we are from the center of the trigger + loc[0] = ( trav->r.mins[0] + trav->r.maxs[0] ) / 2.0f; + loc[1] = ( trav->r.mins[1] + trav->r.maxs[1] ) / 2.0f; + loc[2] = ( ( trav->r.mins[2] + trav->r.maxs[2] ) / 2.0f ) + 30.f; + + if ( sDoNearbyGoalCheck( bs, loc, trav ) ) { + return qtrue; + } + } + } + + return qfalse; +} + +qboolean BotFindNearbyGoal( bot_state_t *bs ) { + vec3_t org; + int i; + qboolean needAmmo; + qboolean needHealth; + + if ( bs->next_nearbygoal > level.time ) { + return qfalse; + } + bs->next_nearbygoal = level.time + 500 + rand() % 500; + + needAmmo = ClientNeedsAmmo( bs->client ); + needHealth = BotHealthScale( bs->client ) >= 1.0 ? qfalse : qtrue; + + for ( i = MAX_CLIENTS; i < level.num_entities; i++ ) { + gentity_t* ent = &g_entities[i]; + switch ( ent->s.eType ) { + case ET_ITEM: + { + gitem_t* item = &bg_itemlist[ent->s.modelindex]; + switch ( item->giType ) { + case IT_TEAM: + switch ( item->giType ) { + case PW_REDFLAG: + if ( bs->sess.sessionTeam == TEAM_AXIS ) { + continue; + } + break; + case PW_BLUEFLAG: + if ( bs->sess.sessionTeam == TEAM_ALLIES ) { + continue; + } + break; + default: + break; + } + break; + case IT_WEAPON: + if ( !needAmmo ) { + continue; + } + switch ( item->giType ) { + case WP_AMMO: + break; + default: + continue; + } + break; + case IT_HEALTH: + if ( !needHealth ) { + continue; + } + break; + default: + continue; + } + + if ( ent->r.ownerNum == bs->client && ent->botIgnoreTime > level.time ) { + continue; + } + VectorCopy( ent->r.currentOrigin, org ); + org[2] += 30; + + if ( sDoNearbyGoalCheck( bs, org, ent ) ) { + return qtrue; + } + } + case ET_SUPPLIER: + case ET_HEALER: + { + vec3_t loc; + if ( ent->s.eType == ET_HEALER && !needHealth ) { + continue; + } + if ( ent->s.eType == ET_SUPPLIER && !needAmmo ) { + continue; + } + + VectorAdd( ent->r.mins, ent->r.maxs, loc ); + VectorScale( loc, 0.5f, loc ); + loc[2] += 30.f; + + if ( sDoNearbyGoalCheck( bs, loc, ent ) ) { + return qtrue; + } + } + default: + continue; + } + + } + + return qfalse; +} + +/* +================== +ClientName +================== +*/ +char *ClientName( int client, char *name, int size ) { + char buf[MAX_INFO_STRING]; + + if ( client < 0 || client >= MAX_CLIENTS ) { + BotAI_Print( PRT_ERROR, "ClientName: client out of range\n" ); + return "[client out of range]"; + } + trap_GetConfigstring( CS_PLAYERS + client, buf, sizeof( buf ) ); + strncpy( name, Info_ValueForKey( buf, "n" ), size - 1 ); + name[size - 1] = '\0'; + Q_CleanStr( name ); + return name; +} + +/* +================== +ClientSkin +================== +*/ +char *ClientSkin( int client, char *skin, int size ) { + char buf[MAX_INFO_STRING]; + + if ( client < 0 || client >= MAX_CLIENTS ) { + BotAI_Print( PRT_ERROR, "ClientSkin: client out of range\n" ); + return "[client out of range]"; + } + trap_GetConfigstring( CS_PLAYERS + client, buf, sizeof( buf ) ); + strncpy( skin, Info_ValueForKey( buf, "model" ), size - 1 ); + skin[size - 1] = '\0'; + return skin; +} + +/* +================== +ClientFromName +================== +*/ +int ClientFromName( char *name ) { + int i; + char buf[MAX_INFO_STRING]; + static int maxclients; + + if ( !maxclients ) { + maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); + } + for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { + if ( !g_entities[i].inuse ) { + continue; + } + trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); + Q_CleanStr( buf ); + if ( !Q_stricmp( Info_ValueForKey( buf, "n" ), name ) ) { + return i; + } + } + return -1; +} + +/* +================== +stristr +================== +*/ +char *stristr( char *str, char *charset ) { + int i; + + while ( *str ) { + for ( i = 0; charset[i] && str[i]; i++ ) { + if ( toupper( charset[i] ) != toupper( str[i] ) ) { + break; + } + } + if ( !charset[i] ) { + return str; + } + str++; + } + return NULL; +} + +/* +================== +EasyClientName +================== +*/ +char *EasyClientName( int client, char *buf, int size ) { + int i; + char *str1, *str2, *ptr, c; + char name[128]; + + strcpy( name, ClientName( client, name, sizeof( name ) ) ); + for ( i = 0; name[i]; i++ ) name[i] &= 127; + //remove all spaces + for ( ptr = strstr( name, " " ); ptr; ptr = strstr( name, " " ) ) { + memmove( ptr, ptr + 1, strlen( ptr + 1 ) + 1 ); + } + //check for [x] and ]x[ clan names + str1 = strstr( name, "[" ); + str2 = strstr( name, "]" ); + if ( str1 && str2 ) { + if ( str2 > str1 ) { + memmove( str1, str2 + 1, strlen( str2 + 1 ) + 1 ); + } else { memmove( str2, str1 + 1, strlen( str1 + 1 ) + 1 );} + } + //remove Mr prefix + if ( ( name[0] == 'm' || name[0] == 'M' ) && + ( name[1] == 'r' || name[1] == 'R' ) ) { + memmove( name, name + 2, strlen( name + 2 ) + 1 ); + } + //only allow lower case alphabet characters + ptr = name; + while ( *ptr ) { + c = *ptr; + if ( ( c >= 'a' && c <= 'z' ) || + ( c >= '0' && c <= '9' ) || c == '_' ) { + ptr++; + } else if ( c >= 'A' && c <= 'Z' ) { + *ptr += 'a' - 'A'; + ptr++; + } else { + memmove( ptr, ptr + 1, strlen( ptr + 1 ) + 1 ); + } + } + strncpy( buf, name, size - 1 ); + buf[size - 1] = '\0'; + return buf; +} + +/* +============== +BotGotEnoughAmmoForWeapon +============== +*/ +qboolean BotGotEnoughAmmoForWeapon( bot_state_t *bs, int weapon ) { + int ammo, clip; + + // if this is a charged weapon, check that it is ready for use (soon) + if ( !BotWeaponCharged( bs, weapon ) ) { + return qfalse; + } + + ammo = bs->cur_ps.ammo[BG_FindAmmoForWeapon( weapon )]; + clip = bs->cur_ps.ammoclip[BG_FindClipForWeapon( weapon )]; + + // TODO!! check some kind of weapon list that holds the minimum requirements for each weapon + switch ( weapon ) { + default: + return (qboolean)( ( clip >= GetAmmoTableData( weapon )->uses ) || ( ammo >= GetAmmoTableData( weapon )->uses ) ); //----(SA) + } +} + +/* +============== +BotWeaponCharged +============== +*/ +#define WC_WEAPON_TIME_LEFT level.time - ps->classWeaponTime +#define WC_SOLDIER_TIME level.soldierChargeTime [team - TEAM_AXIS] +#define WC_ENGINEER_TIME level.engineerChargeTime [team - TEAM_AXIS] +#define WC_FIELDOPS_TIME level.lieutenantChargeTime [team - TEAM_AXIS] +#define WC_MEDIC_TIME level.medicChargeTime [team - TEAM_AXIS] +#define WC_COVERTOPS_TIME level.covertopsChargeTime [team - TEAM_AXIS] + +qboolean G_WeaponCharged( playerState_t* ps, team_t team, int weapon, int* skill ) { + switch ( weapon ) { + case WP_PANZERFAUST: + if ( ps->eFlags & EF_PRONE ) { + return qfalse; + } + + if ( skill[SK_HEAVY_WEAPONS] >= 1 ) { + if ( WC_WEAPON_TIME_LEFT < WC_SOLDIER_TIME * 0.66f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_SOLDIER_TIME ) { + return qfalse; + } + case WP_MORTAR_SET: + if ( skill[SK_HEAVY_WEAPONS] >= 1 ) { + if ( WC_WEAPON_TIME_LEFT < WC_SOLDIER_TIME * 0.33f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_SOLDIER_TIME * 0.5f ) { + return qfalse; + } + return qtrue; + case WP_SMOKE_BOMB: + case WP_SATCHEL: + if ( skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 2 ) { + if ( WC_WEAPON_TIME_LEFT < WC_COVERTOPS_TIME * 0.66f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_COVERTOPS_TIME ) { + return qfalse; + } + break; + + case WP_LANDMINE: + if ( skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 2 ) { + if ( WC_WEAPON_TIME_LEFT < ( WC_ENGINEER_TIME * 0.33f ) ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < ( WC_ENGINEER_TIME * 0.5f ) ) { + return qfalse; + } + break; + + case WP_DYNAMITE: + if ( skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 3 ) { + if ( WC_WEAPON_TIME_LEFT < ( WC_ENGINEER_TIME * 0.66f ) ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_ENGINEER_TIME ) { + return qfalse; + } + break; + + case WP_MEDKIT: + if ( skill[SK_FIRST_AID] >= 2 ) { + if ( WC_WEAPON_TIME_LEFT < WC_MEDIC_TIME * 0.15f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_MEDIC_TIME * 0.25f ) { + return qfalse; + } + break; + + case WP_AMMO: + if ( skill[SK_SIGNALS] >= 1 ) { + if ( WC_WEAPON_TIME_LEFT < WC_FIELDOPS_TIME * 0.15f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_FIELDOPS_TIME * 0.25f ) { + return qfalse; + } + break; + + case WP_SMOKE_MARKER: + if ( skill[SK_SIGNALS] >= 2 ) { + if ( WC_WEAPON_TIME_LEFT < WC_FIELDOPS_TIME * 0.66f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT < WC_FIELDOPS_TIME ) { + return qfalse; + } + break; + + case WP_MEDIC_ADRENALINE: + if ( WC_WEAPON_TIME_LEFT < WC_MEDIC_TIME ) { + return qfalse; + } + break; + + case WP_BINOCULARS: + switch ( ps->stats[ STAT_PLAYER_CLASS ] ) { + case PC_FIELDOPS: + if ( skill[SK_SIGNALS] >= 2 ) { + if ( WC_WEAPON_TIME_LEFT <= WC_FIELDOPS_TIME * 0.66f ) { + return qfalse; + } + } else if ( WC_WEAPON_TIME_LEFT <= WC_FIELDOPS_TIME ) { + return qfalse; + } + default: + return qfalse; + } + break; + + case WP_GPG40: + case WP_M7: + if ( WC_WEAPON_TIME_LEFT < WC_ENGINEER_TIME * 0.5f ) { + return qfalse; + } + break; + } + + return qtrue; +} + +qboolean BotWeaponCharged( bot_state_t *bs, int weapon ) { + return G_WeaponCharged( &bs->cur_ps, bs->sess.sessionTeam, weapon, bs->sess.skill ); +} + +/* +============== +BotWeaponOnlyUseIfInInRange + + returns qtrue if the given weapon should only be used if the enemy is within range (binocs, panzer, etc) +============== +*/ +qboolean BotWeaponOnlyUseIfInInRange( int weaponnum ) { + switch ( weaponnum ) { + case WP_BINOCULARS: + case WP_SMOKE_MARKER: + case WP_PANZERFAUST: + case WP_MOBILE_MG42: + case WP_MORTAR: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + return qtrue; + } + return qfalse; +} + +/* +============== +BotWeaponClosestDist +============== +*/ +float BotWeaponClosestDist( int weaponnum ) { + switch ( weaponnum ) { + case WP_PANZERFAUST: + return 512.0f; + + case WP_BINOCULARS: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + return 1024.0f; + + case WP_GRENADE_PINEAPPLE: + case WP_GRENADE_LAUNCHER: + return 128; + + case WP_M7: + case WP_GPG40: + return 256; + } + return 0; +} + +int BotTeamMatesNearEnemy( bot_state_t* bs ) { + int i, j; + vec_t* vec; + float range = SQR( G_GetWeaponDamage( WP_PANZERFAUST ) ); + int cnt = 0; + + if ( bs->enemy < 0 ) { + return 0; + } + + vec = BotGetOrigin( bs->enemy ); + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + + if ( j == bs->client ) { + continue; + } + + if ( !BotSameTeam( bs, j ) ) { + continue; + } + + if ( BotIsDead( &botstates[j] ) ) { + continue; + } + + if ( VectorDistanceSquared( vec, BotGetOrigin( j ) ) > range ) { + continue; + } + + cnt++; + } + + return cnt; +} + +/* +============== +BotWeaponWantScale +============== +*/ +float BotWeaponWantScale( bot_state_t *bs, weapon_t weapon ) { + qboolean moving; + + weapon_t clip = BG_FindClipForWeapon( weapon ); + weapon_t ammo = BG_FindAmmoForWeapon( weapon ); + + if ( !bs->cur_ps.ammo[ammo] && !bs->cur_ps.ammoclip[clip] ) { + return 0.f; + } + + if ( !BotWeaponCharged( bs, weapon ) ) { + return 0.f; + } + + moving = ( VectorLengthSquared( bs->cur_ps.velocity ) > SQR( 10 ) ); + + switch ( weapon ) { + case WP_KNIFE: + // for fun, have random bots use the knife in warmup scrumage + if ( level.warmupTime > level.time && !( ( bs->client + level.warmupTime / 100 ) % 5 ) ) { + return 2.0; + } + return 0.2; + + case WP_LUGER: + case WP_COLT: + return 0.4; + + case WP_SILENCER: + case WP_SILENCED_COLT: + return 0.45; + + case WP_AKIMBO_COLT: + case WP_AKIMBO_LUGER: + return 0.5; + + case WP_AKIMBO_SILENCEDCOLT: + case WP_AKIMBO_SILENCEDLUGER: + return 0.55; + + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + return 0.6; + + case WP_GPG40: + case WP_M7: + if ( bs->inventory[ENEMY_HORIZONTAL_DIST] > 512 ) { + return 1.0; + } + return 0.1; + + case WP_CARBINE: + case WP_GARAND: + case WP_FG42: + case WP_KAR98: + case WP_K43: + return 0.6; + + case WP_MOBILE_MG42: + if ( !moving && bs->inventory[ENEMY_HORIZONTAL_DIST] > 500 ) { + return 3.0; + } + return 0.3; + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + if ( ( !moving && bs->enemy > -1 ) && ( bs->inventory[ENEMY_HORIZONTAL_DIST] > 300 ) ) { + return 1.0; + } + return 0.1; + case WP_FLAMETHROWER: + if ( ( !moving || bs->enemy > -1 ) && bs->inventory[ENEMY_HORIZONTAL_DIST] < 800 ) { + return 1.0; + } + return 0.1; + case WP_PANZERFAUST: + if ( !moving || bs->enemy > -1 ) { + if ( bs->enemy >= 0 ) { + if ( BotTeamMatesNearEnemy( bs ) > 1 ) { + return 0.f; + } + } + + if ( bs->inventory[INVENTORY_HEALTH] < 15 || bs->inventory[ENEMY_HORIZONTAL_DIST] > 400 ) { + return 1.0; + } + } + return 0.1; + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + + if ( bs->enemy > -1 && bs->inventory[ENEMY_HORIZONTAL_DIST] < 300 ) { + if ( BotHealthScale( bs->client ) < 0.3 && ( bs->enemy < 0 || !BotCarryingFlag( bs->enemy ) ) ) { + return 2.0 * ( 1.0 - (float)bs->inventory[INVENTORY_HEALTH] / 40.0 ); // try and get a grenade off before death + } + if ( bs->inventory[ENEMY_HORIZONTAL_DIST] > 200 ) { + return 0.5; + } else { + return 0.3; + } + } else { + return 0.1; + } + break; + case WP_SMOKE_MARKER: + if ( bs->sess.playerType == PC_FIELDOPS && bs->enemy > -1 && ( bs->inventory[ENEMY_HORIZONTAL_DIST] < 400 ) ) { + if ( BG_GetSkyHeightAtPoint( BotGetOrigin( bs->enemy ) ) == MAX_MAP_SIZE ) { + return 0.f; + } + + return 1.f; + } + break; + case WP_BINOCULARS: + if ( !moving ) { + if ( bs->sess.playerType == PC_FIELDOPS ) { + if ( bs->enemy > -1 && ( bs->inventory[ENEMY_HORIZONTAL_DIST] < 400 ) ) { + if ( BG_GetSkyHeightAtPoint( BotGetOrigin( bs->enemy ) ) == MAX_MAP_SIZE ) { + return 0.f; + } + return 1.f; + } + return 0.f; + } else if ( bs->sess.playerType == PC_COVERTOPS ) { + return 1.f; + } + } else { + return 0.01; + } + break; + default: + break; + } + + // anything else must be non-combat + return 0.0; +} + + +/* +================== +BotBestFightWeapon +================== +*/ +int BotBestFightWeapon( bot_state_t *bs ) { + weapon_t bestWeapon; + int i, *ammo; + float wantScale, bestWantScale, dist, thisRange, bestRange; + qboolean inRange, bestInRange; + + ammo = bs->cur_ps.ammo; + bestWantScale = 0.0; + bestRange = 0.0; + bestWeapon = bs->weaponnum; // default to current weapon + bestInRange = qfalse; + + dist = -1; + if ( bs->enemy >= 0 ) { + dist = VectorDistance( bs->origin, BotGetOrigin( bs->enemy ) ); + } + + for ( i = 0; i < WP_NUM_WEAPONS; i++ ) { + if ( COM_BitCheck( bs->cur_ps.weapons, i ) ) { + + // check that our ammo is enough + if ( !BotGotEnoughAmmoForWeapon( bs, i ) ) { + continue; + } + + // if they are too close, dont use this weapon + if ( dist != -1 && BotWeaponClosestDist( i ) > dist ) { + continue; + } + + // if this is a scoped weapon, only use it if we aren't moving + if ( BotScopedWeapon( i ) && VectorLengthSquared( bs->cur_ps.velocity ) > SQR( 10 ) ) { + continue; + } + + // check the range + if ( ( thisRange = BotWeaponRange( bs, i ) ) >= dist ) { + inRange = qtrue; + } else { + // if this weapon should only be used if we are within range + if ( dist != -1 && BotWeaponOnlyUseIfInInRange( i ) ) { + continue; + } + inRange = qfalse; + } + + if ( !inRange && bestInRange ) { + continue; + } + + // + // get the wantScale for this weapon given the current circumstances (0.0 - 1.0) + wantScale = BotWeaponWantScale( bs, i ); + // + if ( ( inRange && !bestInRange ) || ( inRange && wantScale >= bestWantScale ) || ( !inRange && thisRange > bestRange ) ) { + bestWeapon = i; + bestWantScale = wantScale; + bestRange = thisRange; + bestInRange = inRange; + } + } + } + // + return bestWeapon; +} + +/* +================== +BotChooseWeapon +================== +*/ +void BotChooseWeapon( bot_state_t *bs ) { + int newweaponnum; + + if ( bs->cur_ps.weaponstate == WEAPON_RAISING || + bs->cur_ps.weaponstate == WEAPON_DROPPING || + bs->cur_ps.weaponDelay ) { + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + } else { + if ( ( newweaponnum = BotBestFightWeapon( bs ) ) ) { + + if ( bs->weaponnum != newweaponnum ) { + bs->weaponchange_time = trap_AAS_Time(); + } + + bs->weaponnum = newweaponnum; + //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum); + trap_EA_SelectWeapon( bs->client, bs->weaponnum ); + } + } +} + +/* +================== +BotCycleWeapon +================== + +Bot cycles to next weapon in inventory, and will use it until told to cycle again + +TAT 11/14/2002 + +*/ +void BotCycleWeapon( bot_state_t *bs ) { + int i; + int curWeapon = bs->weaponnum; + float wantScale; + + // loop through all the weapons, starting after the one we have equipped + for ( i = curWeapon + 1; i != curWeapon; i++ ) + { + // if we went off the end, start at the beginning + if ( i >= WP_NUM_WEAPONS ) { + i = 0; + // we have an endless loop here, when we have no weapons, our current weapon is 0, and we never hit the ending condition in the loop + // since it's set in here + if ( curWeapon == 0 ) { + break; + } + } + + // if we have this weapon + if ( COM_BitCheck( bs->cur_ps.weapons, i ) ) { + // if we don't have ammo for it, we can't choose it + if ( !BotGotEnoughAmmoForWeapon( bs, i ) ) { + continue; + } + + // get the wantScale for this weapon given the current circumstances (0.0 - 1.0) + wantScale = BotWeaponWantScale( bs, i ); + + // if the wantscale is positive, then it is a weapon + if ( wantScale > 0 ) { + break; + } + } + } + + if ( i != curWeapon ) { + // we found a new weapon + // set it as our selected weapon + bs->commandedWeapon = i; + bs->weaponnum = i; + trap_EA_SelectWeapon( bs->client, i ); + } +} + +/* +================== +BotSetupForMovement +================== +*/ +void BotSetupForMovement( bot_state_t *bs ) { + bot_initmove_t initmove; + + memset( &initmove, 0, sizeof( bot_initmove_t ) ); + VectorCopy( bs->cur_ps.origin, initmove.origin ); + VectorCopy( bs->cur_ps.velocity, initmove.velocity ); + VectorCopy( bs->cur_ps.origin, initmove.viewoffset ); + initmove.viewoffset[2] += bs->cur_ps.viewheight; + initmove.entitynum = bs->entitynum; + initmove.client = bs->client; + initmove.thinktime = bs->thinktime; + initmove.areanum = bs->areanum; + //set the onground flag + if ( bs->cur_ps.groundEntityNum != ENTITYNUM_NONE ) { + initmove.or_moveflags |= MFL_ONGROUND; + } + //set the teleported flag + if ( ( bs->cur_ps.pm_flags & PMF_TIME_KNOCKBACK ) && ( bs->cur_ps.pm_time > 0 ) ) { + initmove.or_moveflags |= MFL_TELEPORTED; + } + //set the waterjump flag + if ( ( bs->cur_ps.pm_flags & PMF_TIME_WATERJUMP ) && ( bs->cur_ps.pm_time > 0 ) ) { + initmove.or_moveflags |= MFL_WATERJUMP; + } + //set presence type + if ( bs->cur_ps.pm_flags & PMF_DUCKED ) { + initmove.presencetype = PRESENCE_CROUCH; + } else { initmove.presencetype = PRESENCE_NORMAL;} + // + if ( bs->walker > 0.5 ) { + initmove.or_moveflags |= MFL_WALK; + } + // + VectorCopy( bs->viewangles, initmove.viewangles ); + // + trap_BotInitMoveState( bs->ms, &initmove ); +} + +/* +================== +BotUpdateInventory +================== +*/ +void BotUpdateInventory( bot_state_t *bs ) { + //powerups + bs->inventory[INVENTORY_HEALTH] = bs->cur_ps.stats[STAT_HEALTH]; + if ( bs->target_goal.entitynum != -1 ) { + bs->inventory[GOAL_TRAVELTIME] = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, bs->target_goal.areanum, bs->tfl ); + } else { + bs->inventory[GOAL_TRAVELTIME] = 0; + } +/* bs->inventory[INVENTORY_TELEPORTER] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_TELEPORTER; + bs->inventory[INVENTORY_MEDKIT] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM] == MODELINDEX_MEDKIT; + bs->inventory[INVENTORY_REDFLAG] = bs->cur_ps.powerups[PW_REDFLAG] != 0; + bs->inventory[INVENTORY_BLUEFLAG] = bs->cur_ps.powerups[PW_BLUEFLAG] != 0;*/ +} + +/* +================== +BotUpdateBattleInventory +================== +*/ +void BotUpdateBattleInventory( bot_state_t *bs, int enemy ) { + vec3_t dir; + aas_entityinfo_t entinfo; + + BotEntityInfo( enemy, &entinfo ); + VectorSubtract( entinfo.origin, bs->origin, dir ); + bs->inventory[ENEMY_HEIGHT] = (int) dir[2]; + dir[2] = 0; + bs->inventory[ENEMY_HORIZONTAL_DIST] = (int) VectorLength( dir ); + //FIXME: add num visible enemies and num visible team mates to the inventory +} + +/* +================== +BotBattleUseItems +================== +*/ +void BotBattleUseItems( bot_state_t *bs ) { +/* + if (bs->inventory[INVENTORY_HEALTH] < 40) { + if (bs->inventory[INVENTORY_TELEPORTER] > 0) { + trap_EA_Use(bs->client); + } + if (bs->inventory[INVENTORY_MEDKIT] > 0) { + trap_EA_Use(bs->client); + } + } +*/ +} + +/* +================== +BotSetTeleportTime +================== +*/ +void BotSetTeleportTime( bot_state_t *bs ) { + if ( ( bs->cur_ps.eFlags ^ bs->last_eFlags ) & EF_TELEPORT_BIT ) { + bs->teleport_time = trap_AAS_Time(); + } + bs->last_eFlags = bs->cur_ps.eFlags; +} + +/* +================== +BotIsDead +================== +*/ +qboolean BotIsDead( bot_state_t *bs ) { + if ( bs->cur_ps.pm_flags & PMF_LIMBO ) { + return qtrue; + } + // RF, re-enabled these, they are required for the bots to respawn + if ( bs->cur_ps.pm_type == PM_DEAD ) { + return qtrue; + } + if ( g_entities[bs->client].health <= 0 ) { + return qtrue; + } + return qfalse; +} + +// Gordon: 27/11/02: check if the bot is prisoner of war +/* +================== +BotIsPOW +================== +*/ +qboolean BotIsPOW( bot_state_t *bs ) { + return bs->isPOW; +} + +/* +================== +BotIsObserver +================== +*/ +qboolean BotIsObserver( bot_state_t *bs ) { + char buf[MAX_INFO_STRING]; + if ( bs->cur_ps.pm_type == PM_SPECTATOR ) { + return qtrue; + } + trap_GetConfigstring( CS_PLAYERS + bs->client, buf, sizeof( buf ) ); + if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { + return qtrue; + } + return qfalse; +} + +/* +================== +BotIntermission +================== +*/ +qboolean BotIntermission( bot_state_t *bs ) { + //NOTE: we shouldn't look at the game code... + if ( level.intermissiontime ) { + return qtrue; + } + return ( bs->cur_ps.pm_type == PM_FREEZE || bs->cur_ps.pm_type == PM_INTERMISSION ); +} + + +/* +============== +BotInLava +============== +*/ +qboolean BotInLava( bot_state_t *bs ) { + vec3_t feet; + + VectorCopy( bs->origin, feet ); + feet[2] -= 23; + return ( trap_AAS_PointContents( feet ) & CONTENTS_LAVA ); +} + +/* +============== +BotInSlime +============== +*/ +qboolean BotInSlime( bot_state_t *bs ) { + vec3_t feet; + + VectorCopy( bs->origin, feet ); + feet[2] -= 23; + return ( trap_AAS_PointContents( feet ) & CONTENTS_SLIME ); +} + +/* +================== +EntityIsDead +================== +*/ +qboolean EntityIsDead( aas_entityinfo_t *entinfo ) { + if ( entinfo->number >= 0 && entinfo->number < MAX_CLIENTS ) { + if ( !g_entities[entinfo->number].inuse ) { + return qtrue; + } + if ( g_entities[entinfo->number].health <= 0 ) { + return qtrue; + } + } + return qfalse; +} + +/* +================== +EntityInLimbo +================== +*/ +qboolean EntityInLimbo( aas_entityinfo_t *entinfo ) { + if ( !g_entities[entinfo->number].client ) { + return qfalse; + } + + return ( g_entities[entinfo->number].client->ps.pm_flags & PMF_LIMBO ) ? qtrue : qfalse; +} + +/* +================== +EntityIsInvisible +================== +*/ +qboolean EntityIsInvisible( aas_entityinfo_t *entinfo ) { + return qfalse; +} + +/* +================== +EntityIsShooting +================== +*/ +qboolean EntityIsShooting( aas_entityinfo_t *entinfo ) { + if ( entinfo->flags & EF_FIRING ) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityIsChatting +================== +*/ +qboolean EntityIsChatting( aas_entityinfo_t *entinfo ) { + if ( entinfo->flags & EF_TALK ) { + return qtrue; + } + return qfalse; +} + +/* +================== +EntityHasQuad +================== +*/ +qboolean EntityHasQuad( aas_entityinfo_t *entinfo ) { + return qfalse; +} + +/* +================== +BotCreateWayPoint +================== +*/ +bot_waypoint_t *BotCreateWayPoint( char *name, vec3_t origin, int areanum ) { + bot_waypoint_t *wp; + vec3_t waypointmins = {-8, -8, -8}, waypointmaxs = {8, 8, 8}; + + wp = botai_freewaypoints; + if ( !wp ) { + BotAI_Print( PRT_WARNING, "BotCreateWayPoint: Out of waypoints\n" ); + return NULL; + } + botai_freewaypoints = botai_freewaypoints->next; + + Q_strncpyz( wp->name, name, sizeof( wp->name ) ); + VectorCopy( origin, wp->goal.origin ); + VectorCopy( waypointmins, wp->goal.mins ); + VectorCopy( waypointmaxs, wp->goal.maxs ); + wp->goal.areanum = areanum; + wp->next = NULL; + wp->prev = NULL; + return wp; +} + +/* +================== +BotFindWayPoint +================== +*/ +bot_waypoint_t *BotFindWayPoint( bot_waypoint_t *waypoints, char *name ) { + bot_waypoint_t *wp; + + for ( wp = waypoints; wp; wp = wp->next ) { + if ( !Q_stricmp( wp->name, name ) ) { + return wp; + } + } + return NULL; +} + +/* +================== +BotFreeWaypoints +================== +*/ +void BotFreeWaypoints( bot_waypoint_t *wp ) { + bot_waypoint_t *nextwp; + + for (; wp; wp = nextwp ) { + nextwp = wp->next; + wp->next = botai_freewaypoints; + botai_freewaypoints = wp; + } +} + +/* +================== +BotInitWaypoints +================== +*/ +void BotInitWaypoints( void ) { + int i; + + botai_freewaypoints = NULL; + for ( i = 0; i < MAX_BOTAIWAYPOINTS; i++ ) { + botai_waypoints[i].next = botai_freewaypoints; + botai_freewaypoints = &botai_waypoints[i]; + } +} + +/* +================== +TeamPlayIsOn +================== +*/ +int TeamPlayIsOn( void ) { + return qtrue; //( gametype == GT_TEAM || gametype == GT_CTF ); +} + +/* +================== +BotAggression + +FIXME: move this to external fuzzy logic + + NOTE!!: I made no changes to this code for wolf weapon awareness. (SA) +================== +*/ +float BotAggression( bot_state_t *bs ) { + //otherwise the bot is not feeling too good + return 0; +} + +/* +================== +BotWantsToRetreat +================== +*/ +int BotWantsToRetreat( bot_state_t *bs ) { +/* if (bs->engagementMatrix && (BotEngagementFunc(bs) & BOT_SEEK_COVER)) { + return qtrue; + } + if (bs->enemy > -1 && BotCarryingFlag(bs->enemy)) { + return qfalse; + } + if (BotAggression(bs) < 50) { + return qtrue; + }*/ + return qfalse; +} + +/* +================== +BotWantsToChase +================== +*/ +int BotWantsToChase( bot_state_t *bs ) { + if ( BotCarryingFlag( bs->client ) ) { + return qfalse; + } +/* if (bs->engagementMatrix && !(BotEngagementFunc(bs) & BOT_ROE_PURSUE)) { + return qfalse; + }*/ + if ( bs->enemy > -1 && BotCarryingFlag( bs->enemy ) ) { + return qtrue; + } +/* if (BotAggression(bs) > 50) { + return qtrue; + }*/ + return qfalse; +} + +/* +================== +BotWantsToHelp +================== +*/ +int BotWantsToHelp( bot_state_t *bs ) { + return qtrue; +} + +/* +================== +BotCanAndWantsToRocketJump +================== +*/ +int BotCanAndWantsToRocketJump( bot_state_t *bs ) { + + return qfalse; +/* + float rocketjumper; + + //if rocket jumping is disabled + if (!bot_rocketjump.integer) return qfalse; + //if no rocket launcher + if (bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0) return qfalse; + //if low on rockets + if (bs->inventory[INVENTORY_ROCKETS] < 3) return qfalse; + //never rocket jump with the Quad + if (bs->inventory[INVENTORY_QUAD]) return qfalse; + //if low on health + if (bs->inventory[INVENTORY_HEALTH] < 60) return qfalse; + //if not full health + if (bs->inventory[INVENTORY_HEALTH] < 90) { + //if the bot has insufficient armor + if (bs->inventory[INVENTORY_ARMOR] < 40) return qfalse; + } + rocketjumper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_WEAPONJUMPING, 0, 1); + if (rocketjumper < 0.5) return qfalse; + return qtrue; +*/ +} + +/* +================== +BotGoCamp +================== +*/ +void BotGoCamp( bot_state_t *bs, bot_goal_t *goal ) { +/* + float camper; + + //set message time to zero so bot will NOT show any message + bs->teammessage_time = 0; + //set the ltg type + bs->ltgtype = LTG_CAMP; + //set the team goal + memcpy(&bs->teamgoal, goal, sizeof(bot_goal_t)); + //get the team goal time + camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); + if (camper > 0.99) bs->teamgoal_time = 99999; + else bs->teamgoal_time = 120 + 180 * camper + random() * 15; + //set the last time the bot started camping + bs->camp_time = trap_AAS_Time(); + //the teammate that requested the camping + bs->teammate = 0; + //do NOT type arrive message + bs->arrive_time = 1; +*/ +} + +/* +================== +BotWantsToCamp +================== +*/ +/*int BotWantsToCamp(bot_state_t *bs) { + float camper; + int cs, traveltime, besttraveltime; + bot_goal_t goal, bestgoal; + + camper = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_CAMPER, 0, 1); + if (camper < 0.1) return qfalse; + //if the bot has a team goal + if (bs->ltgtype == LTG_TEAMHELP || + bs->ltgtype == LTG_TEAMACCOMPANY || + bs->ltgtype == LTG_DEFENDKEYAREA || + bs->ltgtype == LTG_GETFLAG || + bs->ltgtype == LTG_RUSHBASE || + bs->ltgtype == LTG_CAMP || + bs->ltgtype == LTG_CAMPORDER || + bs->ltgtype == LTG_PATROL) { + return qfalse; + } + //if camped recently + if (bs->camp_time > trap_AAS_Time() - 60 + 300 * (1-camper)) return qfalse; + // + if (random() > camper) { + bs->camp_time = trap_AAS_Time(); + return qfalse; + } + //if the bot isn't healthy anough + if (BotAggression(bs) < 50) return qfalse; + //the bot should have at least have the rocket launcher, the railgun or the bfg10k with some ammo +// if ((bs->inventory[INVENTORY_ROCKETLAUNCHER] <= 0 || bs->inventory[INVENTORY_ROCKETS < 10]) +// && (bs->inventory[INVENTORY_RAILGUN] <= 0 || bs->inventory[INVENTORY_SLUGS] < 10) +// && (bs->inventory[INVENTORY_BFG10K] <= 0 || bs->inventory[INVENTORY_BFGAMMO] < 10) +// ){ +// return qfalse; +// } + //find the closest camp spot + besttraveltime = 99999; + for (cs = trap_BotGetNextCampSpotGoal(0, &goal); cs; cs = trap_BotGetNextCampSpotGoal(cs, &goal)) { + traveltime = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, goal.areanum, bs->tfl); + if (traveltime && traveltime < besttraveltime) { + besttraveltime = traveltime; + memcpy(&bestgoal, &goal, sizeof(bot_goal_t)); + } + } + if (besttraveltime > 150) return qfalse; + //ok found a camp spot, go camp there + BotGoCamp(bs, &bestgoal); + // + return qtrue; +}*/ + +/* +================== +BotDontAvoid +================== +*/ +void BotDontAvoid( bot_state_t *bs, char *itemname ) { + bot_goal_t goal; + int num; + + num = trap_BotGetLevelItemGoal( -1, itemname, &goal ); + while ( num >= 0 ) { + trap_BotRemoveFromAvoidGoals( bs->gs, goal.number ); + num = trap_BotGetLevelItemGoal( num, itemname, &goal ); + } +} + +/* +================== +BotGoForPowerups +================== +*/ +void BotGoForPowerups( bot_state_t *bs ) { + + //don't avoid any of the powerups anymore + BotDontAvoid( bs, "Quad Damage" ); + BotDontAvoid( bs, "Regeneration" ); + BotDontAvoid( bs, "Battle Suit" ); + BotDontAvoid( bs, "Speed" ); + BotDontAvoid( bs, "Invisibility" ); + //BotDontAvoid(bs, "Flight"); + //reset the long term goal time so the bot will go for the powerup + //NOTE: the long term goal type doesn't change + +} + +/* +================== +BotRoamGoal +================== +*/ +void BotRoamGoal( bot_state_t *bs, vec3_t goal ) { + float len, r1, r2, sign, n; + int pc; + vec3_t dir, bestorg, belowbestorg; + bsp_trace_t trace; + + for ( n = 0; n < 10; n++ ) { + //start at the bot origin + VectorCopy( bs->origin, bestorg ); + r1 = random(); + if ( r1 < 0.8 ) { + //add a random value to the x-coordinate + r2 = random(); + if ( r2 < 0.5 ) { + sign = -1; + } else { sign = 1;} + bestorg[0] += sign * 700 * random() + 50; + } + if ( r1 > 0.2 ) { + //add a random value to the y-coordinate + r2 = random(); + if ( r2 < 0.5 ) { + sign = -1; + } else { sign = 1;} + bestorg[1] += sign * 700 * random() + 50; + } + //add a random value to the z-coordinate (NOTE: 48 = maxjump?) + bestorg[2] += 3 * 48 * random() - 2 * 48 - 1; + //trace a line from the origin to the roam target + BotAI_Trace( &trace, bs->origin, NULL, NULL, bestorg, bs->entitynum, MASK_SOLID ); + //direction and length towards the roam target + VectorSubtract( bestorg, bs->origin, dir ); + len = VectorNormalize( dir ); + //if the roam target is far away anough + if ( len > 200 ) { + //the roam target is in the given direction before walls + VectorScale( dir, len * trace.fraction - 40, dir ); + VectorAdd( bs->origin, dir, bestorg ); + //get the coordinates of the floor below the roam target + belowbestorg[0] = bestorg[0]; + belowbestorg[1] = bestorg[1]; + belowbestorg[2] = bestorg[2] - 800; + BotAI_Trace( &trace, bestorg, NULL, NULL, belowbestorg, bs->entitynum, MASK_SOLID ); + // + if ( !trace.startsolid ) { + trace.endpos[2]++; + pc = trap_PointContents( trace.endpos,bs->entitynum ); + if ( !( pc & CONTENTS_LAVA ) ) { //----(SA) modified since slime is no longer deadly +// if (!(pc & (CONTENTS_LAVA | CONTENTS_SLIME))) { + VectorCopy( bestorg, goal ); + return; + } + } + } + } + VectorCopy( bestorg, goal ); +} + +/* +================== +BotAttackMove +================== +*/ +bot_moveresult_t BotAttackMove( bot_state_t *bs, int tfl ) { + int movetype, i; + float attack_skill, jumper, croucher, dist, strafechange_time; + float attack_dist, attack_range; + vec3_t forward, backward, sideward, hordir, up = {0, 0, 1}, end; + aas_entityinfo_t entinfo; + bot_moveresult_t moveresult; + bot_goal_t goal; + trace_t tr; + aas_clientmove_t move; + + if ( bs->attackchase_time > trap_AAS_Time() ) { + //create the chase goal + goal.entitynum = bs->enemy; + goal.areanum = bs->lastenemyareanum; + VectorCopy( bs->lastenemyorigin, goal.origin ); + VectorSet( goal.mins, -8, -8, -8 ); + VectorSet( goal.maxs, 8, 8, 8 ); + //initialize the movement state + BotSetupForMovement( bs ); + //move towards the goal + trap_BotMoveToGoal( &moveresult, bs->ms, &goal, tfl ); + return moveresult; + } + // + memset( &moveresult, 0, sizeof( bot_moveresult_t ) ); + // + attack_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_ATTACK_SKILL, 0, 1 ); + jumper = 0.05 * trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_JUMPER, 0, 1 ); + croucher = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_CROUCHER, 0, 1 ); + //if the bot is really stupid + if ( attack_skill < 0.2 ) { + return moveresult; + } + // if the bot is in the air + if ( bs->cur_ps.groundEntityNum == ENTITYNUM_NONE ) { + return moveresult; + } + //initialize the movement state + BotSetupForMovement( bs ); + //get the enemy entity info + BotEntityInfo( bs->enemy, &entinfo ); + //direction towards the enemy + VectorSubtract( entinfo.origin, bs->origin, forward ); + //the distance towards the enemy + dist = VectorNormalize( forward ); + VectorNegate( forward, backward ); +// RF, removed, has it's own AI Node now +/* // if using mobile mg42, go prone if possible + if (bs->weaponnum == WP_MOBILE_MG42) { + if (dist > 1024.0f) { + int oldviewheight; + // check for obstruction at feet + oldviewheight = level.clients[bs->client].ps.viewheight; + level.clients[bs->client].ps.viewheight = PRONE_VIEWHEIGHT; + if (BotVisibleFromPos( bs->origin, bs->client, BotGetOrigin( bs->enemy ), bs->enemy, qtrue )) { + trap_EA_Prone( bs->client ); + level.clients[bs->client].ps.viewheight = oldviewheight; + return moveresult; + } else { + // just crouch + trap_EA_Crouch( bs->client ); + level.clients[bs->client].ps.viewheight = oldviewheight; + return moveresult; + } + } + } +*/ + // if the enemy is too far away + if ( dist > 1350.0f || !BotMoveWhileFiring( bs->weaponnum ) ) { + // crouching, no movement + trap_EA_Crouch( bs->client ); + return moveresult; + } + //walk, crouch or jump + movetype = MOVE_WALK; + // + if ( bs->attackcrouch_time < trap_AAS_Time() - 1 ) { + if ( ( g_gametype.integer != GT_SINGLE_PLAYER && g_gametype.integer != GT_COOP ) && ( random() < jumper ) ) { + movetype = MOVE_JUMP; + } + //wait at least one second before crouching again + else if ( bs->attackcrouch_time < trap_AAS_Time() - 1 && random() < croucher ) { + bs->attackcrouch_time = trap_AAS_Time() + croucher * 5; + } + } + if ( bs->attackcrouch_time > trap_AAS_Time() ) { + movetype = MOVE_CROUCH; + } else if ( bs->script.flags & BSFL_CROUCH ) { + movetype = MOVE_CROUCH; + } + //if the bot should jump + if ( movetype == MOVE_JUMP ) { + //if jumped last frame + if ( bs->attackjump_time > trap_AAS_Time() ) { + movetype = MOVE_WALK; + } else { + bs->attackjump_time = trap_AAS_Time() + 1; + } + } + attack_dist = IDEAL_ATTACKDIST; + attack_range = 40; +/* //if the bot is stupid + if (attack_skill <= 0.4) { + //just walk to or away from the enemy + if (dist > attack_dist + attack_range) { + if (trap_BotMoveInDirection(bs->ms, forward, 400, movetype)) return moveresult; + } + if (dist < attack_dist - attack_range) { + if (trap_BotMoveInDirection(bs->ms, backward, 400, movetype)) return moveresult; + } + return moveresult; + } +*/ + if ( BotSinglePlayer() || BotCoop() ) { + if ( dist > 512 ) { + // no attack move if they are far away + return moveresult; + } + } + //increase the strafe time + bs->attackstrafe_time += bs->thinktime; + //get the strafe change time + strafechange_time = 0.7 + ( 1 - attack_skill ) * 0.2; + if ( attack_skill > 0.7 ) { + strafechange_time += crandom() * 0.2; + } + //if the strafe direction should be changed + if ( bs->attackstrafe_time > strafechange_time ) { + //some magic number :) + if ( random() > 0.945 ) { + //flip the strafe direction + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + } + } + // + for ( i = 0; i < 2; i++ ) { + hordir[0] = forward[0]; + hordir[1] = forward[1]; + hordir[2] = 0; + VectorNormalize( hordir ); + //get the sideward vector + CrossProduct( hordir, up, sideward ); + //reverse the vector depending on the strafe direction + if ( bs->flags & BFL_STRAFERIGHT ) { + VectorNegate( sideward, sideward ); + } +/* //randomly go back a little + if (random() > 0.9) { + VectorAdd(sideward, backward, sideward); + } + else { + //walk forward or backward to get at the ideal attack distance + if (dist > attack_dist + attack_range) VectorAdd(sideward, forward, sideward); + else if (dist < attack_dist - attack_range) VectorAdd(sideward, backward, sideward); + } +*/ // + // check this direction for trigger_hurt + VectorMA( bs->origin, 80, sideward, end ); + trap_Trace( &tr, bs->origin, vec3_origin, vec3_origin, end, bs->client, CONTENTS_TRIGGER ); + if ( tr.fraction < 1.0 && !Q_stricmp( g_entities[tr.entityNum].classname, "trigger_hurt" ) ) { + // hit something that will hurt us + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + return moveresult; + } + // check for walking off a ledge + if ( trap_AAS_PredictClientMovement( &move, bs->client, bs->origin, -1, qtrue, sideward, vec3_origin, 10, 0, 0.1, SE_GAP | SE_LEAVEGROUND | SE_STUCK, -1, qfalse ) ) { + // if this is a bad movement, flip the direction, and wait util next frame when we can check that that move is ok + switch ( move.stopevent ) { + case SE_GAP: + case SE_LEAVEGROUND: + case SE_STUCK: + // bad movement + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + return moveresult; + } + } else { + // dont move + return moveresult; + } + // + //perform the movement + if ( trap_BotMoveInDirection( bs->ms, sideward, 190, movetype ) ) { + return moveresult; + } + //movement failed, flip the strafe direction + bs->flags ^= BFL_STRAFERIGHT; + bs->attackstrafe_time = 0; + } + //bot couldn't do any usefull movement +// bs->attackchase_time = AAS_Time() + 6; + return moveresult; +} + +/* +================== +BotSameTeam +================== +*/ +int BotSameTeam( bot_state_t *bs, int entnum ) { + // Gordon: this function is way too generic for this to work properly + +/* if (level.warmupTime > level.time) { + // fight everyone in warmup mode + return qfalse; + }*/ + + return OnSameTeam( &g_entities[bs->client], &g_entities[entnum] ); +} + +/* +================== +InFieldOfVision +================== +*/ +qboolean InFieldOfVision( vec3_t viewangles, float fov, vec3_t angles ) { + int i; + float diff, angle; + + for ( i = 0; i < 2; i++ ) { + angle = AngleMod( viewangles[i] ); + angles[i] = AngleMod( angles[i] ); + diff = angles[i] - angle; + if ( angles[i] > angle ) { + if ( diff > 180.0 ) { + diff -= 360.0; + } + } else { + if ( diff < -180.0 ) { + diff += 360.0; + } + } + if ( diff > 0 ) { + if ( diff > fov * 0.5 ) { + return qfalse; + } + } else { + if ( diff < -fov * 0.5 ) { + return qfalse; + } + } + } + return qtrue; +} + +/* +================== +BotEntInvisibleBySmokeBomb + +returns whether smoke from smoke bombs blocks vision from start to end +================== + + Mad Doc xkan, 11/25/2002 +*/ +#define MAX_SMOKE_RADIUS 320.0 +#define MAX_SMOKE_RADIUS_TIME 10000.0 +#define UNAFFECTED_BY_SMOKE_DIST SQR( 100 ) + +qboolean BotEntInvisibleBySmokeBomb( vec3_t start, vec3_t end ) { + gentity_t *ent = NULL; + vec3_t smokeCenter; + float smokeRadius; + + // if the target is close enough, vision is not affected by smoke bomb + if ( DistanceSquared( start,end ) < UNAFFECTED_BY_SMOKE_DIST ) { + return qfalse; + } + + while ( ( ent = G_FindSmokeBomb( ent ) ) ) { + if ( ent->s.effect1Time == 16 ) { + // xkan, the smoke has not really started yet, see weapon_smokeBombExplode + // and CG_RenderSmokeGrenadeSmoke + continue; + } + // check the distance + VectorCopy( ent->s.pos.trBase, smokeCenter ); + // raise the center to better match the position of the smoke, see + // CG_SpawnSmokeSprite(). + smokeCenter[2] += 32; + // smoke sprite has a maximum radius of 640/2. and it takes a while for it to + // reach that size, so adjust the radius accordingly. + smokeRadius = MAX_SMOKE_RADIUS * + ( ( level.time - ent->grenadeExplodeTime ) / MAX_SMOKE_RADIUS_TIME ); + if ( smokeRadius > MAX_SMOKE_RADIUS ) { + smokeRadius = MAX_SMOKE_RADIUS; + } + // if distance from line is short enough, vision is blocked by smoke + if ( DistanceFromLineSquared( smokeCenter, start, end ) < smokeRadius * smokeRadius ) { + return qtrue; + } + } + + return qfalse; +} + +/* +================== +BotEntityVisible + +returns visibility in the range [0, 1] taking fog and water surfaces into account +================== +*/ +float BotEntityVisible( int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent, vec3_t entorigin ) { + int i, contents_mask, passent, hitent, infog, inwater, otherinfog, pc; + float fogdist = 0, waterfactor, vis, bestvis; + bsp_trace_t trace; + aas_entityinfo_t entinfo; + vec3_t dir, entangles, start, end, middle; + int checkCount; + + //calculate middle of bounding box + BotEntityInfo( ent, &entinfo ); + VectorAdd( entinfo.mins, entinfo.maxs, middle ); + VectorScale( middle, 0.5, middle ); + if ( entorigin ) { + VectorAdd( entorigin, middle, middle ); + } else { + VectorAdd( entinfo.origin, middle, middle ); + } + + // if the entity is using an mg42, then move the trace upwards to avoid the gun + if ( g_entities[ent].s.eFlags & EF_MG42_ACTIVE ) { + middle[2] += 16; + } + + //check if entity is within field of vision + VectorSubtract( middle, eye, dir ); + vectoangles( dir, entangles ); + if ( fov < 360 && !InFieldOfVision( viewangles, fov, entangles ) ) { + return 0; + } + + // RF, check PVS + if ( !trap_InPVS( eye, middle ) ) { + return 0.f; + } + + // RF, if they are carrying the flag, then we can see them if they are in PVS + if ( BotCarryingFlag( ent ) ) { + return 1.f; + } + + // RF, if they are far away, and we arent using a sniper rifle, then only do 1 check + checkCount = 3; + if ( ( botstates[viewer].inuse && BotCanSnipe( &botstates[viewer], qtrue ) ) && ( VectorLengthSquared( dir ) > SQR( 1024 ) ) ) { + checkCount = 1; + } + + pc = trap_AAS_PointContents( eye ); + infog = ( pc & CONTENTS_SOLID ); + inwater = ( pc & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ); + + bestvis = 0; + for ( i = 0; i < checkCount; i++ ) { + // + contents_mask = MASK_SHOT & ~( CONTENTS_CORPSE ); //CONTENTS_SOLID|CONTENTS_PLAYERCLIP; + passent = viewer; + hitent = ent; + VectorCopy( eye, start ); + VectorCopy( middle, end ); + //if the entity is in water, lava or slime + if ( trap_AAS_PointContents( middle ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { + contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); + } + //if eye is in water, lava or slime + if ( inwater ) { + if ( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { + passent = ent; + hitent = viewer; + VectorCopy( middle, start ); + VectorCopy( eye, end ); + } + contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); + } + //trace from start to end + BotAI_Trace( &trace, start, NULL, NULL, end, passent, contents_mask ); + //if water was hit + waterfactor = 1.0; + if ( trace.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { + //if the water surface is translucent + //trace through the water + contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); + BotAI_Trace( &trace, trace.endpos, NULL, NULL, end, passent, contents_mask ); + waterfactor = 0.5; + } + + //if a full trace or the hitent was hit + if ( trace.fraction >= .99f || trace.ent == hitent || ( ( entinfo.flags & EF_TAGCONNECT ) && ( g_entities[trace.ent].nextTrain == g_entities[ent].tagParent ) ) ) { + + //check for fog, assuming there's only one fog brush where + //either the viewer or the entity is in or both are in + otherinfog = ( trap_AAS_PointContents( middle ) & CONTENTS_FOG ); + if ( infog && otherinfog ) { + VectorSubtract( trace.endpos, eye, dir ); + fogdist = VectorLength( dir ); + } else if ( infog ) { + VectorCopy( trace.endpos, start ); + BotAI_Trace( &trace, start, NULL, NULL, eye, viewer, CONTENTS_FOG ); + VectorSubtract( eye, trace.endpos, dir ); + fogdist = VectorLength( dir ); + } else if ( otherinfog ) { + VectorCopy( trace.endpos, end ); + BotAI_Trace( &trace, eye, NULL, NULL, end, viewer, CONTENTS_FOG ); + VectorSubtract( end, trace.endpos, dir ); + fogdist = VectorLength( dir ); + } else { + //if the entity and the viewer are not in fog assume there's no fog in between + fogdist = 0; + + } // else... + + //decrease visibility with the view distance through fog + vis = 1 / ( ( fogdist * fogdist * 0.001 ) < 1 ? 1 : ( fogdist * fogdist * 0.001 ) ); + + //if entering water visibility is reduced + vis *= waterfactor; + + // xkan 11/25/2002 - are there smoke (from smoke bombs) blocking the sight? + if ( vis > 0 && BotEntInvisibleBySmokeBomb( start, end ) ) { + vis = 0; + } + + if ( vis > bestvis ) { + bestvis = vis; + } + + // if pretty much no fog + if ( bestvis >= 0.95 ) { + return bestvis; + } + } + + //check bottom and top of bounding box as well + if ( i == 0 ) { + middle[2] += entinfo.mins[2]; + } else if ( i == 1 ) { + middle[2] += entinfo.maxs[2] - entinfo.mins[2]; + } + } + return bestvis; +} + +/* +================ +BotVisibleFromPos +================ +*/ +qboolean BotVisibleFromPos( vec3_t srcorigin, int srcnum, vec3_t destorigin, int destent, qboolean dummy ) { + vec3_t eye; + // + VectorCopy( srcorigin, eye ); + eye[2] += level.clients[srcnum].ps.viewheight; + // + if ( BotEntityVisible( srcnum, eye, vec3_origin, 360, destent, destorigin ) ) { + return qtrue; + } + // + return qfalse; +} + +/* +================ +BotCheckAttackAtPos + + FIXME: do better testing here +================ +*/ +qboolean BotCheckAttackAtPos( int entnum, int enemy, vec3_t pos, qboolean ducking, qboolean allowHitWorld ) { + vec3_t eye; + // + VectorCopy( pos, eye ); + eye[2] += level.clients[entnum].ps.viewheight; + // + if ( BotEntityVisible( entnum, eye, vec3_origin, 360, enemy, NULL ) ) { + return qtrue; + } + // + return qfalse; +} + +/* +================== +BotGetMovementAutonomyLevel +================== +*/ +int BotGetMovementAutonomyLevel( bot_state_t *bs ) { + return BMA_HIGH; +} + +/* +===================== +BotGetMovementAutonomyPos + + returns qtrue if a valid pos was returned +===================== +*/ +qboolean BotGetMovementAutonomyPos( bot_state_t *bs, vec3_t pos ) { + // if we are doing a scripted move, then make the destination our autonomy pos + if ( bs->script.frameFlags & BSFFL_MOVETOTARGET ) { + if ( bs->target_goal.entitynum == bs->script.entityNum ) { + //VectorCopy( g_entities[bs->leader].s.origin, pos ); + //VectorCopy( pos, bs->script.movementAutonomyPos ); // use this as the new autonomy pos + //VectorCopy( pos, bs->movementAutonomyPos ); // use this as the new autonomy pos + + // TAT 10/8/2002 - set in AIEnter_SP_Script_MoveToMarker + VectorCopy( bs->movementAutonomyPos, pos ); + return qtrue; + } + } + + // if we have been commanded by a player, then only override that + // if scripting has issued a "force" command + if ( bs->movementAutonomy != BMA_NOVALUE ) { + if ( !( bs->script.flags & BSFL_FORCED_MOVEMENT_AUTONOMY ) ) { + // if we are following a leader + VectorCopy( bs->movementAutonomyPos, pos ); + return qtrue; + } + } + + // if the scripting has set the autonomy + if ( bs->script.movementAutonomy != BMA_NOVALUE ) { + // if we are following a leader + VectorCopy( bs->script.movementAutonomyPos, pos ); + return qtrue; + } + + return qfalse; +} + +float BotGetFollowAutonomyDist( bot_state_t *bs ); + +/* +================== +BotGetRawMovementAutonomyRange +================== +*/ +float BotGetRawMovementAutonomyRange( bot_state_t *bs ) { + float range; + int level; + // + // RF, if we are in followme mode, then use special following ranges + // TAT 12/16/2002 - Don't use this range in SP here - if we want the follow distance, we check it explicitly + if ( ( bs->leader >= 0 ) && !G_IsSinglePlayerGame() ) { + range = BotGetFollowAutonomyDist( bs ); + return range; + } + // + level = BotGetMovementAutonomyLevel( bs ); + if ( level > BMA_HIGH ) { + G_Printf( "BotGetMovementAutonomyRange(): autonomy exceeds BMA_HIGH\n" ); + return 0; // should never happen + } else if ( level < BMA_NOVALUE ) { + G_Printf( "BotGetMovementAutonomyRange(): autonomy range less than BMA_NOVALUE\n" ); + return 0; // should never happen + } + + // Use a different value for SP + if ( g_gametype.integer == GT_SINGLE_PLAYER ) { + // Use the special SP table + range = movementAutonomyRangeSP[level]; + return range; + } + + // + range = movementAutonomyRange[level]; + return range; +} +/* +================== +BotGetMovementAutonomyRange +================== +*/ +float BotGetMovementAutonomyRange( bot_state_t *bs, bot_goal_t *goal ) { + float range; + + // Get the basic movement autonomy range + range = BotGetRawMovementAutonomyRange( bs ); + + // medium urgency goals let us wander slightly outside the autonomy range + if ( goal && goal->urgency >= BGU_MEDIUM ) { + range = ( range * ( 1.0 + 0.25 * ( goal->urgency - BGU_LOW ) ) ) + 256 * ( goal->urgency - BGU_LOW ); + } + + // + return range; +} + +// Start - TAT 9/18/2002 +// What distance should a bot be from its leader during a follow order? Based on autonomy +float BotGetFollowAutonomyDist( bot_state_t *bs ) { + int level; + + // The autonomy distances have nothing to do with how far a unit should be for following + // Let's make up some constants + static float followAutonomyDist[NUM_BMA] = + { + 128, // LOW + 256, // MEDIUM + 384, // HIGH + }; + + level = BotGetMovementAutonomyLevel( bs ); + if ( level > BMA_HIGH ) { + G_Printf( "BotGetMovementAutonomyRange(): autonomy exceeds BMA_HIGH\n" ); + return 0; // should never happen + } else if ( level < BMA_NOVALUE ) { + G_Printf( "BotGetMovementAutonomyRange(): autonomy range less than BMA_NOVALUE\n" ); + return 0; // should never happen + } + + return followAutonomyDist[level]; +} + +// Is a bot within the desired distance of its leader? +qboolean BotWithinLeaderFollowDist( bot_state_t *bs ) { + float dist; + gentity_t* leader; + + if ( !BotSinglePlayer() && !BotCoop() ) { + return qtrue; + } + + // if there's no leader, than sure, we're close enough + if ( bs->leader == -1 ) { + return qtrue; + } + + // get the leader + leader = BotGetEntity( bs->leader ); + + // how far is the bot from the leader? + dist = BotGetFollowAutonomyDist( bs ); + dist *= dist; + + // is the distance is greater than the follow dist + if ( dist > VectorDistanceSquared( bs->origin, leader->r.currentOrigin ) ) { + return qfalse; + } + + return qtrue; +} + +// End - TAT 9/18/2002 + +/* +================== +BotPointWithinMovementAutonomy +================== +*/ +qboolean BotPointWithinMovementAutonomy( bot_state_t *bs, bot_goal_t *goal, vec3_t point ) { + float dist; + vec3_t pos; + // + // no autonomy in MP + if ( !BotSinglePlayer() && !BotCoop() ) { + return qtrue; + } + // + if ( !BotGetMovementAutonomyPos( bs, pos ) ) { + return qtrue; // no autonomy yet defined + } + // + dist = VectorDistance( pos, point ); + // + // must be outside range of current goal origin + if ( dist > BotGetMovementAutonomyRange( bs, goal ) ) { + // also if we are following a leader, we are allowed to be near them + if ( bs->leader >= 0 ) { + dist = VectorDistance( g_entities[bs->leader].r.currentOrigin, point ); + if ( dist > BotGetMovementAutonomyRange( bs, goal ) ) { + return qfalse; + } + } else { + return qfalse; + } + } + // + // RF, had to remove, alcoves were causing this to fail, when they shouldn't be + //if ( !trap_InPVS( pos, point ) ) { + // return qfalse; + //} + // + return qtrue; +} + +qboolean BotPointWithinRawMovementAutonomy( bot_state_t *bs, vec3_t point ) { + float dist; + vec3_t pos; + + if ( !BotGetMovementAutonomyPos( bs, pos ) ) { + return qtrue; // no autonomy yet defined + + } + dist = VectorDistance( pos, point ); + + // must be outside range of current goal origin + if ( dist > BotGetRawMovementAutonomyRange( bs ) ) { + return qfalse; + } + + return qtrue; +} + + +/* +================== +BotGoalWithinMovementAutonomy +================== +*/ +qboolean BotGoalWithinMovementAutonomy( bot_state_t *bs, bot_goal_t *goal, int urgency ) { + int i; + botIgnoreGoal_t *ignoreTrav; + vec3_t pos; + + // no autonomy in MP + if ( !BotSinglePlayer() && !BotCoop() ) { + return qtrue; + } + // + if ( !BotGetMovementAutonomyPos( bs, pos ) ) { + return qtrue; // no autonomy yet defined + } + // + // if this goal is in our ignore list, then ignore it + for ( i = 0, ignoreTrav = bs->ignoreGoals; i < MAX_IGNORE_GOALS; i++, ignoreTrav++ ) { + if ( !ignoreTrav->expireTime || ignoreTrav->expireTime <= level.time ) { + continue; + } + if ( ignoreTrav->entityNum != goal->entitynum ) { + continue; + } + if ( ignoreTrav->areanum != goal->areanum ) { + continue; + } + // has our autonomy pos changed? + if ( !VectorCompare( pos, ignoreTrav->autonomyPos ) ) { + ignoreTrav->expireTime = 0; + continue; + } + // this ignoreGoal relates to this goal, so ignore it + return qfalse; + } + // + return BotPointWithinMovementAutonomy( bs, goal, goal->origin ); +} + +/* +================== +BotGoalForEntity + + returns qfalse if the goal is outside our movement autonomy range + + if bs is NULL, qtrue is always returned +================== +*/ +qboolean BotGoalForEntity( bot_state_t *bs, int entityNum, bot_goal_t *goal, int urgency ) { + vec3_t p; + + gentity_t *ent = BotGetEntity( entityNum ); + BotClearGoal( goal ); + + if ( ent ) { + goal->entitynum = entityNum; + if ( VectorLengthSquared( ent->r.absmax ) && ( /*ent->s.eType == ET_MOVER ||*/ ent->s.eType == ET_GENERAL ) ) { + VectorAdd( ent->r.absmax, ent->r.absmin, p ); + VectorScale( p, 0.5, p ); + + if ( bs && !BotGetReachableEntityArea( bs, entityNum, goal ) ) { + return qfalse; + } + } else { + VectorCopy( ent->r.currentOrigin, p ); + p[2] += 30; + VectorCopy( ent->r.mins, goal->mins ); + VectorCopy( ent->r.maxs, goal->maxs ); + } + + if ( !goal->areanum ) { + goal->areanum = trap_AAS_PointAreaNum( p ); + if ( !goal->areanum || !trap_AAS_AreaReachability( goal->areanum ) ) { + goal->areanum = BotPointAreaNum( -1, p ); + } + } + + // RF, drop out if no area is found + if ( !goal->areanum ) { + return qfalse; + } + } else { + // TAT - try server entity + g_serverEntity_t *serverEnt = GetServerEntity( entityNum ); + if ( serverEnt ) { + goal->entitynum = entityNum; + VectorCopy( serverEnt->origin, p ); + p[2] += 30; + if ( !( goal->areanum = BotGetArea( entityNum ) ) ) { + return qfalse; + } + } else { + return qfalse; + } + } + + goal->number = -1; + VectorCopy( p, goal->origin ); + goal->urgency = urgency; + + if ( bs ) { + if ( !BotGoalWithinMovementAutonomy( bs, goal, urgency ) ) { + return qfalse; + } + return qtrue; + } else { + return qtrue; + } +} + + + +// Start - TAT 8/26/2002 +// NOTE: The following 3 funcs to create and destroy the bot indicator objects should probably live somewhere else +// but I don't have time to figure out where +#define BOTINDICATORSET_POSTDELAY_TIME 1000 // can only set a waypoint once every 1 seconds + +void botindicator_think( gentity_t *ent ) { + + if ( level.time - ent->lastHintCheckTime < BOTINDICATORSET_POSTDELAY_TIME ) { + ent->nextthink = level.time + FRAMETIME; + return; + } + + ent->nextthink = level.time + FRAMETIME; + +} + +// a helper func, this returns the angle between 2 vectors +float sAngleBetweenVectors( vec3_t a, vec3_t b ) { + float val = DotProduct( a, b ) / sqrt( DotProduct( a, a ) * DotProduct( b, b ) ); + + if ( val <= -1.0f ) { + return (float)M_PI; + } else if ( val >= 1.0f ) { + return 0.0f; + } + + return acos( val ); +} + +// Gordon: 25/11/02: removing alot of the statics on these funcs, need them elsewhere, rename? +// Move a point back towards the player a bit, and down to the floor +void sAdjustPointTowardsPlayer( vec3_t playerLoc, vec3_t endPos, qboolean shouldLoop, vec3_t outPos /* Gordon: vec3_t is an array, no need for the pointer (was vec3_t*) */ ) { + vec3_t diff, normalizedDiff, scaled; + trace_t trace; + vec3_t point; + // put in a max number of times we'll do this loop + int timesThrough = 0; + + // amount to move back from the wall if our goal is on it + const float wallDist = 75.0f; + + // Vector pointing straight up + vec3_t floorNormal; + // setup the floor normal + VectorSet( floorNormal, 0, 0, 1 ); + + VectorCopy( endPos, point ); + + do + { + // gotta move the point + // New Loc = endpos + k * normalized(playerLoc - endpos) + + // diff = goal point - our start point + VectorSubtract( playerLoc, point, diff ); + + // normalize that vector + VectorNormalize2( diff, normalizedDiff ); + + // scale that vector by a constant amount, then stuff it in muzzlePoint + VectorScale( normalizedDiff, wallDist, scaled ); + + // and finally add back the original end point to get the final goal + VectorAdd( point, scaled, outPos ); + + // Drop the point to the floor, we don't want the bots trying to climb the walls or anything + // Started with some code from BotDropToFloor + + // We're going to do a trace from our goal point to a new location that is way below the old one + // the trace will stop when it hits something solid + VectorSet( diff, outPos[0], outPos[1], outPos[2] - 4096 ); + trap_Trace( &trace, outPos, NULL, NULL, diff, -1, MASK_PLAYERSOLID ); + + // now copy the result of the trace into our loc + VectorCopy( trace.endpos, outPos ); + + VectorCopy( trace.endpos, point ); + timesThrough++; + // only loop if we're supposed to, and don't do it more than 10 times + } while ( shouldLoop && ( timesThrough < 10 ) && sAngleBetweenVectors( floorNormal, trace.plane.normal ) >= M_PI / 4 ); +} + +// END - TAT 9/16/2002 + +/* +===================== +BotCheckMovementAutonomy + + if we wander outside out movement autonomy, it better be for a bloody good reason + + returns qtrue if we have moved outside autonomy range, and need to get back +===================== +*/ +qboolean BotCheckMovementAutonomy( bot_state_t *bs, bot_goal_t *goal ) { + // no autonomy in MP + if ( !BotSinglePlayer() && !BotCoop() ) { + return qfalse; + } + + // is our current goal too important to abort? + if ( goal->urgency == BGU_MAXIMUM ) { + return qfalse; + } + + // are we outside range? + if ( !BotPointWithinMovementAutonomy( bs, goal, bs->origin ) ) { + return qtrue; + } + + // is our goal outside the range? + if ( !VectorCompare( vec3_origin, goal->origin ) && !BotPointWithinMovementAutonomy( bs, goal, goal->origin ) ) { + return qtrue; + } + + // everything is ok + return qfalse; +} + +/* +================= +BotIgnoreGoal +================= +*/ +void BotIgnoreGoal( bot_state_t *bs, bot_goal_t *goal, int duration ) { + int i; + botIgnoreGoal_t *ignoreTrav, *ignoreOldest = NULL; + vec3_t pos; + + // no autonomy in MP + if ( !BotSinglePlayer() && !BotCoop() ) { + return; + } + + if ( !BotGetMovementAutonomyPos( bs, pos ) ) { + return; // no autonomy yet defined + } + + // are we sure we aren't already ignoring this goal? + for ( i = 0, ignoreTrav = bs->ignoreGoals; i < MAX_IGNORE_GOALS; i++, ignoreTrav++ ) { + if ( goal->entitynum >= 0 && ignoreTrav->entityNum == goal->entitynum ) { + // found a match, update expiryTime and get out of here + ignoreTrav->areanum = goal->areanum; + ignoreTrav->entityNum = goal->entitynum; + VectorCopy( pos, ignoreTrav->autonomyPos ); + ignoreTrav->expireTime = level.time + duration; + return; + } + } + + // find a free slot + for ( i = 0, ignoreTrav = bs->ignoreGoals; i < MAX_IGNORE_GOALS; i++, ignoreTrav++ ) { + if ( ignoreTrav->expireTime >= level.time ) { + if ( ( ignoreTrav->expireTime < level.time + duration ) && ( !ignoreOldest || ( ignoreOldest->expireTime < ignoreTrav->expireTime ) ) ) { + ignoreOldest = ignoreTrav; + } + continue; + } + // this slot is free + ignoreTrav->areanum = goal->areanum; + ignoreTrav->entityNum = goal->entitynum; + VectorCopy( pos, ignoreTrav->autonomyPos ); + ignoreTrav->expireTime = level.time + duration; + return; + } +} + +/* +================== +BotDangerousGoal + + returns qtrue if the given goal is in a dangerous area (dynamite, grenade, etc) +================== +*/ +#define MAX_DANGER_ENTS 64 + +qboolean BotDangerousGoal( bot_state_t *bs, bot_goal_t *goal ) { + int i, j; + vec3_t bangPos; + float radius = 0.0; + + gentity_t *trav; + int dangerEnts[MAX_DANGER_ENTS]; + int dangerEntsCount = 0; + + if ( bs->last_dangerousgoal > level.time - 300 ) { + return qfalse; + } + bs->last_dangerousgoal = level.time + rand() % 100; + + if ( dangerEntsCount < MAX_DANGER_ENTS ) { + if ( level.time - level.clients[bs->client].lastConstructibleBlockingWarnTime < 5000 ) { + dangerEnts[dangerEntsCount] = level.clients[bs->client].lastConstructibleBlockingWarnEnt; + dangerEntsCount++; + } + } + + trav = g_entities; + for ( j = 0; j < level.num_entities; j++, trav++ ) { + if ( !trav->inuse ) { + continue; + } + + switch ( trav->s.eType ) { + case ET_FLAMETHROWER_CHUNK: + break; + case ET_PLAYER: + if ( trav->client ) { + // is this player dangerous? + if ( !( trav->client->ps.weapon == WP_PANZERFAUST && trav->client->ps.weaponDelay ) ) { + continue; + } + } + break; + case ET_MISSILE: + switch ( trav->methodOfDeath ) { + case MOD_ARTY: + break; + case MOD_DYNAMITE: + if ( trav->s.teamNum >= 4 ) { + continue; // not armed + } + break; + case MOD_LANDMINE: + if ( !G_LandmineArmed( trav ) ) { + continue; + } + + if ( G_LandmineTeam( trav ) != bs->sess.sessionTeam ) { + if ( !G_LandmineSpotted( trav ) ) { + continue; + } + } + + if ( goal->entitynum == trav->s.number ) { + continue; + } + case MOD_PANZERFAUST: + case MOD_GRENADE: + case MOD_GRENADE_LAUNCHER: + break; + default: + continue; + } + break; + default: + continue; + } + + // save this to the cache + dangerEnts[dangerEntsCount] = j; + dangerEntsCount++; + + if ( dangerEntsCount >= MAX_DANGER_ENTS ) { + break; // too many + } + } + + for ( i = 0; i < dangerEntsCount; i++ ) { + trav = &g_entities[dangerEnts[i]]; + + switch ( trav->s.eType ) { + case ET_MISSILE: + switch ( trav->methodOfDeath ) { + case MOD_PANZERFAUST: + case MOD_GRENADE: + case MOD_GRENADE_LAUNCHER: + if ( trav->nextthink > level.time + ( 2000 + 8 * trav->splashRadius ) ) { + continue; + } + if ( !G_PredictMissile( trav, trav->nextthink - level.time, bangPos, trav->methodOfDeath == MOD_PANZERFAUST ? qfalse : qtrue ) ) { + // not dangerous + continue; + } + break; + default: + VectorCopy( trav->r.currentOrigin, bangPos ); + break; + } + break; + default: + VectorCopy( trav->r.currentOrigin, bangPos ); + break; + } + + if ( !trap_InPVS( bs->origin, bangPos ) ) { + continue; + } + + // if the bot is zoomed, they cant see it unless they are looking at it + if ( BG_IsScopedWeapon( bs->cur_ps.weapon ) ) { + vec3_t dir, ang; + + VectorSubtract( trav->r.currentOrigin, bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, ang ); + if ( !InFieldOfVision( bs->viewangles, 15, ang ) ) { + continue; + } + } + + switch ( trav->s.eType ) { + case ET_FLAMETHROWER_CHUNK: + radius = 128; + break; + case ET_PLAYER: + radius = 512; + break; + case ET_CONSTRUCTIBLE: + radius = 32; + break; + case ET_MISSILE: + switch ( trav->methodOfDeath ) { + case MOD_ARTY: + radius = 550; + break; + case MOD_DYNAMITE: + case MOD_LANDMINE: + case MOD_PANZERFAUST: + case MOD_GRENADE: + case MOD_GRENADE_LAUNCHER: + radius = trav->splashRadius; + break; + default: // rain - default + radius = 0; + break; + } + break; + default: // rain - default + radius = 0; + break; + } + + // are we far enough away from it now? + if ( VectorDistanceSquared( bs->origin, bangPos ) > SQR( radius + 500 ) ) { + continue; + } + + // + // will it explode near the goal? + if ( trav->s.eType == ET_CONSTRUCTIBLE || ( VectorDistanceSquared( bangPos, goal->origin ) < SQR( radius + 90 ) ) || ( VectorDistanceSquared( bangPos, bs->origin ) < SQR( radius + 90 ) ) ) { + // we must avoid this danger + BotClearGoal( &bs->avoid_goal ); + bs->avoid_goal.entitynum = trav->s.number; + bs->avoid_goal.number = radius; + if ( trav->s.eType == ET_MISSILE && trav->methodOfDeath == MOD_ARTY ) { + bs->avoid_goal.goalEndTime = level.time + 15000; + } else { + bs->avoid_goal.goalEndTime = level.time + 100; + } + VectorCopy( bangPos, bs->avoid_goal.origin ); + bs->avoid_spawnCount = trav->spawnCount; + + return qtrue; + } + } + + return qfalse; +} + + + +/* +=================== +BotNoLeaderPenalty +=================== + + We can have a penalty associated with not being near a leader. + 0 == no penalty, 1 == max penalty + +*/ +float BotNoLeaderPenalty( bot_state_t *bs ) { + //@COOPTODO. Make this look for any allied player + // Distance to nearest allied player + float distanceToPlayer = 0; + + // What sort of penalty do we get for being away from a leader + float noLeaderPenalty = 0; + + // How far are we from the player? + distanceToPlayer = VectorDistance( bs->origin, g_entities[0].r.currentOrigin ); + + // If we're really close, or we're Nazis, don't bother + if ( ( distanceToPlayer <= kBOT_NEAR_LEADER_DISTANCE ) + || ( bs->mpTeam == TEAM_AXIS ) + ) { + // Jsut use the entity damage ratio + return 0; + + } + + // If we're really far, max out the farness + if ( distanceToPlayer >= kBOT_FAR_FROM_LEADER_DISTANCE ) { + // Just use the max distance + distanceToPlayer = kBOT_FAR_FROM_LEADER_DISTANCE; + + } // if (distanceToPlayer >= kBOT_FAR_FROM_LEADER_DISTANCE)... + + // What's our penalty? + noLeaderPenalty = ( distanceToPlayer - kBOT_NEAR_LEADER_DISTANCE ) + / ( kBOT_FAR_FROM_LEADER_DISTANCE - kBOT_NEAR_LEADER_DISTANCE ); + + // Send the penalty back + return noLeaderPenalty; + +} + + + +/* +================== +BotFindLeadersEnemy + +Inherit a target from the leader, if one exists. +================== +*/ +/*int BotFindLeadersEnemy(bot_state_t *bs, int curenemy, aas_entityinfo_t *entinfo) +{ + // + // if we dont have a curenemy, and our leader has an enemy, we should know about it + if (curenemy < 0 && bs->leader_tagent >= 0 && botstates[bs->leader_tagent].inuse) { + if (botstates[bs->leader_tagent].enemy >= 0) { + BotEntityInfo(botstates[bs->leader_tagent].enemy, entinfo); + if (!EntityIsDead(entinfo)) { + // if we can see our leader + if (BotEntityVisible( bs->client, bs->eye, bs->viewangles, 360, bs->leader_tagent, BotGetOrigin(bs->leader_tagent) ) ) { + //found an enemy + bs->enemy = botstates[bs->leader_tagent].enemy; + // if they aren't visible, and we dont want to chase, then ignore them + if (!BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL) && !BotWantsToChase(bs)) { + bs->enemy = -1; + } else { + if (curenemy >= 0) + bs->enemysight_time = trap_AAS_Time() - 2; + else + bs->enemysight_time = trap_AAS_Time(); +// bs->enemysuicide = qfalse; + bs->enemydeath_time = 0; + bs->last_findenemy_enemy = bs->enemy; + if (!bs->last_enemysight || (bs->last_enemysight < level.time - 10000)) { + Bot_ScriptEvent( bs->client, "enemysight", !(g_entities[bs->enemy].r.svFlags & SVF_BOT) ? g_entities[bs->enemy].client->pers.netname : g_entities[bs->enemy].scriptName ); + } + bs->last_enemysight = level.time; + return qtrue; + } + } + } + } + } + + // We did not inherit a target from our leader + return qfalse; + +} // int BotFindLeadersEnemy(bot_state_t *bs, int curenemy) ... */ + +/*===================== +change the bot's alert state + + return whether the alert state was changed +=====================*/ +qboolean ChangeBotAlertState( bot_state_t *bs, aistateEnum_t newAlertState, qboolean force ) { + if ( force ) { // should only be used by script action + aistateEnum_t oldState = bs->alertState; + bs->alertState = newAlertState; + bs->alertStateChangeTime = level.time; + bs->alertStateSetTime = level.time; + return ( bs->alertState != oldState ); + } + + if ( newAlertState != bs->alertState + && level.time > bs->alertStateAllowChangeTime + // if we are relaxing, make sure we don't relax too quickly, this would prevent model from + // twitching in some weird situations (alert->combat->alert->combat in rapid fire fashion). + && ( newAlertState > bs->alertState || level.time - bs->alertStateChangeTime > 2000 ) ) { + bs->alertState = newAlertState; + bs->alertStateChangeTime = level.time; + bs->alertStateSetTime = level.time; + return qtrue; + } else if ( newAlertState == bs->alertState ) { + // record the time although we simply reaffirmed our current alert state. + bs->alertStateSetTime = level.time; + } + + return qfalse; +} + +void BotUpdateAlertStateSquadSensingInfo( bot_state_t *bs, qboolean canSeeTarget, qboolean heardFootSteps, qboolean heardShooting ) { +} + + + +/* +================ +BotGetEntitySurfaceSoundCoefficient + +Return a modifier for how loud a bot is depending on the surface they're moving on + +================ +*/ +float BotGetEntitySurfaceSoundCoefficient( int clientNum ) { + if ( g_entities[clientNum].surfaceFlags & SURF_NOSTEPS ) { + return 0; + } + if ( g_entities[clientNum].surfaceFlags & SURF_METAL ) { + return 2.0f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_WOOD ) { + return 1.5f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_GRASS ) { + return 0.6f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_GRAVEL ) { + return 1.2f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_ROOF ) { + return 1.3f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_SNOW ) { + return 1.0f; + } + + if ( g_entities[clientNum].surfaceFlags & SURF_CARPET ) { + return 0.9f; + } + return 1.0f; +} + + + + + + +// +// BotIsValidTarget +// +// Description: Is this target at all valid? +// Written: 10/31/2002 +// +qboolean BotIsValidTarget +( + bot_state_t *bs, + int target, + int curenemy +) { + // Local Variables //////////////////////////////////////////////////////// + + // Info about the target + aas_entityinfo_t entinfo; + + /////////////////////////////////////////////////////////////////////////// + + // Fill in the info for this potential target + BotEntityInfo( target, &entinfo ); + + // We are not our own enemy. Ever. At least in the game. In real life, it + // happens all the time. + if ( target == bs->client ) { + return qfalse; + } + + // if it's the current enemy, don't do more analysis + if ( target == curenemy ) { + return qfalse; + } + + // Fill in the info for this potential target + BotEntityInfo( target, &entinfo ); + + // Skip clients who are no longer good. + if ( !entinfo.valid ) { + return qfalse; + } + + // See if the enemy has a notarget on it, and skip if so. + if ( g_entities[target].flags & FL_NOTARGET ) { + return qfalse; + } + + //if on the same team + if ( BotSameTeam( bs, target ) ) { + return qfalse; + } + + // xkan, 1/6/2003 - if target is civilian + if ( g_entities[target].client->isCivilian ) { + return qfalse; // don't target civilian + + } + //if the enemy isn't dead and the enemy isn't the bot self + if ( EntityIsDead( &entinfo ) || entinfo.number == bs->entitynum ) { + return qfalse; + } + + //if the enemy is invisible and not shooting + if ( EntityIsInvisible( &entinfo ) && !EntityIsShooting( &entinfo ) ) { + return qfalse; + } + + // TAT 10/10/2002 + // If it's disguised, don't target them + if ( g_entities[target].client->ps.powerups[PW_OPS_DISGUISED] ) { + return qfalse; + } + + // If we are not mounted on an MG42, or sniping, then ignore clients that are not in a valid area + if ( !( bs->cur_ps.eFlags & EF_MG42_ACTIVE ) && !( bs->flags & BFL_SNIPING ) ) { + // if they are not in a valid area + if ( !BotGetArea( target ) ) { + return qfalse; + } + + } // if (!(bs->script.flags & BSFL_MOUNT_MG42))... + + // He's OK to use as a target + return qtrue; +} +// +// BotIsValidTarget +// + + + +// +// BotFindEnemies +// +// Description: Set up our danger spots +// Written: 12/26/2002 +// +void BotFindEnemies +( + bot_state_t *bs, + int *dangerSpots, + int *dangerSpotCount +) { + int i; + float dist; + aas_entityinfo_t entinfo; + vec3_t dir; + + // Loop through all potential targets + for ( i = 0; i < level.maxclients; i++ ) + { + + // Fill in the info for this potential target + BotEntityInfo( i, &entinfo ); + + // Is it a valid target? Puts all checks in one place + if ( !BotIsValidTarget( bs, i, -1 ) ) { + continue; + } + + //calculate the distance towards the enemy + VectorSubtract( entinfo.origin, bs->origin, dir ); + dist = VectorLength( dir ); + + // if this enemy is too far, skip her + if ( dist > kBOT_MAX_RETREAT_ENEMY_DIST ) { + continue; + } + + // Add this guy's area to the list + dangerSpots[*dangerSpotCount] = BotGetArea( i ); + + // Record that we have one more guy + ( *dangerSpotCount )++; + + } + +} +// +// BotFindEnemies +// + + +float BotWeaponRange( bot_state_t *bs, int weaponnum ) { + switch ( weaponnum ) { + // dont use unless manually forced + case WP_PLIERS: + case WP_MEDKIT: + case WP_AMMO: + case WP_MEDIC_SYRINGE: + case WP_LANDMINE: + case WP_SATCHEL: + case WP_SATCHEL_DET: + case WP_TRIPMINE: + case WP_MEDIC_ADRENALINE: + case WP_DYNAMITE: + case WP_ARTY: + return -2.0f; + + // short range + case WP_KNIFE: + return 256.0f; + + case WP_GRENADE_PINEAPPLE: + case WP_GRENADE_LAUNCHER: + return 512.0f; + + case WP_LUGER: + case WP_COLT: + + case WP_AKIMBO_COLT: + case WP_AKIMBO_LUGER: + case WP_SILENCER: + + case WP_SILENCED_COLT: + + case WP_FLAMETHROWER: + case WP_SMOKE_MARKER: + case WP_SMOKE_BOMB: + + case WP_AKIMBO_SILENCEDCOLT: + case WP_AKIMBO_SILENCEDLUGER: + + return 1000.f; + + // low-mid range + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + + case WP_GPG40: + case WP_M7: + + return 2000.f; + + // mid range + case WP_KAR98: + case WP_CARBINE: + case WP_GARAND: + + case WP_MOBILE_MG42: + case WP_K43: + case WP_FG42: + + return 3000.f; + + // long range + case WP_PANZERFAUST: + + return 7000.f; + + case WP_MORTAR: + case WP_MORTAR_SET: + + return 4500.f; + + case WP_BINOCULARS: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + + return 6500.f; + + default: + return 999999.0; + } +} + +qboolean BotHasWeaponWithRange( bot_state_t *bs, float dist ) { + int i, *ammo; + + ammo = bs->cur_ps.ammo; + + // if we are mounted on an MG42, always return true + if ( g_entities[bs->client].s.eFlags & EF_MG42_ACTIVE ) { + return qtrue; + } + + for ( i = 0; i < WP_NUM_WEAPONS; i++ ) { + if ( COM_BitCheck( bs->cur_ps.weapons, i ) ) { + if ( !BotGotEnoughAmmoForWeapon( bs, i ) ) { + continue; + } + if ( BotWeaponRange( bs, i ) < dist ) { + continue; + } + // found one + return qtrue; + } + } + + return qfalse; +} + +/* +================== +BotSortClientsByDistance +================== +*/ +void BotSortClientsByDistance( vec3_t srcpos, int *sorted, qboolean hasPanzer ) { + int i, j, best = 0; + float distances[MAX_CLIENTS]; + int indexes[MAX_CLIENTS]; + float closest; + + memset( distances, 0, sizeof( distances ) ); + memset( indexes, 0, sizeof( indexes ) ); + // + // build the distances + for ( i = 0; i < level.numConnectedClients; i++ ) { + int k = level.sortedClients[i]; + + + distances[i] = VectorDistanceSquared( srcpos, level.clients[k].ps.origin ); + if ( hasPanzer && level.clients[k].ps.eFlags & EF_MG42_ACTIVE ) { + distances[i] /= ( 3 * 3 ); + } + indexes[i] = k; + } + + // + // now build the output list + for ( i = 0; i < level.numConnectedClients; i++ ) { + closest = -1; + for ( j = 0; j < level.numConnectedClients; j++ ) { + if ( indexes[j] < 0 ) { + continue; + } + + if ( closest < 0 || distances[j] < closest ) { + best = j; + closest = distances[j]; + } + } + + sorted[i] = indexes[best]; + indexes[best] = -1; + } +} + +/* +================== +BotFindEnemyMP + +Find a new target for bots in multiplayer +================== +*/ +int BotFindEnemyMP( bot_state_t *bs, int curenemy, qboolean ignoreViewRestrictions ) { + int i, healthdecrease, j; + float fov, dist, curdist, alertness, easyfragger, vis; + aas_entityinfo_t entinfo, curenemyinfo; + vec3_t dir, ang; + int heardShooting, heardFootSteps; + int distanceSorted[MAX_CLIENTS]; + int startTime = 0; + if ( bot_profile.integer == 1 ) { + startTime = trap_Milliseconds(); + } + + if ( bs->last_findenemy == level.time ) { + if ( bs->last_findenemy_enemy >= 0 || curenemy >= 0 ) { + bs->enemy = bs->last_findenemy_enemy; + } + + if ( bot_profile.integer == 1 ) { + botTime_FindEnemy += trap_Milliseconds() - startTime; + } + return ( bs->last_findenemy_enemy > -1 ); + } + bs->last_findenemy = level.time; + + alertness = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_ALERTNESS, 0, 1 ); + easyfragger = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1 ); + //check if the health decreased + healthdecrease = bs->lasthealth > bs->inventory[INVENTORY_HEALTH]; + //remember the current health value + bs->lasthealth = bs->inventory[INVENTORY_HEALTH]; + // + if ( curenemy >= 0 ) { + BotEntityInfo( curenemy, &curenemyinfo ); + if ( EntityIsDead( &curenemyinfo ) ) { + bs->enemy = -1; + curenemy = -1; + curdist = 0; + } else { + VectorSubtract( curenemyinfo.origin, bs->origin, dir ); + curdist = VectorLength( dir ); + } + } else { + curdist = 0; + } + + // See if we're inheriting a target from our leader +// if (BotFindLeadersEnemy( bs, curenemy, &entinfo)) +// return qtrue; + + BotSortClientsByDistance( bs->origin, distanceSorted, COM_BitCheck( bs->cur_ps.weapons, WP_PANZERFAUST ) ); + + // + heardShooting = qfalse; + heardFootSteps = qfalse; + for ( i = 0; i < level.numConnectedClients; i++ ) { +// j = level.sortedClients[i]; + j = distanceSorted[i]; + + if ( j == bs->client ) { + continue; + } + + //if it's the current enemy + if ( j == curenemy ) { + continue; + } + + BotEntityInfo( j, &entinfo ); + if ( !entinfo.valid ) { + continue; + } + + // See if the enemy has a notarget on it + if ( g_entities[j].flags & FL_NOTARGET ) { + continue; + } + + //if on the same team + if ( BotSameTeam( bs, j ) ) { + continue; + } + + //if the enemy is dead, Gordon: and not in limbo, still want to kill em totally + if ( EntityIsDead( &entinfo ) && !EntityInLimbo( &entinfo ) ) { + continue; + } + + //if the enemy is invisible and not shooting + //if(EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) { + // continue; + //} + + // TAT 10/10/2002 + // If it's disguised, don't target them + if ( g_entities[j].client->ps.powerups[PW_OPS_DISGUISED] ) { + continue; + } + + //if not an easy fragger don't shoot at chatting players + if ( easyfragger < 0.5 && EntityIsChatting( &entinfo ) ) { + continue; + } + + // this is a complete check, which takes into account all forms of view restriction + if ( !ignoreViewRestrictions && !BotEntityWithinView( bs, j ) ) { + continue; + } + + // + //if (lastteleport_time > trap_AAS_Time() - 3) { + // VectorSubtract(entinfo.origin, lastteleport_origin, dir); + // if (VectorLength(dir) < 70) continue; + //} + + //calculate the distance towards the enemy + VectorSubtract( entinfo.origin, bs->origin, dir ); + dist = VectorLength( dir ); + vectoangles( dir, ang ); + + // Gordon: if using panzerfaust, attack guys on mg42s more readily + if ( COM_BitCheck( bs->cur_ps.weapons, WP_PANZERFAUST ) && ( g_entities[j].client->ps.eFlags & EF_MG42_ACTIVE ) ) { + dist /= 3; + } + + // If this is enemy is attempting to disarm dynamite/build, they take preference! + if ( g_entities[j].client->ps.weapon == WP_PLIERS && g_entities[j].client->touchingTOI ) { + dist /= 3; + } + + //if this enemy is further away than the current one + if ( curenemy >= 0 && dist > curdist ) { + continue; + } + + //if the bot has no + // RF, disabled, doesnt work well with snipers + //if (dist > 900 + alertness * 4000) continue; + + // weapons have range limit + // if we cant get to them, or we dont want to chase them, ignore them + if ( ( !BotWantsToChase( bs ) || !trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, BotGetArea( j ), bs->tfl ) ) + && !BotHasWeaponWithRange( bs, dist ) ) { + continue; + } + + // if the bot's health decreased or the enemy is shooting + if ( curenemy < 0 && ( healthdecrease || EntityIsShooting( &entinfo ) ) ) { + fov = 360; + } else { + fov = 90 + 270 - ( ( dist > 810 ? 810 : dist ) / 3 ); + } + + // RF, smaller fov with sniperrifle + if ( BG_IsScopedWeapon( bs->weaponnum ) ) { + fov = 10; + } + + VectorSubtract( bs->origin, entinfo.origin, dir ); +/* //if the enemy is quite far away, not shooting and the bot is not damaged + if (curenemy < 0 && dist > 200 && !healthdecrease && !EntityIsShooting(&entinfo)) + { + //check if we can avoid this enemy + vectoangles(dir, angles); + //if the bot isn't in the fov of the enemy + if (!InFieldOfVision(g_entities[j].client->ps.viewangles, 120, angles)) { + //update some stuff for this enemy + BotUpdateBattleInventory(bs, j); + //if the bot doesn't really want to fight + if (BotWantsToRetreat(bs)) + continue; + } + } +*/ + // Gordon: with our aggresive botclipping this is a LOT of places, so still attack them, + // NOTE: perhaps change this to check if not valid area, then see if we can hit with long range weapon, if not, THEN ignore + // if we are not mounted on an MG42, or sniping, then ignore clients that are not in a valid area +/* if (!(bs->cur_ps.eFlags & EF_MG42_ACTIVE) && !(bs->flags & BFL_SNIPING)) + { + // if they are not in a valid area + if (!BotGetArea( j )) + continue; + } */ + + // check if the enemy visibility. This does a trace to make sure you're actually visible + vis = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, fov, j, NULL ); + + // If we're not visible, and we're not heard, skip out + if ( ( vis <= 0 ) && !heardShooting && !heardFootSteps ) { + continue; + } + + // found an enemy + bs->enemy = entinfo.number; + if ( curenemy >= 0 || bs->last_fire > level.time - 2000 || bs->last_enemysight > level.time - 2000 ) { + bs->enemysight_time = trap_AAS_Time() - 2; + } else { + bs->enemysight_time = trap_AAS_Time(); + } + // bs->enemysuicide = qfalse; + bs->enemydeath_time = 0; + +/* if (!bs->last_enemysight || (bs->last_enemysight < level.time - 10000)) + { + Bot_ScriptEvent( bs->client, "enemysight", !(g_entities[bs->enemy].r.svFlags & SVF_BOT) ? g_entities[bs->enemy].client->pers.netname : g_entities[bs->enemy].scriptName ); + }*/ + + if ( bot_profile.integer == 1 ) { + botTime_FindEnemy += trap_Milliseconds() - startTime; + } + + bs->last_findenemy_enemy = bs->enemy; + bs->last_enemysight = level.time; + return qtrue; + } + + if ( bot_profile.integer == 1 ) { + botTime_FindEnemy += trap_Milliseconds() - startTime; + } + + bs->last_findenemy_enemy = -1; + return qfalse; + +} // int BotFindEnemyMP(bot_state_t *bs, int curenemy) ... + +// Reaction time for this bot +float BotGetReactionTime( bot_state_t *bs ) { + // Just use the scripted value + return bs->attribs[BOT_REACTION_TIME]; // * trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); +} + +float swayrand( float x, float y ) { + return sin( level.time / 1000.0 * x * M_PI * 2 ) * cos( level.time / 1000.0 * y * M_PI * 2 ); +} + +/* +================== +BotAimAtEnemy +================== +*/ +void BotAimAtEnemy( bot_state_t *bs ) { + int /*i, */ enemyvisible; + float dist, f, aim_skill, aim_accuracy, speed, reactiontime; + vec3_t dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity; + vec3_t mins = {-4,-4,-4}, maxs = {4, 4, 4}; + weaponinfo_t wi; + aas_entityinfo_t entinfo; + bot_goal_t goal; + bsp_trace_t trace; + vec3_t target; + + //if the bot has no enemy + if ( bs->enemy < 0 ) { + return; + } + + //BotAI_Print(PRT_MESSAGE, "client %d: aiming at client %d\n", bs->entitynum, bs->enemy); + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1 ); + aim_accuracy = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1 ); + + if ( aim_skill > 0.95 ) { + //don't aim too early + reactiontime = 0.5 * trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1 ); + if ( bs->enemysight_time > trap_AAS_Time() - reactiontime ) { + return; + } + if ( bs->teleport_time > trap_AAS_Time() - reactiontime ) { + return; + } + } + + //get the weapon information + trap_BotGetWeaponInfo( bs->ws, bs->weaponnum, &wi ); + // if we have mounted an mg42 + if ( g_entities[bs->client].s.eFlags & EF_MG42_ACTIVE ) { + // Gordon: lowering all of these from 0.8 per atvi request + aim_accuracy = 0.65; + aim_skill = 0.65; + } else { + //get the weapon specific aim accuracy and or aim skill + if ( bs->weaponnum == WP_GRENADE_LAUNCHER || bs->weaponnum == WP_GRENADE_PINEAPPLE ) { + aim_accuracy = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1 ); + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1 ); + } + if ( bs->weaponnum == WP_PANZERFAUST || bs->weaponnum == WP_MORTAR_SET ) { + aim_accuracy = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1 ); + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1 ); + } + if ( bs->weaponnum == WP_FLAMETHROWER ) { + aim_accuracy = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_FLAMETHROWER, 0, 1 ); + } + if ( bs->weaponnum == WP_MOBILE_MG42 && bs->cur_ps.eFlags & EF_PRONE ) { + aim_accuracy = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_FLAMETHROWER, 0, 1 ); + } + } + // + if ( aim_accuracy <= 0 ) { + aim_accuracy = 0.0001; + } + //get the enemy entity information + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is invisible then shoot crappy most of the time + if ( EntityIsInvisible( &entinfo ) ) { + if ( random() > 0.1 ) { + aim_accuracy *= 0.4; + } + } + // + VectorSubtract( entinfo.origin, entinfo.lastvisorigin, enemyvelocity ); + VectorScale( enemyvelocity, 1 / entinfo.update_time, enemyvelocity ); + //enemy origin and velocity is remembered every 0.5 seconds + if ( bs->enemyposition_time < trap_AAS_Time() ) { + // + bs->enemyposition_time = trap_AAS_Time() + 0.5; + VectorCopy( enemyvelocity, bs->enemyvelocity ); + VectorCopy( entinfo.origin, bs->enemyorigin ); + } + // aiming gets better with time + f = trap_AAS_Time() - bs->enemysight_time; + if ( f > 2.0 ) { + f = 2.0; + } + aim_accuracy += 0.2 * f / 2.0; + if ( aim_accuracy > 1.0 ) { + aim_accuracy = 1.0; + } + // better if enemy is not moving + f = VectorLength( bs->enemyvelocity ); + if ( f > 200 ) { + f = 200; + } + aim_accuracy += 0.2 * ( 0.5 - ( f / 200.0 ) ); + //if not extremely skilled + if ( aim_skill < 0.96 ) { + VectorSubtract( entinfo.origin, bs->enemyorigin, dir ); + //if the enemy moved a bit + if ( VectorLengthSquared( dir ) > SQR( 48 ) ) { + //if the enemy changed direction + if ( DotProduct( bs->enemyvelocity, enemyvelocity ) < 0 ) { + //aim accuracy should be worse now + aim_accuracy *= 0.45; + } + } + } + if ( aim_accuracy > 1.0 ) { + aim_accuracy = 1.0; + } + + //check visibility of enemy + enemyvisible = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ); + //if the enemy is visible + if ( enemyvisible ) { + // + VectorCopy( entinfo.origin, bestorigin ); + bestorigin[2] += g_entities[bs->enemy].client->ps.viewheight - 6; + //get the start point shooting from + //NOTE: the x and y projectile start offsets are ignored + VectorCopy( bs->origin, start ); + start[2] += bs->cur_ps.viewheight; + //start[2] += wi.offset[2]; + // + BotAI_Trace( &trace, start, mins, maxs, bestorigin, bs->entitynum, MASK_SHOT ); + //if the enemy is NOT hit + if ( trace.fraction <= 1 && trace.ent != entinfo.number ) { + bestorigin[2] = g_entities[bs->enemy].r.absmax[2] - 2; + } + //if it is not an instant hit weapon the bot might want to predict the enemy + if ( wi.speed ) { + // + VectorSubtract( bestorigin, bs->origin, dir ); + dist = VectorLength( dir ); + VectorSubtract( entinfo.origin, bs->enemyorigin, dir ); + //if the enemy is NOT pretty far away and strafing just small steps left and right + if ( !( dist > 100 && VectorLengthSquared( dir ) < SQR( 32 ) ) ) { + //if skilled anough do exact prediction + if ( aim_skill > 0.8 && + //if the weapon is ready to fire + bs->cur_ps.weaponstate == WEAPON_READY ) { + aas_clientmove_t move; + vec3_t origin; + + VectorSubtract( entinfo.origin, bs->origin, dir ); + //distance towards the enemy + dist = VectorLength( dir ); + //direction the enemy is moving in + VectorSubtract( entinfo.origin, entinfo.lastvisorigin, dir ); + // + VectorScale( dir, 1 / entinfo.update_time, dir ); + // + VectorCopy( entinfo.origin, origin ); + origin[2] += 1; + // + VectorClear( cmdmove ); + //AAS_ClearShownDebugLines(); + trap_AAS_PredictClientMovement( &move, bs->enemy, origin, + PRESENCE_CROUCH, qfalse, + dir, cmdmove, 0, + dist * 10 / wi.speed, 0.1, 0, 0, qfalse ); + VectorCopy( move.endpos, bestorigin ); + //BotAI_Print(PRT_MESSAGE, "%1.1f predicted speed = %f, frames = %f\n", trap_AAS_Time(), VectorLength(dir), dist * 10 / wi.speed); + } + //if not that skilled do linear prediction + else if ( aim_skill > 0.4 ) { + VectorSubtract( entinfo.origin, bs->origin, dir ); + //distance towards the enemy + dist = VectorLength( dir ); + //direction the enemy is moving in + VectorSubtract( entinfo.origin, entinfo.lastvisorigin, dir ); + dir[2] = 0; + // + speed = VectorNormalize( dir ) / entinfo.update_time; + //botimport.Print(PRT_MESSAGE, "speed = %f, wi->speed = %f\n", speed, wi->speed); + //best spot to aim at + VectorMA( entinfo.origin, ( dist / wi.speed ) * speed, dir, bestorigin ); + } + } + + } // if (wi.speed) ... + + //if the projectile does radial damage + if ( ( bs->weaponnum != WP_FLAMETHROWER ) && ( wi.proj.damagetype & DAMAGETYPE_RADIAL ) ) { + //if the enemy isn't standing significantly higher than the bot + if ( entinfo.origin[2] < bs->origin[2] + 16 ) { + //try to aim at the ground in front of the enemy + VectorCopy( entinfo.origin, end ); + end[2] -= 64; + BotAI_Trace( &trace, bestorigin, NULL, NULL, end, entinfo.number, MASK_SHOT ); + // + VectorCopy( bestorigin, groundtarget ); + if ( trace.startsolid ) { + groundtarget[2] = entinfo.origin[2] - 16; + } else { groundtarget[2] = trace.endpos[2];} + //trace a line from projectile start to ground target + BotAI_Trace( &trace, start, NULL, NULL, groundtarget, bs->entitynum, MASK_SHOT ); + //if hitpoint is not vertically too far from the ground target + if ( fabs( trace.endpos[2] - groundtarget[2] ) < 50 ) { + VectorSubtract( trace.endpos, groundtarget, dir ); + //if the hitpoint is near anough the ground target + if ( VectorLengthSquared( dir ) < SQR( 100 ) ) { + VectorSubtract( trace.endpos, start, dir ); + //if the hitpoint is far anough from the bot + if ( VectorLengthSquared( dir ) > SQR( 100 ) ) { + //check if the bot is visible from the ground target + trace.endpos[2] += 1; + BotAI_Trace( &trace, trace.endpos, NULL, NULL, entinfo.origin, entinfo.number, MASK_SHOT ); + if ( trace.fraction >= .99f ) { + //botimport.Print(PRT_MESSAGE, "%1.1f aiming at ground\n", AAS_Time()); + VectorCopy( groundtarget, bestorigin ); + } + } + } + } + } + } + if ( BotScopedWeapon( bs->cur_ps.weapon ) ) { + bestorigin[0] += 20 * swayrand( 0.47f, 0.53f ) * ( 1 - aim_accuracy ); + bestorigin[1] += 20 * swayrand( 0.44f, 0.57f ) * ( 1 - aim_accuracy ); + bestorigin[2] += 10 * swayrand( 0.52f, 0.49f ) * ( 1 - aim_accuracy ); + } + } else { + // + VectorCopy( bs->lastenemyorigin, bestorigin ); + bestorigin[2] += 8; + //if the bot is skilled anough + if ( aim_skill > 0.5 ) { + //do prediction shots around corners +// if (wi.number == WP_BFG || //----(SA) removing old weapon references + if ( wi.number == WP_PANZERFAUST || + wi.number == WP_GRENADE_LAUNCHER || + wi.number == WP_GRENADE_PINEAPPLE ) { + //create the chase goal + goal.entitynum = bs->client; + goal.areanum = bs->areanum; + VectorCopy( bs->eye, goal.origin ); + VectorSet( goal.mins, -8, -8, -8 ); + VectorSet( goal.maxs, 8, 8, 8 ); + // + if ( trap_BotPredictVisiblePosition( bs->lastenemyorigin, bs->lastenemyareanum, &goal, bs->tfl, target ) ) { + VectorCopy( target, bestorigin ); + bestorigin[2] -= 20; + } + aim_accuracy = 1; + } + } + } + // + if ( enemyvisible ) { + BotAI_Trace( &trace, bs->eye, NULL, NULL, bestorigin, bs->entitynum, MASK_SHOT ); + VectorCopy( trace.endpos, bs->aimtarget ); + } else { + VectorCopy( bestorigin, bs->aimtarget ); + } + //get aim direction + VectorSubtract( bestorigin, bs->eye, dir ); + // + //distance towards the enemy + dist = VectorLength( dir ); + if ( dist > 150 ) { + dist = 150; + } + f = 0.6 + dist / 150 * 0.4; + aim_accuracy *= f; + + // + //add some random stuff to the aim direction depending on the aim accuracy + if ( aim_accuracy < 0.8 ) { + VectorNormalize( dir ); + dir[0] += 0.3 * swayrand( 0.58f, 0.37f ) * ( 1 - aim_accuracy ); + dir[1] += 0.3 * swayrand( 0.54f, 0.47f ) * ( 1 - aim_accuracy ); + dir[2] += 0.3 * swayrand( 0.61f, 0.38f ) * ( 1 - aim_accuracy ); + } + + //set the ideal view angles + vectoangles( dir, bs->ideal_viewangles ); + + //take the weapon spread into account for lower skilled bots + bs->ideal_viewangles[PITCH] += 6 * wi.vspread * swayrand( 0.36f, 0.45f ) * ( 1 - aim_accuracy ); + bs->ideal_viewangles[PITCH] = AngleMod( bs->ideal_viewangles[PITCH] ); + bs->ideal_viewangles[YAW] += 6 * wi.hspread * swayrand( 0.654f, 0.54f ) * ( 1 - aim_accuracy ); + bs->ideal_viewangles[YAW] = AngleMod( bs->ideal_viewangles[YAW] ); + + // adjust for sway + if ( BotScopedWeapon( bs->weaponnum ) ) { + float spreadfrac, phase; + vec3_t swayang; + gentity_t *ent = BotGetEntity( bs->client ); + int i; + + spreadfrac = ent->client->currentAimSpreadScale; + + // rotate 'forward' vector by the sway + phase = level.time / 1000.0 * ZOOM_PITCH_FREQUENCY * M_PI * 2; + swayang[PITCH] = ZOOM_PITCH_AMPLITUDE * sin( phase ) * ( spreadfrac + ZOOM_PITCH_MIN_AMPLITUDE ); + + phase = level.time / 1000.0 * ZOOM_YAW_FREQUENCY * M_PI * 2; + swayang[YAW] = ZOOM_YAW_AMPLITUDE * sin( phase ) * ( spreadfrac + ZOOM_YAW_MIN_AMPLITUDE ); + + swayang[ROLL] = 0; + + // adjust sway-correction with aim_skill + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_SNIPERRIFLE, 0, 1 ); + VectorMA( bs->ideal_viewangles, -aim_skill, swayang, bs->ideal_viewangles ); + for ( i = 0; i < 3; i++ ) bs->ideal_viewangles[i] = AngleNormalize360( bs->ideal_viewangles[i] ); + } else if ( !( bs->cur_ps.eFlags & EF_MG42_ACTIVE ) && ( bs->weaponnum == WP_MOBILE_MG42 ) ) { + // aim downward slightly to adjust for kick + bs->ideal_viewangles[PITCH] += 4; + } + + // if smoke grenade, aim a bit higher + if ( bs->cur_ps.weapon == WP_SMOKE_MARKER ) { + if ( bs->ideal_viewangles[PITCH] > -60 ) { + bs->ideal_viewangles[PITCH] = -60; + } + } + + //if the bot is really accurate and has the enemy in view for some time + if ( !( bs->cur_ps.eFlags & EF_MG42_ACTIVE ) && aim_accuracy > 0.95 && bs->enemysight_time < trap_AAS_Time() - 2 ) { + + //set the view angles directly + if ( bs->ideal_viewangles[PITCH] > 180 ) { + bs->ideal_viewangles[PITCH] -= 360; + } + if ( AngleNormalize180( bs->ideal_viewangles[YAW] - bs->viewangles[YAW] ) < 25 ) { + VectorCopy( bs->ideal_viewangles, bs->viewangles ); + trap_EA_View( bs->client, bs->viewangles ); + } + } +} + + + +/* +================== +BotAimAtEnemySP +================== +*/ +void BotAimAtEnemySP( bot_state_t *bs ) { +} + + +/* +================== +BotMoveWhileFiring +================== +*/ +qboolean BotMoveWhileFiring( int weapon ) { + switch ( weapon ) { + case WP_BINOCULARS: + case WP_PANZERFAUST: + case WP_MOBILE_MG42: + case WP_MORTAR: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + return qfalse; + } + // + return qtrue; +} + +/* +================== +BotThrottleWeapon +================== +*/ +qboolean BotThrottleWeapon( int weapon ) { + switch ( weapon ) { + case WP_PANZERFAUST: + case WP_FLAMETHROWER: + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + return qfalse; + } + // + return qtrue; +} + +/* +=================== +BotScopedWeapon +=================== +*/ +qboolean BotScopedWeapon( int weapon ) { + switch ( weapon ) { + case WP_BINOCULARS: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_FG42SCOPE: + return qtrue; + } + // + return qfalse; +} + +/* +================== +BotCheckAttack + + returns qfalse if the enemy is unreachable. qtrue if it is possible to attack the enemy (this doesnt always mean + the trigger is pulled). +================== +*/ +qboolean BotCheckAttack( bot_state_t *bs ) { + float reactiontime, fov, firethrottle, dist, aimskill = 0.0; + bsp_trace_t bsptrace; + //float selfpreservation; + vec3_t forward, right, start, end, dir, angles; + weaponinfo_t wi; + bsp_trace_t trace; + aas_entityinfo_t entinfo; + vec3_t mins = {-12, -12, -12}, maxs = {12, 12, 12}; + int i, fcnt, ecnt; + int mask; + + if ( bs->enemy < 0 ) { + return qfalse; + } + + // Gordon: limit teh fire rate of the bots on these weapons + if ( ( bs->weaponnum == WP_GARAND_SCOPE || bs->weaponnum == WP_K43_SCOPE ) && ( level.time - bs->last_fire ) < 800 ) { + // pause for a bit + return qtrue; + } + + { + float range = BotWeaponRange( bs, bs->weaponnum ); + if ( SQR( range ) < VectorDistanceSquared( bs->origin, BotGetOrigin( bs->enemy ) ) ) { + // cant reach them + return qfalse; + } + } + + // + // if we have recently called this routine, then repeat the last result + if ( ( bs->last_botcheckattack_weapon == bs->weaponnum ) && ( bs->enemy == bs->last_botcheckattack_enemy ) && ( bs->last_botcheckattack > level.time - 250 ) ) { + // if our last call passed, then let it through + if ( bs->last_botcheckattack <= bs->last_fire ) { + goto passed; + } + } + + bs->last_botcheckattack = level.time + rand() % 80; + bs->last_botcheckattack_weapon = bs->weaponnum; + bs->last_botcheckattack_enemy = bs->enemy; + + reactiontime = BotGetReactionTime( bs ); //trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1); + aimskill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1 ); + + if ( bs->enemysight_time > trap_AAS_Time() - reactiontime ) { + // wait until we have had time to react + return qtrue; + } + + if ( bs->teleport_time > trap_AAS_Time() - reactiontime ) { + return qtrue; + } + + if ( bs->weaponchange_time > trap_AAS_Time() - 0.1 ) { + return qtrue; + } + + if ( BotThrottleWeapon( bs->weaponnum ) ) { + //check fire throttle characteristic + if ( bs->firethrottlewait_time > trap_AAS_Time() ) { + return qtrue; + } + firethrottle = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_FIRETHROTTLE, 0, 1 ); + if ( bs->firethrottleshoot_time < trap_AAS_Time() ) { + if ( random() > firethrottle ) { + bs->firethrottlewait_time = trap_AAS_Time() + firethrottle; + bs->firethrottleshoot_time = 0; + } else { + bs->firethrottleshoot_time = trap_AAS_Time() + 1 - firethrottle; + bs->firethrottlewait_time = 0; + } + } + } + // always pass if grenade is live + if ( ( bs->cur_ps.weapon == WP_GRENADE_LAUNCHER || bs->cur_ps.weapon == WP_GRENADE_PINEAPPLE ) && bs->cur_ps.grenadeTimeLeft ) { + goto passed; + } + // + // always pass if smoke grenade + if ( bs->cur_ps.weapon == WP_SMOKE_MARKER ) { + goto passed; + } + // + BotEntityInfo( bs->enemy, &entinfo ); + VectorSubtract( entinfo.origin, bs->eye, dir ); + // + if ( bs->weaponnum == WP_KNIFE ) { + if ( VectorLengthSquared( dir ) > SQR( 256 ) ) { + return qfalse; // too far away + } + } + // + if ( VectorLengthSquared( dir ) < SQR( 100 ) ) { + fov = 120; + } else { + fov = 50; + } + + vectoangles( dir, angles ); + + if ( !InFieldOfVision( bs->viewangles, fov, angles ) ) { + return qtrue; + } + + // set mask based on weapon + mask = MASK_SHOT; + switch ( bs->cur_ps.weapon ) { + case WP_PANZERFAUST: + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + mask = MASK_MISSILESHOT; + } + + BotAI_Trace( &bsptrace, bs->eye, NULL, NULL, bs->aimtarget, bs->client, mask ); + if ( !( bs->cur_ps.weapon == WP_GRENADE_LAUNCHER || bs->cur_ps.weapon == WP_GRENADE_PINEAPPLE ) ) { + if ( bsptrace.fraction < .99f && bsptrace.ent != bs->enemy ) { + return qfalse; + } + } + + //get the weapon info + trap_BotGetWeaponInfo( bs->ws, bs->cur_ps.weapon, &wi ); + //get the start point shooting from + VectorCopy( bs->origin, start ); + start[2] += bs->cur_ps.viewheight; + AngleVectors( bs->viewangles, forward, right, NULL ); + start[0] += forward[0] * wi.offset[0] + right[0] * wi.offset[1]; + start[1] += forward[1] * wi.offset[0] + right[1] * wi.offset[1]; + start[2] += forward[2] * wi.offset[0] + right[2] * wi.offset[1] + wi.offset[2]; + //end point aiming at + VectorMA( start, BotWeaponRange( bs, bs->weaponnum ), forward, end ); + //a little back to make sure not inside a very close enemy + VectorMA( start, -12, forward, start ); + BotAI_Trace( &trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT ); //----(SA) should this maybe check the weapon type and adjust the clipflag? it seems like this is probably fine as-is, but I thought I'd note it. + //if won't hit the enemy + if ( trace.ent != bs->enemy ) { + //if the entity is a client + if ( trace.ent > 0 && trace.ent < MAX_CLIENTS ) { + //if a teammate is hit + if ( BotSameTeam( bs, trace.ent ) ) { + return qfalse; + } + } + } + //if the projectile does a radial damage + if ( wi.proj.damagetype & DAMAGETYPE_RADIAL ) { + // if they are carrying the flag, and are close to the destination, fire away + if ( BotCarryingFlag( bs->enemy ) ) { + gentity_t *trav; + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !trav ) { + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( trav ) { + VectorAdd( trav->r.absmin, trav->r.absmax, end ); + VectorScale( end, 0.5, end ); + if ( VectorDistanceSquared( g_entities[bs->enemy].r.currentOrigin, end ) < SQR( 800 ) ) { + goto passed; + } + } + } + + //check if a teammate gets radial damage + fcnt = 0; + ecnt = 0; + for ( i = 0; i < level.maxclients; i++ ) { + if ( !g_entities[i].inuse ) { + continue; + } + if ( i == bs->client ) { + continue; + } + if ( g_entities[i].client->pers.connected != CON_CONNECTED ) { + continue; + } + if ( g_entities[i].client->sess.sessionTeam != TEAM_AXIS && g_entities[i].client->sess.sessionTeam != TEAM_ALLIES ) { + continue; + } + if ( !CanDamage( BotGetEntity( i ), end ) && !CanDamage( BotGetEntity( i ), bs->origin ) ) { + continue; + } + // if they are within range + if ( ( dist = VectorDistance( g_entities[i].r.currentOrigin, trace.endpos ) ) < wi.proj.radius ) { + //points = (wi.proj.damage * (dist / wi.proj.radius)); + //if (points > 10) { + if ( BotSameTeam( bs, i ) ) { + fcnt++; + } else { + ecnt++; + } + //} + // check distance from source + } else if ( ( dist = 4.0 * VectorDistance( g_entities[i].r.currentOrigin, bs->origin ) ) < wi.proj.radius ) { + //dist /= 4.0; + //points = (wi.proj.damage * (dist / wi.proj.radius)); + //if (points > 10) { + if ( BotSameTeam( bs, i ) ) { + fcnt++; + } else { + ecnt++; + } + //} + } + } + // + if ( fcnt >= ecnt ) { + return qfalse; // too dangerous + } + } + +passed: + + //if fire has to be release to activate weapon + if ( bs->cur_ps.weapon == WP_GRENADE_LAUNCHER || bs->cur_ps.weapon == WP_GRENADE_PINEAPPLE ) { + if ( bs->cur_ps.grenadeTimeLeft && + ( ( bs->inventory[INVENTORY_HEALTH] < 15 ) || + ( bs->cur_ps.grenadeTimeLeft < ( 700 + (int)( 4000.0 * ( 1.0 - aimskill ) ) ) ) ) ) { + // release grenade + } else { + // hold onto it + trap_EA_Attack( bs->client ); + } + } else { + trap_EA_Attack( bs->client ); + } + bs->flags ^= BFL_ATTACKED; + bs->last_fire = level.time; + // + // we have attacked them + g_entities[bs->enemy].botLastAttackedTime = level.time; + g_entities[bs->enemy].botLastAttackedEnt = bs->client; + // if this is k43/garand, then add a delay since recoil usually makes it tough + switch ( bs->weaponnum ) { + case WP_K43: + case WP_GARAND: + case WP_K43_SCOPE: + case WP_GARAND_SCOPE: + bs->teleport_time = trap_AAS_Time() + 0.5 + random() * 0.4; + break; + } + // + return qtrue; +} + +/* +================== +BotMapScripts +================== +*/ +void BotMapScripts( bot_state_t *bs ) { + return; +} + +/* +================== +BotCheckButtons +================== +*/ +/* +void CheckButtons(void) +{ + int modelindex, i, numbuttons = 0; + char *classname, *model; + float lip, health, dist; + bsp_entity_t *ent; + vec3_t mins, maxs, size, origin, angles, movedir, goalorigin; + vec3_t start, end, bboxmins, bboxmaxs; + aas_trace_t trace; + + for (ent = entities; ent; ent = ent->next) + { + classname = AAS_ValueForBSPEpairKey(ent, "classname"); + if (!strcmp(classname, "func_button")) + { + //create a bot goal towards the button + model = AAS_ValueForBSPEpairKey(ent, "model"); + modelindex = AAS_IndexFromModel(model); + //if the model is not loaded + if (!modelindex) modelindex = atoi(model+1); + VectorClear(angles); + AAS_BSPModelMinsMaxsOrigin(modelindex - 1, angles, mins, maxs, NULL); + //get the lip of the button + lip = AAS_FloatForBSPEpairKey(ent, "lip"); + if (!lip) lip = 4; + //get the move direction from the angle + VectorSet(angles, 0, AAS_FloatForBSPEpairKey(ent, "angle"), 0); + AAS_SetMovedir(angles, movedir); + //button size + VectorSubtract(maxs, mins, size); + //button origin + VectorAdd(mins, maxs, origin); + VectorScale(origin, 0.5, origin); + //touch distance of the button + dist = fabs(movedir[0]) * size[0] + fabs(movedir[1]) * size[1] + fabs(movedir[2]) * size[2];// - lip; + dist *= 0.5; + // + health = AAS_FloatForBSPEpairKey(ent, "health"); + //if the button is shootable + if (health) + { + //calculate the goal origin + VectorMA(origin, -dist, movedir, goalorigin); + AAS_DrawPermanentCross(goalorigin, 4, LINECOLOR_BLUE); + } //end if + else + { + //add bounding box size to the dist + AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bboxmins, bboxmaxs); + for (i = 0; i < 3; i++) + { + if (movedir[i] < 0) dist += fabs(movedir[i]) * fabs(bboxmaxs[i]); + else dist += fabs(movedir[i]) * fabs(bboxmins[i]); + } //end for + //calculate the goal origin + VectorMA(origin, -dist, movedir, goalorigin); + // + VectorCopy(goalorigin, start); + start[2] += 24; + VectorSet(end, start[0], start[1], start[2] - 100); + trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); + if (!trace.startsolid) + { + VectorCopy(trace.endpos, goalorigin); + } //end if + // + AAS_DrawPermanentCross(goalorigin, 4, LINECOLOR_YELLOW); + // + VectorSubtract(mins, origin, mins); + VectorSubtract(maxs, origin, maxs); + // + VectorAdd(mins, origin, start); + AAS_DrawPermanentCross(start, 4, LINECOLOR_BLUE); + VectorAdd(maxs, origin, start); + AAS_DrawPermanentCross(start, 4, LINECOLOR_BLUE); + } //end else + if (++numbuttons > 5) return; + } //end if + } //end for +} //end of the function CheckButtons +*/ + +/* +================== +BotEntityToActivate +================== +*/ +//#define OBSTACLEDEBUG + +int BotEntityToActivate( int entitynum ) { +/* int i, ent, cur_entities[10]; + char model[MAX_INFO_STRING], tmpmodel[128]; + char target[128], classname[128]; + float health; + char targetname[10][128]; + aas_entityinfo_t entinfo;*/ + + // RF, disabled this until I can figure out a way of getting this all working with the current code + return 0; + +/* BotEntityInfo(entitynum, &entinfo); + Com_sprintf(model, sizeof( model ), "*%d", entinfo.modelindex); + for (ent = trap_AAS_NextBSPEntity(0); ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "model", tmpmodel, sizeof(tmpmodel))) continue; + if (!strcmp(model, tmpmodel)) break; + } + if (!ent) { + BotAI_Print(PRT_ERROR, "BotEntityToActivate: no entity found with model %s\n", model); + return 0; + } + trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname)); + if (!classname) { + BotAI_Print(PRT_ERROR, "BotEntityToActivate: entity with model %s has no classname\n", model); + return 0; + } + //if it is a door + if (!strcmp(classname, "func_door")) { + if (trap_AAS_FloatForBSPEpairKey(ent, "health", &health)) { + //if health the door must be shot to open + if (health) return ent; + } + } + //if it is a explosive + if (!strcmp(classname, "func_explosive")) { + if (trap_AAS_IntForBSPEpairKey(ent, "spawnflags", &i)) { + //if DYNOMITE then only dynomite can blow it up + if (!(i&64)) return ent; + } + } + //get the targetname so we can find an entity with a matching target + if (!trap_AAS_ValueForBSPEpairKey(ent, "targetname", targetname[0], sizeof(targetname[0]))) { +#ifdef OBSTACLEDEBUG + BotAI_Print(PRT_ERROR, "BotEntityToActivate: entity with model \"%s\" has no targetname\n", model); +#endif //OBSTACLEDEBUG + return 0; + } + cur_entities[0] = trap_AAS_NextBSPEntity(0); + for (i = 0; i >= 0 && i < 10;) { + for (ent = cur_entities[i]; ent; ent = trap_AAS_NextBSPEntity(ent)) { + if (!trap_AAS_ValueForBSPEpairKey(ent, "target", target, sizeof(target))) continue; + if (!strcmp(targetname[i], target)) { + cur_entities[i] = trap_AAS_NextBSPEntity(ent); + break; + } + } + if (!ent) { + BotAI_Print(PRT_ERROR, "BotEntityToActivate: no entity with target \"%s\"\n", targetname[i]); + i--; + continue; + } + if (!trap_AAS_ValueForBSPEpairKey(ent, "classname", classname, sizeof(classname))) { + BotAI_Print(PRT_ERROR, "BotEntityToActivate: entity with target \"%s\" has no classname\n", targetname[i]); + continue; + } + if (!strcmp(classname, "func_button")) { + //BSP button model + return ent; + } + else if (!strcmp(classname, "trigger_multiple")) { + //invisible trigger multiple box + return ent; + } + else { + i--; + } + } + //BotAI_Print(PRT_ERROR, "BotEntityToActivate: unknown activator with classname \"%s\"\n", classname); + return 0;*/ +} + +/* +================== +BotSetMovedir +================== +*/ +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 BotSetMovedir( vec3_t angles, vec3_t movedir ) { + if ( VectorCompare( angles, VEC_UP ) ) { + VectorCopy( MOVEDIR_UP, movedir ); + } else if ( VectorCompare( angles, VEC_DOWN ) ) { + VectorCopy( MOVEDIR_DOWN, movedir ); + } else { + AngleVectors( angles, movedir, NULL, NULL ); + } +} + +void BotModelMinsMaxs( int modelindex, vec3_t mins, vec3_t maxs ) { + gentity_t *ent; + int i; + + ent = BotGetEntity( 0 ); + for ( i = 0; i < level.num_entities; i++, ent++ ) { + if ( !ent->inuse ) { + continue; + } + if ( ent->s.modelindex == modelindex ) { + VectorCopy( ent->r.mins, mins ); + VectorCopy( ent->r.maxs, maxs ); + return; + } + } + VectorClear( mins ); + VectorClear( maxs ); +} + +/* +================== +BotAIBlocked +================== +*/ +void BotAIBlocked( bot_state_t *bs, bot_moveresult_t *moveresult, int activate ) { + int movetype, ent, modelindex; + char classname[128], model[128]; +#ifdef OBSTACLEDEBUG + char buf[128]; +#endif + vec3_t up = {0, 0, 1}; + vec3_t angles, mins, maxs, origin, movedir, hordir, start, end, sideward; + aas_entityinfo_t entinfo; + int oldMoverState; + +#ifdef OBSTACLEDEBUG + char netname[MAX_NETNAME]; +#endif + + if ( bs->blockentTime > level.time ) { + // just strafe to avoid other players + if ( level.time % 3000 < 1500 ) { + trap_EA_Move( bs->client, vec3_origin, 0 ); + trap_EA_MoveLeft( bs->client ); + } else { + trap_EA_Move( bs->client, vec3_origin, 0 ); + trap_EA_MoveRight( bs->client ); + } + return; + } + + if ( !moveresult->blocked ) { + /* + if (bs->blockent >= 0 && (bs->blockentTime >= level.time - 500)) { + moveresult->blocked = qtrue; + moveresult->blockentity = bs->blockent; + // + bs->blockent = -1; + bs->blockentTime = 0; + } else {*/ + return; + //} + } + // + BotEntityInfo( moveresult->blockentity, &entinfo ); + // RF, dont avoid our leader, just set this as our goal position + if ( ( entinfo.number < level.maxclients ) && ( bs->blockentTime < level.time ) ) { + // RF, in multiplayer, only ignore if this is our leader and we are currently following them + if ( ( ( bs->ainode == AINode_MP_DefendTarget ) && ( bs->target_goal.flags & GFL_LEADER ) && ( bs->leader == entinfo.number ) ) ) { + // make this our spot + VectorCopy( bs->origin, bs->target_goal.origin ); + bs->target_goal.areanum = bs->areanum; + return; + } + // + if ( G_IsSinglePlayerGame() && bs->leader == entinfo.number ) { + VectorCopy( bs->origin, bs->target_goal.origin ); + bs->target_goal.areanum = bs->areanum; + return; + } + // + if ( bs->target_goal.flags & GFL_LEADER ) { + if ( ( BotPointWithinMovementAutonomy( bs, &bs->target_goal, bs->origin ) ) + && ( VectorDistanceSquared( bs->origin, g_entities[bs->leader].r.currentOrigin ) < SQR( 512 ) ) ) { + VectorCopy( bs->origin, bs->target_goal.origin ); + bs->target_goal.areanum = bs->areanum; + return; + } + } + + bs->blockentTime = level.time + 400; + bs->blockent = entinfo.number; + return; + } + // +#ifdef OBSTACLEDEBUG + ClientName( bs->client, netname, sizeof( netname ) ); + BotAI_Print( PRT_MESSAGE, "%s: I'm blocked by model %d\n", netname, entinfo.modelindex ); +#endif /* OBSTACLEDEBUG */ + ent = 0; + //if blocked by a bsp model and the bot wants to activate it if possible + if ( entinfo.modelindex > 0 && entinfo.modelindex <= max_bspmodelindex && activate ) { + //find the bsp entity which should be activated in order to remove + //the blocking entity + if ( ( ( Q_stricmp( g_entities[entinfo.number].classname, "func_door" ) == 0 ) || ( Q_stricmp( g_entities[entinfo.number].classname, "func_door_rotating" ) == 0 ) ) ) { + if ( ( g_entities[entinfo.number].moverState == MOVER_POS1 ) || ( g_entities[entinfo.number].moverState == MOVER_POS1ROTATE ) ) { + oldMoverState = g_entities[entinfo.number].moverState; + G_TryDoor( BotGetEntity( entinfo.number ), BotGetEntity( bs->client ), BotGetEntity( bs->client ) ); + if ( g_entities[entinfo.number].moverState != oldMoverState ) { + // wait for it to finish opening + return; + } + } else if ( ( g_entities[entinfo.number].moverState == MOVER_POS2 ) || ( g_entities[entinfo.number].moverState == MOVER_POS2ROTATE ) ) { + // it is fully open, try and avoid it + ent = 0; + } else { + // else just wait for it to finish moving + return; + } + } else { + ent = BotEntityToActivate( entinfo.number ); + } + if ( !ent ) { + strcpy( classname, "" ); +#ifdef OBSTACLEDEBUG + BotAI_Print( PRT_MESSAGE, "%s: can't find activator for blocking entity\n", ClientName( bs->client, netname, sizeof( netname ) ) ); +#endif //OBSTACLEDEBUG + } else { + trap_AAS_ValueForBSPEpairKey( ent, "classname", classname, sizeof( classname ) ); +#ifdef OBSTACLEDEBUG + ClientName( bs->client, netname, sizeof( netname ) ); + BotAI_Print( PRT_MESSAGE, "%s: I should activate %s\n", netname, classname ); +#endif /* OBSTACLEDEBUG */ + } +#ifdef OBSTACLEDEBUG +// ClientName(bs->client, netname, sizeof(netname)); +// BotAI_Print(PRT_MESSAGE, "%s: I've got no brain cells for activating entities\n", netname); +#endif /* OBSTACLEDEBUG */ + //if it is an explosive we should shoot it + if ( !strcmp( classname, "func_explosive" ) ) { + //get the door model + trap_AAS_ValueForBSPEpairKey( ent, "model", model, sizeof( model ) ); + modelindex = atoi( model + 1 ); + //if the model is not loaded + if ( !modelindex ) { + return; + } + VectorClear( angles ); + BotModelMinsMaxs( modelindex, mins, maxs ); + //get a goal to shoot at + VectorAdd( maxs, mins, origin ); + VectorScale( origin, 0.5, origin ); + VectorSubtract( origin, bs->eye, movedir ); + // + vectoangles( movedir, moveresult->ideal_viewangles ); + moveresult->flags |= MOVERESULT_MOVEMENTVIEW; + //select a weapon + BotChooseWeapon( bs ); + trap_EA_SelectWeapon( bs->client, WP_KNIFE ); + //shoot + trap_EA_Attack( bs->client ); + // + return; + } //end if*/ + } + if ( !strcmp( g_entities[entinfo.number].classname, "script_mover" ) ) { + if ( entinfo.number == bs->target_goal.entitynum ) { + // it's our goal, we need to get closer + return; + } + // + bs->blockentTime = level.time + 400; + bs->blockent = entinfo.number; + return; + } + //just some basic dynamic obstacle avoidance code + hordir[0] = moveresult->movedir[0]; + hordir[1] = moveresult->movedir[1]; + hordir[2] = 0; + //if no direction just take a random direction + if ( VectorNormalize( hordir ) < 0.1 ) { + VectorSet( angles, 0, 360 * random(), 0 ); + AngleVectors( angles, hordir, NULL, NULL ); + } + // +// if (moveresult->flags & MOVERESULT_ONTOPOFOBSTACLE) movetype = MOVE_JUMP; +// else + movetype = MOVE_WALK; + //if there's an obstacle at the bot's feet and head then + //the bot might be able to crouch through + VectorCopy( bs->origin, start ); + start[2] += 18; + VectorMA( start, 5, hordir, end ); + VectorSet( mins, -16, -16, -24 ); + VectorSet( maxs, 16, 16, 4 ); + // +// bsptrace = AAS_Trace(start, mins, maxs, end, bs->entitynum, MASK_PLAYERSOLID); +// if (bsptrace.fraction >= 1) movetype = MOVE_CROUCH; + //get the sideward vector + // START xkan, 8/22/2002 + // If the script says we should be crouching, then move crouched. + if ( bs->script.flags & BSFL_CROUCH ) { + movetype = MOVE_CROUCH; + } + // END xkan, 8/22/2002 + CrossProduct( hordir, up, sideward ); + // + if ( bs->flags & BFL_AVOIDRIGHT ) { + VectorNegate( sideward, sideward ); + } + //try to crouch straight forward? + if ( movetype != MOVE_CROUCH || !trap_BotMoveInDirection( bs->ms, hordir, 400, movetype ) ) { + //perform the movement + if ( !trap_BotMoveInDirection( bs->ms, sideward, 400, movetype ) ) { + //flip the avoid direction flag + bs->flags ^= BFL_AVOIDRIGHT; + //flip the direction + VectorNegate( sideward, sideward ); + //move in the other direction + trap_BotMoveInDirection( bs->ms, sideward, 400, movetype ); + } + } + //just reset goals and hope the bot will go into another direction + //still needed?? +} + +/* +================== +BotCheckEvents +================== +*/ +void BotCheckEvents( bot_state_t *bs, entityState_t *state ) { + int event; + char buf[128]; + + // The array "entityeventTime" is hardcoded to 1024 + assert( state->number < 1024 ); + + // + //this sucks, we're accessing the gentity_t directly but there's no other fast way + //to do it right now + if ( bs->entityeventTime[state->number] == g_entities[state->number].eventTime ) { + return; + } + bs->entityeventTime[state->number] = g_entities[state->number].eventTime; + +//@TEST. See if this is ever called when the state->number isn't us! + if ( state->number != bs->client ) { + int foo; + foo = 7; + + } // if (state->number != bs->client)... + //if it's an event only entity + if ( state->eType > ET_EVENTS ) { + event = ( state->eType - ET_EVENTS ) & ~EV_EVENT_BITS; + } else { + event = state->event & ~EV_EVENT_BITS; + } + // + switch ( event ) { + //client obituary event + case EV_OBITUARY: + { + int target, attacker, mod; + + target = state->otherEntityNum; + attacker = state->otherEntityNum2; + mod = state->eventParm; + // + if ( target == bs->client ) { + + + // + + + // + bs->num_deaths++; + } + //else if this client was killed by the bot + else if ( attacker == bs->client ) { + + + + // + bs->num_kills++; + } else if ( attacker == bs->enemy && target == attacker ) { + + } + break; + } + case EV_GLOBAL_SOUND: + break; + case EV_PLAYER_TELEPORT_IN: + { + VectorCopy( state->origin, lastteleport_origin ); + lastteleport_time = trap_AAS_Time(); + break; + } + case EV_GENERAL_SOUND: + { + //if this sound is played on the bot + if ( state->number == bs->client ) { + if ( state->eventParm < 0 || state->eventParm > MAX_SOUNDS ) { + BotAI_Print( PRT_ERROR, "EV_GENERAL_SOUND: eventParm (%d) out of range\n", state->eventParm ); + break; + } + //check out the sound + trap_GetConfigstring( CS_SOUNDS + state->eventParm, buf, sizeof( buf ) ); + //if falling into a death pit + if ( !strcmp( buf, "*falling1.wav" ) ) { +/* //if the bot has a personal teleporter + if (bs->inventory[INVENTORY_TELEPORTER] > 0) { + //use the holdable item + trap_EA_Use(bs->client); + } +*/ } + } + break; + } + } +} + +/* +================== +BotCheckSnapshot +================== +*/ +void BotCheckSnapshot( bot_state_t *bs ) { + int ent; + entityState_t state; + + // + ent = 0; + while ( ( ent = BotAI_GetSnapshotEntity( bs->client, ent, &state ) ) != -1 ) { + //check the entity state for events + BotCheckEvents( bs, &state ); + } + //check the player state for events + BotAI_GetEntityState( bs->client, &state ); + //copy the player state events to the entity state + //state.event = bs->cur_ps.externalEvent; + //state.eventParm = bs->cur_ps.externalEventParm; + // + BotCheckEvents( bs, &state ); +} + +/* +================== +BotCheckAir +================== +*/ +void BotCheckAir( bot_state_t *bs ) { +/* if (bs->inventory[INVENTORY_ENVIRONMENTSUIT] <= 0) { + if (trap_AAS_PointContents(bs->eye) & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) { + return; + } + }*/ +} + +// START Mad Doctor I changes, 8/15/2002 + +/* +================== +BotCheckAlert +================== +*/ +#define AISTATE_RELAXING_STAGE1 4000 // bot still feels like in battle mode +#define AISTATE_RELAXING_STAGE2 60000 // bot is in high alert +#define AISTATE_RELAXING_FOOTSTEP 4000 // time it takes to relax from foot step hearing + +void BotCheckAlert( bot_state_t *bs ) { +} + + + +// END Mad Doctor I changes, 8/15/2002 + +//============================================================================ + +// Start TAT 9/23/2002 + +// Update recon state information for a bot +void BotUpdateReconInfo( bot_state_t *bs ) { + // Index for looping through entities + int i = 0; + + // Detailed info about the entities + aas_entityinfo_t entinfo; + + + // Only relevant for Single Player + if ( !BotSinglePlayer() && !BotCoop() ) { + return; + } + + // See if I've been hit. + if ( bs->lasthealth > bs->inventory[INVENTORY_HEALTH] ) { + bs->reconInfo = BOTRECON_UNDERFIRE; + return; + } + + // want to update if we've been hit above regardless, since that's worse than just spotting an enemy + // but now if it's not all clear, we don't need to do any further updates + if ( bs->reconInfo != BOTRECON_ALLCLEAR ) { + return; + } + + // not hit, no enemies spotted, so see if we can find anyone + // Check all entities to see if someone near me is shooting + for ( i = 0; i < level.maxclients; i++ ) + { + // We don't count + if ( i == bs->client ) { + continue; + } + + // Fill in the detailed bot info + BotEntityInfo( i, &entinfo ); + + // Skip invalid entities + if ( !entinfo.valid ) { + continue; + } + + // if the enemy isn't dead and the enemy isn't the bot self + if ( EntityIsDead( &entinfo ) || entinfo.number == bs->entitynum ) { + continue; + } + + // if the enemy is invisible + if ( EntityIsInvisible( &entinfo ) ) { + continue; + } + + // if it's an ally, skip it + if ( BotSameTeam( bs, entinfo.number ) ) { + continue; + } + + // If I can see the bot, then we're done + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 120, entinfo.number, NULL ) ) { + bs->reconInfo = BOTRECON_ENEMYSPOTTED; + return; + } + } +} + +// End TAT 9/23/2002 + +#define MAX_NODESWITCHES 10 +int numnodeswitches; +char nodeswitch[MAX_NODESWITCHES + 1][144]; + +/* +================== +BotResetNodeSwitches() +================== +*/ +void BotResetNodeSwitches( void ) { + numnodeswitches = 0; +} + +/* +================== +BotDumpNodeSwitches() +================== +*/ +void BotDumpNodeSwitches( bot_state_t *bs ) { + int i; + char netname[MAX_NETNAME]; + + ClientName( bs->client, netname, sizeof( netname ) ); + +#ifndef DONT_PRINT_REPEATED_AI_ERRORS + + BotAI_Print( PRT_MESSAGE, "%s at %1.1f switched more than %d AI nodes\n", netname, trap_AAS_Time(), MAX_NODESWITCHES ); + +#endif // #ifndef DONT_PRINT_REPEATED_AI_ERRORS + + for ( i = 0; i < numnodeswitches; i++ ) { + BotAI_Print( PRT_MESSAGE, nodeswitch[i] ); + } + +#ifndef DONT_PRINT_REPEATED_AI_ERRORS + BotAI_Print( PRT_FATAL, "" ); +#endif // #ifndef DONT_PRINT_REPEATED_AI_ERRORS +} + +//============================================================================ + +/* +================== +BotSetupDeathmatchAI +================== +*/ +void BotDeathmatchAIFirstCalled +( + bot_state_t *bs +) { + char gender[144], name[144]; + char userinfo[MAX_INFO_STRING]; + + bs->setupcount--; + if ( bs->setupcount > 0 ) { + return; + } + //get the gender characteristic + trap_Characteristic_String( bs->character, CHARACTERISTIC_GENDER, gender, sizeof( gender ) ); + //set the bot gender + trap_GetUserinfo( bs->client, userinfo, sizeof( userinfo ) ); +// Info_SetValueForKey(userinfo, "sex", gender); + trap_SetUserinfo( bs->client, userinfo ); + /* + //set the team + if ( g_gametype.integer != GT_TOURNAMENT ) { + Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); + trap_EA_Command(bs->client, buf); + } + */ + //set the chat gender + if ( gender[0] == 'm' ) { + trap_BotSetChatGender( bs->cs, CHAT_GENDERMALE ); + } else if ( gender[0] == 'f' ) { + trap_BotSetChatGender( bs->cs, CHAT_GENDERFEMALE ); + } else { trap_BotSetChatGender( bs->cs, CHAT_GENDERLESS );} + //set the chat name + ClientName( bs->client, name, sizeof( name ) ); + trap_BotSetChatName( bs->cs, name ); + // + bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; + + // + bs->setupcount = 0; + +} + + +void BotPowThink +( + bot_state_t *bs +) { + if ( bs->clientCheckTime < trap_AAS_Time() ) { + int i; + gentity_t* ent; + vec3_t vec; + qboolean axisNearby = qfalse; + qboolean alliesNearby = qfalse; + + for ( i = 0; i < level.numConnectedClients; i++ ) { + if ( bs->client == level.sortedClients[i] ) { + continue; + } + + ent = BotGetEntity( level.sortedClients[i] ); + if ( !ent ) { + continue; + } + + if ( ent->client->sess.sessionTeam != TEAM_AXIS && ent->client->sess.sessionTeam != TEAM_ALLIES ) { + continue; + } + + if ( ent->health <= 0 ) { + continue; + } + + if ( fabs( ent->client->ps.origin[2] - bs->origin[2] ) > 64 ) { + // basically, not thru ceilings + continue; + } + + VectorSubtract( ent->client->ps.origin, bs->origin, vec ); + vec[2] = 0; + // ignore height diffs + + if ( DotProduct( vec, vec ) > ( 128 * 128 ) ) { + continue; + } + + if ( ent->client->sess.sessionTeam == TEAM_AXIS ) { + axisNearby = qtrue; + if ( alliesNearby ) { + break; + } + } else if ( ent->client->sess.sessionTeam == TEAM_ALLIES ) { + alliesNearby = qtrue; + if ( axisNearby ) { + break; + } + } + + } + + if ( axisNearby && !alliesNearby ) { + Bot_ScriptEvent( bs->client, "trigger", "axisnearby" ); + } else if ( !axisNearby && alliesNearby ) { + Bot_ScriptEvent( bs->client, "trigger", "alliesnearby" ); + } + + bs->clientCheckTime = trap_AAS_Time() + 1.f; + } +} + + + +/* +================== +BotDeathmatchAI +================== +*/ +void BotDeathmatchAI( bot_state_t *bs, float thinktime ) { + int i = 0; + + //if the bot has just been setup + if ( bs->setupcount > 0 ) { + BotDeathmatchAIFirstCalled( bs ); + } + + //while in warmup, keep checking for best class + if ( ( level.warmupTime > level.time ) && ( bs->lastClassCheck < level.time - 1000 ) ) { // check for a better class + bs->mpClass = BotSuggestClass( bs, bs->mpTeam ); + level.clients[bs->client].sess.latchPlayerType = bs->mpClass; + bs->lastClassCheck = level.time; + } + // if we want to dismount + if ( bs->flags & BFL_DISMOUNT_MG42 ) { + // clear scripted mg42 command + bs->script.flags &= ~BSFL_MOUNT_MG42; + bs->script.mg42entnum = -1; + // + if ( !( g_entities[bs->client].s.eFlags & EF_MG42_ACTIVE ) ) { + bs->flags &= ~BFL_DISMOUNT_MG42; + } else { + if ( rand() % 2 ) { + trap_EA_Activate( bs->client ); + } + } + } + + bs->leader_tagent = -1; + + // share last attacked + BotShareLastAttacked( bs ); + + //no ideal view set + bs->flags &= ~BFL_IDEALVIEWSET; + bs->flags &= ~BFL_BATTLE_MODE; // set it if we are + bs->flags &= ~BFL_SNIPING; + + //set the teleport time + BotSetTeleportTime( bs ); + + //update some inventory values + BotUpdateInventory( bs ); + + //check out the snapshot + BotCheckSnapshot( bs ); + + //check for air + BotCheckAir( bs ); + + //check for required engineer's + if ( BotIsDead( bs ) || bs->sess.playerType != PC_ENGINEER ) { + if ( BotCheckNeedEngineer( bs, bs->sess.sessionTeam ) ) { + // should change automatically + bs->mpClass = PC_ENGINEER; + level.clients[bs->client].sess.latchPlayerType = bs->mpClass; + if ( !BotIsDead( bs ) ) { + Cmd_Kill_f( &g_entities[bs->client] ); + } + } + } + + // Check to see if I need to change my level of alert +// BotCheckAlert(bs); + + // xkan, 1/10/2003 - set aiState of playerState (used by animation system) + g_entities[bs->client].client->ps.aiState = bs->alertState; + + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + if ( BotIsPOW( bs ) ) { + BotPowThink( bs ); + } + + // if the bot has no ai node + if ( !bs->ainode ) { + BotDefaultNode( bs ); + if ( !bs->ainode ) { + return; // nothing to do.. + } + } + + // reset the node switches from the previous frame + BotResetNodeSwitches(); + + // If we've been scripted to SLEEP, we want to not execute ainodes! + // But, we'd still want to do our scripted, etc... +/* if (!bs->scriptedSleep) { + //execute AI nodes + for (i = 0; i < MAX_NODESWITCHES && bs->ainode; i++) { + if(bot_profile.integer == 4 || bot_profile.integer == 5) { + qboolean ret; + int t = trap_Milliseconds(); + const char* txt = bs->ainodeText; + + ret = bs->ainode(bs); + + t = trap_Milliseconds() - t; + + if(bot_profile.integer == 5) { + if(t > 5) { + G_Printf( "AINode %s took %i ms\n", txt, t ); + } + } else { + G_Printf( "AINode %s took %i ms\n", txt, t ); + } + + if( ret ) { + break; + } + } else { + if( bs->ainode(bs)) { + break; + } + } + } + }*/ + + //if the bot removed itself :) + if ( !bs->inuse ) { + return; + } + + // if the node changed, log entry + if ( i > 0 ) { +// Bot_ScriptLog_Entry( bs, qfalse, ">> NODE CHANGE <<", va("%s\r\n", bs->ainodeText) ); + } + + // bot scripting + Bot_ScriptRun( bs, qfalse ); + + //if the bot executed too many AI nodes + if ( i >= MAX_NODESWITCHES ) { +#ifdef _DEBUG + char name[144]; + trap_BotDumpGoalStack( bs->gs ); + trap_BotDumpAvoidGoals( bs->gs ); + BotDumpNodeSwitches( bs ); + ClientName( bs->client, name, sizeof( name ) ); + +//#ifndef DONT_PRINT_REPEATED_AI_ERRORS + BotAI_Print( PRT_ERROR, "%s at %1.1f switched more than %d AI nodes\n", name, trap_AAS_Time(), MAX_NODESWITCHES ); +//#endif // #ifndef DONT_PRINT_REPEATED_AI_ERRORS +#endif + } + + // + bs->lastframe_health = bs->inventory[INVENTORY_HEALTH]; + + + bs->lasthealth = g_entities[bs->client].health; +} + +/* +================== +BotSetupDeathmatchAI +================== +*/ +void BotSetupDeathmatchAI( void ) { + int ent, modelnum; + char model[128]; + + gametype = trap_Cvar_VariableIntegerValue( "g_gametype" ); + + trap_Cvar_Register( &bot_rocketjump, "bot_rocketjump", "0", 0 ); + trap_Cvar_Register( &bot_grapple, "bot_grapple", "0", 0 ); + trap_Cvar_Register( &bot_fastchat, "bot_fastchat", "0", 0 ); + trap_Cvar_Register( &bot_nochat, "bot_nochat", "1", CVAR_ROM ); + trap_Cvar_Register( &bot_testrchat, "bot_testrchat", "0", 0 ); + // + max_bspmodelindex = 0; + for ( ent = trap_AAS_NextBSPEntity( 0 ); ent; ent = trap_AAS_NextBSPEntity( ent ) ) { + if ( !trap_AAS_ValueForBSPEpairKey( ent, "model", model, sizeof( model ) ) ) { + continue; + } + if ( model[0] == '*' ) { + modelnum = atoi( model + 1 ); + if ( modelnum > max_bspmodelindex ) { + max_bspmodelindex = modelnum; + } + } + } + //initialize the waypoint heap + BotInitWaypoints(); +} + +/* +================== +BotShutdownDeathmatchAI +================== +*/ +void BotShutdownDeathmatchAI( void ) { +} + +/* +=============== +BotSetBlockEnt +=============== +*/ +void BotSetBlockEnt( int client, int blocker ) { + botstates[client].blockent = blocker; + botstates[client].blockentTime = level.time; +} + + +/* +================== +BotMoveToIntermission +================== +*/ +void BotMoveToIntermission( int client ) { + char cs[MAX_STRING_CHARS]; // DHM - Nerve + char *buf; // DHM - Nerve + int winner; // DHM - Nerve + bot_state_t *bs; + + if ( !g_entities[client].r.svFlags & SVF_BOT ) { + return; + } + + bs = &botstates[client]; + + winner = -1; + if ( g_gametype.integer >= GT_WOLF ) { + if ( rand() % 2 == 0 ) { + trap_GetConfigstring( CS_MULTI_MAPWINNER, cs, sizeof( cs ) ); + buf = Info_ValueForKey( cs, "winner" ); + winner = atoi( buf ); + // if we won, talk it up + if ( winner == ( bs->sess.sessionTeam - 1 ) ) { + //if (rand()%3) { + BotSendVoiceChat( bs, "Cheer", SAY_ALL, 1000 + rand() % 5000, BOT_SHOWTEXT, qfalse ); + //} else { + // BotSendVoiceChat( bs, "GoodGame", SAY_ALL, 1000 + rand()%3000, BOT_SHOWTEXT, qfalse ); + //} + } else if ( bs->sess.sessionTeam ) { + //if (rand()%2) { + BotSendVoiceChat( bs, "Negative", SAY_ALL, 1000 + rand() % 5000, BOT_SHOWTEXT, qfalse ); + //} else { + // BotSendVoiceChat( bs, "GoodGame", SAY_ALL, 1000 + rand()%3000, BOT_SHOWTEXT, qfalse ); + //} + } + } + } + +} + +/* +================ +BotRecordTeamChange +================ +*/ +void BotRecordTeamChange( int client ) { + int team; + int i; + bot_state_t *bs; + + team = g_entities[client].client->sess.sessionTeam; + + // greet the new player + for ( i = 0; i < level.maxclients; i++ ) { + bs = &botstates[i]; + if ( !bs->inuse ) { + continue; + } + if ( bs->sess.sessionTeam != team ) { + continue; + } + + // Don't use excess voices in Single Player + if ( !BotSinglePlayer() && !BotCoop() ) { + BotVoiceChatAfterIdleTime( bs->client, "Hi", SAY_TEAM, 1000 + rand() % 6000, BOT_SHOWTEXT, 7000, qfalse ); + } + } +} + +/* +================== +BotRecordKill +================== +*/ +void BotRecordKill( int client, int enemy ) { + bot_state_t *bs = &botstates[client]; + + if ( client == enemy ) { + return; + } + + // if this is a team kill, ignore + if ( BotSameTeam( bs, enemy ) ) { + // friendly fire! + // Don't use excess voices in Single Player + if ( !BotSinglePlayer() && !BotCoop() ) { + BotVoiceChatAfterIdleTime( bs->client, "Sorry", SAY_TEAM, 1000 + rand() % 4000, qfalse, 3000 + rand() % 2000, qfalse ); + } + return; + } + + // if last kill was too long ago + if ( bs->last_kill_time < level.time - 10000 ) { + bs->shorterm_kill_count = 0; + bs->last_kill_time = level.time; + return; + } + + // another kill + bs->shorterm_kill_count++; + bs->last_kill_time = level.time; + + // Don't use excess voices in Single Player + if ( !G_IsSinglePlayerGame() ) { + // if we have killed enough in the short term, talk it up + if ( bs->shorterm_kill_count > 2 ) { + BotSendVoiceChat( bs, "Yeah", SAY_ALL, 1000 + rand() % 1000, qfalse, qfalse ); + } else if ( bs->shorterm_kill_count > 1 ) { + BotVoiceChatAfterIdleTime( bs->client, "EnemyWeak", SAY_TEAM, 1000 + rand() % 1000, qfalse, 3000, qfalse ); + } + } +} + +/* +=================== +BotRecordPain +=================== +*/ +void BotRecordPain( int client, int enemy, int mod ) { + bot_state_t *bs = &botstates[client]; + gentity_t *targ; + + if ( client == enemy ) { + return; + } + + // Mad Doc xkan, 10/30/2002 - if we are already dead, just return + if ( g_entities[bs->client].health <= 0 ) { + return; + } + + // if this is a team kill, ignore + if ( enemy < level.maxclients && BotSameTeam( bs, enemy ) ) { + // friendly fire! + // Don't use excess voices in Single Player + BotVoiceChatAfterIdleTime( bs->client, "HoldYourFire", SAY_TEAM, 1000 + rand() % 1000, qfalse, 3000 + rand() % 2000, qfalse ); + return; + } + + if ( enemy >= level.maxclients ) { + return; + } + + bs->last_pain = level.time; + bs->last_pain_client = enemy; + g_entities[bs->client].botLastAttackedTime = level.time; + + // if we are defending/near an objective + if ( bs->defendgoal.entitynum >= level.maxclients ) { + targ = BotGetEntity( bs->defendgoal.entitynum ); + if ( !targ->inuse ) { + return; + } + if ( Q_stricmp( targ->classname, "team_CTF_redflag" ) && + Q_stricmp( targ->classname, "team_CTF_blueflag" ) && + Q_stricmp( targ->classname, "trigger_flagonly" ) && + Q_stricmp( targ->classname, "team_WOLF_checkpoint" ) ) { + return; + } + if ( VectorDistanceSquared( bs->origin, bs->defendgoal.origin ) > SQR( 1024 ) ) { + return; + } + //if (!trap_InPVS( bs->origin, bs->defendgoal.origin )) return; + // + // we are near the goal + // Don't use excess voices in Single Player + if ( !BotSinglePlayer() && !BotCoop() ) { + BotVoiceChatAfterIdleTime( bs->client, "TakingFire", SAY_TEAM, 1000 + rand() % 1000, qfalse, 5000 + rand() % 4000, qfalse ); + } + } + +} + +/* +=================== +BotRecordDeath +=================== +*/ +void BotRecordDeath( int client, int enemy ) { + bot_state_t *bs = &botstates[client]; + gentity_t *targ; + + if ( client == enemy ) { + return; + } + + // if this is a team kill, ignore + if ( enemy < level.maxclients && BotSameTeam( bs, enemy ) ) { + return; + } + + // if we are defending/near an objective + if ( bs->defendgoal.entitynum > level.maxclients ) { + targ = BotGetEntity( bs->defendgoal.entitynum ); + if ( !targ->inuse ) { + return; + } + if ( Q_stricmp( targ->classname, "team_CTF_redflag" ) && + Q_stricmp( targ->classname, "team_CTF_blueflag" ) && + Q_stricmp( targ->classname, "trigger_flagonly" ) && + Q_stricmp( targ->classname, "team_WOLF_checkpoint" ) ) { + return; + } + if ( VectorDistanceSquared( bs->origin, bs->defendgoal.origin ) > SQR( 1024 ) ) { + return; + } + // + // we are near the goal + BotVoiceChatAfterIdleTime( bs->client, "Incoming", SAY_TEAM, 1000 + rand() % 1000, qfalse, 6000, qfalse ); + } + +} + +/* +=================== +BotGetAimAccuracySkill + Get the bot's aim accuracy and skill accounting for distance from leader, + weapon, pose (standing vs. crouching vs. prone) +=================== +*/ +void BotGetAimAccuracySkill( bot_state_t *bs, float *outAimAccuracy, float *outAimSkill ) { + float aim_accuracy, aim_skill; + //weaponinfo_t wi; + gclient_t *client = g_entities[bs->client].client; + gclient_t *clientEnemy; + // + // What sort of penalty do we get for being away from a leader + float noLeaderPenalty = BotNoLeaderPenalty( bs ); + + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1 ); + //aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1); + // + //get the weapon information + //trap_BotGetWeaponInfo(bs->ws, bs->weaponnum, &wi); + //get the weapon specific aim accuracy and or aim skill + if ( bs->weaponnum == WP_GRENADE_LAUNCHER || bs->weaponnum == WP_GRENADE_PINEAPPLE ) { + //aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1); + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1 ); + } + if ( bs->weaponnum == WP_PANZERFAUST ) { + //aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1); + aim_skill = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1 ); + } + if ( bs->weaponnum == WP_FLAMETHROWER ) { + //aim_accuracy = trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_FLAMETHROWER, 0, 1); + } + + aim_accuracy = bs->attribs[BOT_AIM_ACCURACY]; + + if ( aim_skill > 1 ) { + aim_skill = 1; + } + if ( aim_accuracy > 1 ) { + aim_accuracy = 1; + } + + // The current accuracy is original minus original times penalty + aim_accuracy = aim_accuracy + * ( 1.0f - ( NO_LEADER_MAX_AIM_PENALTY * noLeaderPenalty ) ); + + if ( client->ps.eFlags & EF_PRONE ) { + // if prone, increase aim accuracy/skill + aim_accuracy += ( 1 - aim_accuracy ) * AIM_ACCURACY_BONUS_PRONE; + aim_skill += ( 1 - aim_skill ) * AIM_SKILL_BONUS_PRONE; + } else if ( client->ps.eFlags & EF_CROUCHING ) { + // if crouching, increase aim accuracy + aim_accuracy += ( 1 - aim_accuracy ) * AIM_ACCURACY_BONUS_CROUCH; + } + + if ( bs->enemy >= 0 && ( clientEnemy = g_entities[bs->enemy].client ) != NULL ) { + // if our enemy is prone/crouching, we are less accurate + if ( clientEnemy->ps.eFlags & EF_PRONE ) { + aim_accuracy *= ( 1 - AIM_ACCURACY_ENEMY_PENALTY_PRONE ); + } else if ( clientEnemy->ps.eFlags & EF_CROUCHING ) { + aim_accuracy *= ( 1 - AIM_ACCURACY_ENEMY_PENALTY_CROUCH ); + } + } + + // return the result through output params + if ( outAimAccuracy ) { + *outAimAccuracy = aim_accuracy; + } + if ( outAimSkill ) { + *outAimSkill = aim_skill; + } +} + + +/* +================ +BotBestSniperSpot + + returns -1 if no spot found +================ +*/ +int BotBestSniperSpot( bot_state_t *bs ) { + gentity_t *trav, *bestSpot; + int areanum, t, bestTime; + + trav = NULL; + bestSpot = NULL; + bestTime = 99999; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_SNIPERSPOT ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + + // if this is not for our team + if ( trav->aiTeam && trav->aiTeam != bs->sess.sessionTeam ) { + continue; + } + + // get the travel time to the goal + areanum = BotPointAreaNum( trav->s.number, trav->s.origin ); + if ( !areanum ) { + continue; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ); + if ( !t ) { + continue; + } + + if ( bs->target_goal.entitynum != trav - g_entities ) { + // if we have recently used this spot, avoid it + if ( trav->missionLevel && ( trav->missionLevel - level.time < 60000 ) ) { + continue; + } + } + + if ( t < bestTime ) { + bestTime = t; + bestSpot = trav; + } + } + + if ( bestSpot ) { + // avoid this spot for a while + bestSpot->missionLevel = level.time; + return bestSpot->s.number; + } + + return -1; +} + +/* +================ +BotBestLandmineSpotingSpot + + returns -1 if no spot found +================ +*/ +int BotBestLandmineSpotingSpot( bot_state_t *bs ) { + gentity_t *trav, *bestSpot; + int areanum, t, bestTime; + + trav = NULL; + bestSpot = NULL; + bestTime = 99999; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINESPOTINGSPOT ) ) ) { + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + + // if this is not for our team + if ( trav->aiTeam && trav->aiTeam != bs->sess.sessionTeam ) { + continue; + } + + // get the travel time to the goal + areanum = BotPointAreaNum( trav->s.number, trav->s.origin ); + if ( !areanum ) { + continue; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ); + if ( !t ) { + continue; + } + + if ( bs->target_goal.entitynum != trav - g_entities ) { + // if we have recently used this spot, avoid it + if ( trav->missionLevel && ( trav->missionLevel - level.time < 20000 ) ) { + continue; + } + } + + if ( t < bestTime ) { + bestTime = t; + bestSpot = trav; + } + } + + if ( bestSpot ) { + // avoid this spot for a while + bestSpot->missionLevel = level.time; + return bestSpot->s.number; + } + + return -1; +} + +/* +================ +BotBestMG42Spot + + returns -1 if no spot found +================ +*/ +int BotBestMG42Spot( bot_state_t *bs, qboolean force ) { + gentity_t *trav, *bestSpot, *mg42; + int areanum, t, bestTime; + // + trav = NULL; + mg42 = NULL; + bestSpot = NULL; + bestTime = 99999; + while ( ( mg42 = BotFindNextStaticEntity( mg42, BOTSTATICENTITY_MG42 ) ) ) { + //while (trav = BotFindEntity( trav, FOFS(classname), "bot_mg42_spot" )) { + if ( !mg42->melee ) { + continue; // it doesnt have a "mg42 spot" + } + trav = mg42->melee; + // is it disabled? + if ( mg42->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + // if this is not for our team + //if (trav->aiTeam && trav->aiTeam != bs->sess.sessionTeam) continue; + if ( !trav->melee->takedamage ) { + continue; + } + if ( trav->melee->entstate != STATE_DEFAULT ) { + continue; + } + if ( trav->melee->active ) { + // if the person using it is from the other team, then we should still head for it, to try and take it from them + if ( mg42->r.ownerNum < level.maxclients && ( g_entities[mg42->r.ownerNum].client->sess.sessionTeam != bs->sess.sessionTeam ) ) { + // errr.. + } else { + continue; + } + } + // if we have recently used this spot, ignore it + // NOTE: if it's active, and we got to here, then it must be an enemy, so "ignore" the ignoreTime + if ( !force && !mg42->active && trav->botIgnoreTime && ( trav->botIgnoreTime > level.time - 1000 ) ) { + continue; + } + // get the travel time to the goal + areanum = BotPointAreaNum( trav->s.number, trav->s.origin ); + if ( !areanum ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, areanum, bs->tfl ); + if ( !t ) { + continue; + } + if ( t < bestTime ) { + bestTime = t; + bestSpot = trav; + } + } + // + if ( bestSpot ) { + // avoid this spot for a while + bestSpot->botIgnoreTime = level.time; + return bestSpot->s.number; + } + // + return -1; +} + +/* +================ +BotGetNumVisibleSniperSpots +================ +*/ +int BotGetNumVisibleSniperSpots( bot_state_t *bs ) { + gentity_t *trav; + int cnt; + vec3_t dest; + trace_t tr; + // + // count the spots first + trav = NULL; + cnt = 0; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_SNIPERSPOT ) ) ) { + // if this is not for the other team + if ( trav->aiTeam && trav->aiTeam == bs->sess.sessionTeam ) { + continue; + } + // is this spot visible? + VectorCopy( trav->s.origin, dest ); + trap_Trace( &tr, bs->eye, NULL, NULL, dest, bs->client, MASK_SHOT ); + if ( tr.fraction < 0.9 ) { + continue; + } + cnt++; + } + // + // return a random spot + return cnt; +} + +/* +================ +BotGetRandomVisibleSniperSpot +================ +*/ +int BotGetRandomVisibleSniperSpot( bot_state_t *bs ) { + #define MAX_SNIPER_SPOTS 40 + gentity_t *trav; + int cnt, spots[MAX_SNIPER_SPOTS]; + vec3_t dest; + trace_t tr; + // + // count the spots first + trav = NULL; + cnt = 0; + while ( ( trav = BotFindEntity( trav, FOFS( classname ), "bot_sniper_spot" ) ) ) { + // if this is not for the other team + if ( trav->aiTeam && trav->aiTeam == bs->sess.sessionTeam ) { + continue; + } + // is this spot visible? + VectorCopy( trav->s.origin, dest ); + trap_Trace( &tr, bs->eye, NULL, NULL, dest, bs->client, MASK_SHOT ); + if ( tr.fraction < 0.9 ) { + continue; + } + spots[cnt++] = trav->s.number; + } + // + if ( !cnt ) { + return -1; + } + // return a random spot + return spots[rand() % cnt]; +} + +/* +================== +BotClearGoal +================== +*/ +void BotClearGoal( bot_goal_t *goal ) { + memset( goal, 0, sizeof( *goal ) ); + goal->entitynum = -1; +} + +/* +================== +BotDropFlag +================== +*/ +void BotDropFlag( bot_state_t *bs ) { + gentity_t *ent; + gitem_t *item = NULL; + gentity_t *flag = NULL; + vec3_t launchvel; + + ent = BotGetEntity( bs->client ); + if ( ent->client->ps.powerups[PW_REDFLAG] ) { + item = BG_FindItem( "Red Flag" ); + if ( !item ) { + item = BG_FindItem( "Objective" ); + } + + ent->client->ps.powerups[PW_REDFLAG] = 0; + } + if ( ent->client->ps.powerups[PW_BLUEFLAG] ) { + item = BG_FindItem( "Blue Flag" ); + if ( !item ) { + item = BG_FindItem( "Objective" ); + } + + ent->client->ps.powerups[PW_BLUEFLAG] = 0; + } + + if ( item ) { + launchvel[0] = crandom() * 20; + launchvel[1] = crandom() * 20; + launchvel[2] = 10 + random() * 10; + + flag = LaunchItem( item,ent->r.currentOrigin,launchvel,ent->s.number ); + flag->s.modelindex2 = ent->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here + flag->message = ent->message; // DHM - Nerve :: also restore item name + flag->botIgnoreTime = level.time + 2500; + flag->r.ownerNum = bs->client; + // Clear out player's temp copies + ent->s.otherEntityNum2 = 0; + ent->message = NULL; + } +} + +/* +=================== +BotCalculateMg42Spots +=================== +*/ +extern vec3_t playerMins, playerMaxs; +void BotCalculateMg42Spots( void ) { + gentity_t *trav, *sptrav, *newent; + vec3_t pos, epos, dir, v; + trace_t tr; + vec3_t mins, maxs; + int blueCount, redCount; + int i; + float dist; + + // Start TAT 9/24/2002 + // This isn't working for constructible MG42s, because our traces are hitting the constructible markers + // so we need to temporarily unlink all the linked constructible markers, and then we'll link + // them again after we're done, so they don't show up in the traces + int constructMarkers[MAX_GENTITIES]; + int numMarkers = 0; + + trav = NULL; + // loop through all the constructible markers + while ( ( trav = G_Find( trav, FOFS( classname ), "misc_constructiblemarker" ) ) ) + { + // if it's linked + if ( trav->r.linked ) { + // add it to our list + constructMarkers[numMarkers++] = trav->s.number; + + // and unlink it + trap_UnlinkEntity( trav ); + } + } + + // End TAT 9/24/2002 + + + VectorCopy( playerMins, mins ); + VectorCopy( playerMaxs, maxs ); + + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_MG42 ) ) ) { + // + // RF, if this entity already has an mg42 spot, then skip it + if ( trav->melee ) { + continue; + } + // + blueCount = 0; + redCount = 0; + // go backwards from the mg42 mount position + AngleVectors( trav->s.angles, dir, NULL, NULL ); + + if ( trav->r.maxs[0] > maxs[0] ) { + dist = trav->r.maxs[0]; + } else { + dist = maxs[0]; + } + + while ( ( dist += 2 ) < 80 ) { + maxs[2] = 4; + mins[2] = 0; + // + VectorMA( trav->r.currentOrigin, -dist, dir, pos ); + trap_Trace( &tr, pos, mins, maxs, pos, ENTITYNUM_NONE, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + continue; + } + VectorCopy( tr.endpos, pos ); + VectorCopy( tr.endpos, epos ); + epos[2] -= 48; + trap_Trace( &tr, pos, mins, maxs, epos, ENTITYNUM_NONE, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + continue; + } + VectorCopy( tr.endpos, pos ); + // move it up to allow for bounding mins + pos[2] += -playerMins[2]; + mins[2] = playerMins[2]; + // one last check + trap_Trace( &tr, pos, mins, maxs, pos, ENTITYNUM_NONE, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + continue; + } + break; + } + if ( tr.startsolid || tr.allsolid ) { + // didnt find a good spot + continue; + } + // go back to normal head height + //maxs[2] = playerMaxs[2]; + // trace down to the ground + VectorCopy( pos, epos ); + epos[2] -= 128; + //trap_Trace( &tr, pos, mins, maxs, epos, trav->s.number, MASK_PLAYERSOLID ); + trap_Trace( &tr, pos, mins, maxs, epos, ENTITYNUM_NONE, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + continue; + } + VectorCopy( tr.endpos, pos ); + // go back to normal head height, check position + maxs[2] = playerMaxs[2]; + trap_Trace( &tr, pos, mins, maxs, pos, ENTITYNUM_NONE, MASK_PLAYERSOLID ); + if ( tr.startsolid || tr.allsolid ) { + continue; + } + // if this position is close enough to the mg42, then assume it's valid + if ( VectorDistanceSquared( trav->r.currentOrigin, pos ) > SQR( 48 ) ) { + continue; + } + // + // spawn an mg42 spot + newent = G_Spawn(); + newent->classname = "bot_mg42_spot"; + + // try and place it into the bot game entities +// newent = BotCheckBotGameEntity( newent ); + + newent->melee = trav; + trav->melee = newent; + VectorCopy( pos, newent->s.origin ); + VectorCopy( pos, newent->r.currentOrigin ); + VectorAdd( pos, playerMaxs, newent->r.absmax ); + VectorAdd( pos, playerMins, newent->r.absmin ); + VectorCopy( trav->r.currentAngles, newent->r.currentAngles ); + // is this pointing to an axis or allied spawn point + // BLUE + sptrav = NULL; + while ( ( sptrav = G_Find( sptrav, FOFS( classname ), "team_CTF_bluespawn" ) ) ) { + if ( !( sptrav->spawnflags & 2 ) ) { + continue; // ignore NON-STARTACTIVE spawns + } + VectorSubtract( sptrav->s.origin, trav->r.currentOrigin, v ); + VectorNormalize( v ); + if ( DotProduct( v, dir ) > 0 ) { + blueCount++; + } + } + // RED + sptrav = NULL; + while ( ( sptrav = G_Find( sptrav, FOFS( classname ), "team_CTF_redspawn" ) ) ) { + if ( !( sptrav->spawnflags & 2 ) ) { + continue; // ignore NON-STARTACTIVE spawns + } + VectorSubtract( sptrav->s.origin, trav->r.currentOrigin, v ); + VectorNormalize( v ); + if ( DotProduct( v, dir ) > 0 ) { + redCount++; + } + } + // if the count is hugely in favor of one side, then it must belong to the other side + if ( blueCount - redCount > 4 ) { + newent->aiTeam = TEAM_AXIS; // mostly facing blue spots + } else if ( redCount - blueCount > 4 ) { + newent->aiTeam = TEAM_ALLIES; // mostly facing red spots + } else { newent->aiTeam = 0;} + } + + + // Start TAT 9/24/2002 + // Relink those constructible markers + for ( i = 0; i < numMarkers; i++ ) + { + trap_LinkEntity( &g_entities[constructMarkers[i]] ); + + } + // End TAT 9/24/2002 + +} + +/* +================= +BotMovementAutonomyForString +================= +*/ +int BotMovementAutonomyForString( char *string ) { + if ( !Q_stricmp( string, "high" ) ) { + return BMA_HIGH; + } else if ( !Q_stricmp( string, "medium" ) ) { + return BMA_MEDIUM; + } else if ( !Q_stricmp( string, "low" ) ) { + return BMA_LOW; + } else { + return -1; + } +} + +/* +================= +BotStringForMovementAutonomy +================= +*/ +char *BotStringForMovementAutonomy( int value ) { + switch ( value ) { + case BMA_LOW: return "LOW"; + case BMA_MEDIUM: return "MEDIUM"; + case BMA_HIGH: return "HIGH"; + } + // + return "(unknown)"; +} + +/* +================= +BotWeaponAutonomyForString +================= +*/ +int BotWeaponAutonomyForString( char *string ) { + if ( !Q_stricmp( string, "high" ) ) { + return BWA_HIGH; + } else if ( !Q_stricmp( string, "medium" ) ) { + return BWA_MEDIUM; + } else if ( !Q_stricmp( string, "low" ) ) { + return BWA_LOW; + } else { + return -1; + } +} + +/* +================= +BotStringForWeaponAutonomy +================= +*/ +char *BotStringForWeaponAutonomy( int value ) { + switch ( value ) { + case BMA_LOW: return "LOW"; + case BMA_MEDIUM: return "MEDIUM"; + case BMA_HIGH: return "HIGH"; + } + // + return "(unknown)"; +} + + +/* +================= +BotScriptAutonomyForString +================= +*/ +int BotScriptAutonomyForString( char *string ) { + if ( !Q_stricmp( string, "quitscript" ) ) { + return BSA_QUITSCRIPT; + } else if ( !Q_stricmp( string, "nochase" ) ) { + return BSA_NOCHASE; + } else if ( !Q_stricmp( string, "maintainscript" ) ) { + return BSA_MAINTAINSCRIPT; + } else if ( !Q_stricmp( string, "ignoreenemies" ) ) { + return BSA_IGNOREENEMIES; + } else { + return -1; + } +} + + +/* +================== +BotCheckEmergencyTargets() +================== +*/ +qboolean BotCheckEmergencyTargets( bot_state_t *bs ) { + qboolean retval; + int startTime = 0; + if ( bot_profile.integer == 1 ) { + startTime = trap_Milliseconds(); + } + + retval = BotMP_CheckEmergencyGoals( bs ); + + if ( bot_profile.integer == 1 ) { + botTime_EmergencyGoals += trap_Milliseconds() - startTime; + } + + return retval; +} + +/* +================== +BotFindSpecialGoals() +================== +*/ +qboolean BotFindSpecialGoals( bot_state_t *bs ) { + qboolean retval; + int startTime = 0; + if ( bot_profile.integer == 1 ) { + startTime = trap_Milliseconds(); + } + + trap_Cvar_Update( &bot_findgoal ); + if ( !bot_findgoal.integer ) { + retval = BotMP_FindGoal_New( bs ); + } else { + retval = BotMP_FindGoal( bs ); + } + + if ( bot_profile.integer == 1 ) { + botTime_FindGoals += trap_Milliseconds() - startTime; + } + + return retval; +} + +/* +=================== +BotDefaultNode +=================== +*/ +void BotDefaultNode( bot_state_t *bs ) { + //if the bot is not in a valid area, then there's not much we can do + if ( !bs->areanum || bs->sess.sessionTeam >= TEAM_SPECTATOR || !bs->sess.sessionTeam ) { + AIEnter_MP_Stand( bs ); + + return; + } + + //check emergency goals + BotClearGoal( &bs->target_goal ); + bs->last_checkemergencytargets = 0; // bypass optimizations + if ( BotCheckEmergencyTargets( bs ) ) { + return; + } + + //check idle goals + bs->last_findspecialgoals = 0; // bypass optimizations + if ( BotFindSpecialGoals( bs ) ) { + return; + } + + //just stand around, waiting for a goal (or enemy) to present itself + AIEnter_MP_Stand( bs ); + return; +} + +#define PLAYER_PERFORMANCE_COMMENT_FREQENCY 5 // the number of recent shots we will comment on +#define ACCURACY_CHANGE_THRESHOLD 0.2 // how big should the change (in either direction) be to consider commenting + +// If we're doing well even if it's not an improvement, comment +#define GOOD_ACCURACY_THRESHOLD ( 0.6f ) + +// If we're doing poorly even if it's not worse, comment +#define BAD_ACCURACY_THRESHOLD ( 0.1f ) + +/* +================= +BotCheckVoiceChats() +================= +*/ +void BotCheckVoiceChats( bot_state_t *bs ) { + if ( VectorLengthSquared( bs->cur_ps.velocity ) < SQR( 10 ) ) { + // do we need ammo? + // TAT 10/8/2002 - Lieutenants shouldn't bother asking for ammo + if ( bs->sess.playerType != PC_FIELDOPS && ClientNeedsAmmo( bs->client ) ) { + BotVoiceChatAfterIdleTime( bs->client, "NeedAmmo", SAY_TEAM, 2000 + rand() % 10000, qfalse, 40000 + rand() % 15000, qfalse ); + } + + // do we need health? + // TAT 10/8/2002 - Medics only ask for health if they are dead + if ( ( bs->sess.playerType == PC_MEDIC && BotHealthScale( bs->client ) <= 0.0 ) || ( bs->sess.playerType != PC_MEDIC && BotHealthScale( bs->client ) <= 0.2 ) ) { + BotVoiceChatAfterIdleTime( bs->client, "Medic", SAY_TEAM, 2000 + rand() % 10000, qfalse, 30000 + rand() % 10000, qfalse ); + } + + // if we have received health, then thank someone + if ( bs->sess.playerType != PC_MEDIC && bs->last_checkvoice_health > 0 && bs->cur_ps.stats[STAT_HEALTH] > bs->last_checkvoice_health ) { + BotVoiceChatAfterIdleTime( bs->client, "Thanks", SAY_TEAM, 500 + rand() % 1000, qfalse, 5000 + rand() % 5000, qfalse ); + } + } + + bs->last_checkvoice_health = bs->cur_ps.stats[STAT_HEALTH]; +} + + +/* +================ +BotEnemyFire +================ +*/ +void BotEnemyFire( bot_state_t *bs ) { + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + // TAT 12/6/2002 - don't use the knife here (only can successfully use it in BattleChase) + if ( bs->weaponnum != WP_KNIFE ) { + //aim at the enemy + BotAimAtEnemySP( bs ); + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } + } else { + bs->enemy = -1; + } + } + } +} + +/* +================ +BotShareLastAttacked +================ +*/ +void BotShareLastAttacked( bot_state_t *bs ) { + int i; + bot_state_t *ptbs, *tbs; + gentity_t *ent, *trav; + // + if ( bs->lastAttackShared > level.time - 400 ) { + return; + } + bs->lastAttackShared = level.time; + // + ent = BotGetEntity( bs->client ); + // + for ( i = 0, ptbs = botstates, trav = g_entities; i < level.maxclients; i++, ptbs++, trav++ ) + { + if ( !ptbs->inuse ) { + continue; + } + if ( !BotSameTeam( bs, i ) ) { + continue; + } + // + tbs = ptbs; + // + if ( ent->botLastAttackedTime >= trav->botLastAttackedTime ) { + continue; // no need to share + } + if ( VectorDistanceSquared( bs->origin, tbs->origin ) > SQR( 2048 ) ) { + continue; + } + if ( !trap_InPVS( bs->origin, tbs->origin ) ) { + continue; + } + // + // get their attack time + ent->botLastAttackedTime = trav->botLastAttackedTime; + ent->botLastAttackedEnt = trav->botLastAttackedEnt; + } +} + +/* +============== +BotEnemyCarryingFlag +============== +*/ +qboolean BotEnemyCarryingFlag( int entnum ) { + bot_state_t *pbs; + int i; + + for ( i = 0, pbs = botstates; i < level.maxclients; i++, pbs++ ) { + if ( !pbs->inuse ) { + continue; + } + if ( BotSameTeam( ( pbs ), entnum ) ) { + continue; + } + if ( pbs->sess.sessionTeam == TEAM_SPECTATOR ) { + continue; + } + if ( !BotCarryingFlag( i ) ) { + continue; + } + // + // carrying flag + return qtrue; + } + // + return qfalse; +} + +/* +============== +BotLastAttacked +============== +*/ +int BotLastAttacked( bot_state_t *bs ) { + if ( !g_entities[bs->client].botLastAttackedTime ) { + return -99999; // allow for game starting at time index 0 + } + return g_entities[bs->client].botLastAttackedTime; +} + +/* +============== +BotLastHurt +============== +*/ +int BotLastHurt( bot_state_t *bs ) { + if ( !bs->last_pain ) { + return -99999; // allow for game starting at time index 0 + } + return bs->last_pain; +} + +/* +================= +BotSeekCover +================= +*/ +qboolean BotSeekCover( bot_state_t *bs ) { + int area, enemyarea; + vec3_t autonomyPos; + //static int lastcall; + + if ( bs->enemy < 0 ) { + return qfalse; + } + + // TAT 10/8/2002 + // If I'm in an invalid area, then don't do this, it will crash + if ( bs->areanum == 0 ) { + return qfalse; + } + + // + // limit calls + //if (bs->lastSeekCover >= level.time - 500) return qfalse; // dont hog calls + //if (lastcall == level.time) return qfalse; + //lastcall = level.time; + + + // look for a place to hide from our enemy + BotGetMovementAutonomyPos( bs, autonomyPos ); + // + enemyarea = BotGetArea( bs->enemy ); + // + area = trap_AAS_NearestHideArea( bs->client, bs->origin, bs->areanum, bs->enemy, g_entities[bs->enemy].r.currentOrigin, enemyarea, bs->tfl, BotGetMovementAutonomyRange( bs, NULL ), autonomyPos ); + // + if ( area ) { + // get the area waypoint, and return qtrue + BotClearGoal( &bs->hidegoal ); + if ( trap_AAS_AreaWaypoint( area, bs->hidegoal.origin ) ) { + bs->hidegoal.areanum = area; + return qtrue; + } + } + // + return qfalse; +} + +/* +================= +BotGetEye +================= +*/ +float *BotGetEye( int entnum ) { + #define BOT_GETEYE_NUMEYES 9 + static vec3_t eyes[BOT_GETEYE_NUMEYES]; + static int lastEye = 0; + float *eye; + // + if ( entnum < 0 || entnum >= level.maxclients ) { + G_Error( "BotGetEye: entnum out of range" ); + return NULL; + } + // + eye = &eyes[lastEye][0]; + if ( ++lastEye >= BOT_GETEYE_NUMEYES ) { + lastEye = 0; + } + // + VectorCopy( g_entities[entnum].client->ps.origin, eye ); + eye[2] += g_entities[entnum].client->ps.viewheight; + // + return eye; +} + +/* +================= +BotGetOrigin +================= +*/ +float *BotGetOrigin( int entnum ) { + #define BOT_GETORIGIN_NUMEYES 9 + static vec3_t eyes[BOT_GETORIGIN_NUMEYES]; + static int lastEye = 0; + float *eye; + gentity_t *ent = BotGetEntity( entnum ); + + // TAT 11/12/2002 - using Ryan's botentity system, so might have an entity num out of range + // let's just see if we found one with BotGetEntity + if ( !ent ) { + G_Error( "BotGetOrigin: invalid entity num %d", entnum ); + return NULL; + } + // +// if (entnum < 0 || entnum >= level.num_entities) { +// G_Error( "BotGetOrigin: entnum out of range" ); +// return NULL; +// } + // + eye = &( eyes[lastEye][0] ); + if ( ++lastEye >= BOT_GETORIGIN_NUMEYES ) { + lastEye = 0; + } + // + if ( ( entnum < level.maxclients ) && ( g_entities[entnum].client ) ) { + VectorCopy( ent->client->ps.origin, eye ); + } else if ( g_entities[entnum].s.eType == ET_TRIGGER_MULTIPLE || g_entities[entnum].s.eType == ET_MOVER ) { + VectorAdd( ent->r.absmin, ent->r.absmax, eye ); + VectorScale( eye, 0.5, eye ); + } else if ( VectorLengthSquared( ent->r.currentOrigin ) ) { + VectorCopy( ent->r.currentOrigin, eye ); + } else if ( VectorLengthSquared( ent->s.origin ) ) { + VectorCopy( ent->s.origin, eye ); + } else { + VectorAdd( ent->r.absmin, ent->r.absmax, eye ); + VectorScale( eye, 0.5, eye ); + } + // + return eye; +} + +/* +================ +BotGetArea +================ +*/ +int BotGetArea( int entnum ) { + bot_state_t *bs = NULL; + gentity_t *ent; + // + if ( entnum < level.maxclients ) { + bs = &botstates[entnum]; + } + ent = BotGetEntity( entnum ); + // + if ( !ent ) { + // try to get a server entity + g_serverEntity_t *serverEnt = GetServerEntity( entnum ); + // we found one, so return the cached value + if ( serverEnt ) { + if ( serverEnt->areaNum == -1 ) { + // we haven't calculated it yet + // these don't move, so only calc once + serverEnt->areaNum = BotPointAreaNum( -1, serverEnt->origin ); + } + return serverEnt->areaNum; + } + + // didn't find it + return 0; + } + if ( !bs || !bs->inuse ) { + if ( VectorCompare( BotGetOrigin( entnum ), ent->botGetAreaPos ) ) { + return ent->botGetAreaNum; + } + VectorCopy( BotGetOrigin( entnum ), ent->botGetAreaPos ); + ent->botGetAreaNum = BotPointAreaNum( entnum, ent->botGetAreaPos ); + return ent->botGetAreaNum; + } else { + return bs->areanum; + } +} + +/* +============== +BotReduceListByRange +============== +*/ +int BotReduceListByTravelTime( int *list, int numList, vec3_t destpos, int destarea, int traveltime ) { + int listCopy[MAX_CLIENTS], numListCopy = 0, i; + int areanum, t; + bot_state_t *lbs; + + if ( !traveltime ) { + return numList; // no change + } + + for ( i = 0; i < numList; i++ ) { + areanum = BotGetArea( list[i] ); + if ( !areanum ) { + continue; // eliminate them + } + lbs = &botstates[list[i]]; + if ( !lbs->inuse ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( areanum, BotGetOrigin( list[i] ), destarea, lbs->tfl ); + if ( !t ) { + continue; + } + if ( t >= traveltime ) { + continue; + } + + // + // they passed so copy to the new list + // + listCopy[numListCopy++] = list[i]; + } + + memcpy( list, listCopy, sizeof( int ) * numListCopy ); + return numListCopy; +} + +/* +================ +BotTravelTimeToEntity +================ +*/ +int BotTravelTimeToEntity( bot_state_t *bs, int entnum ) { + int area = 0; + // + if ( !bs->areanum ) { + return 0; + } + // + area = BotGetArea( entnum ); + // + if ( !area ) { + return 0; + } + // + if ( !bs->tfl ) { + bs->tfl = BotTravelFlagsForClient( bs->client ); + } + return trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); +} + +// How far can a bot go to seek cover? +int BotGetSeekCoverRange( bot_state_t *bs, int targetEntity ); + +/* +============== +BotBattleNewNode +============== +*/ +qboolean BotBattleNewNode( bot_state_t *bs ) { + return qfalse; +} + +/* +=============== +BotDirectMoveToGoal +=============== +*/ +qboolean BotDirectMoveToGoal( bot_state_t *bs, bot_goal_t *goal, bot_moveresult_t *moveresult ) { + vec3_t dir; + aas_clientmove_t move; + trace_t tr; + float dist; + // + if ( VectorDistanceSquared( bs->origin, goal->origin ) > SQR( 1400 ) ) { + return qfalse; + } + if ( !trap_InPVS( bs->origin, goal->origin ) ) { + return qfalse; + } +// trap_Trace( &tr, bs->origin, vec3_origin, vec3_origin, goal->origin, bs->client, MASK_PLAYERSOLID & ~CONTENTS_BODY ); +// if (tr.fraction < 1.0) return qfalse; + // + VectorSubtract( goal->origin, bs->origin, dir ); + dist = VectorNormalize( dir ); + VectorScale( dir, 300, dir ); + // + if ( trap_AAS_PredictClientMovement( &move, bs->client, bs->origin, + goal->entitynum, qfalse, + dir, goal->origin, -1, + 40, 0.05, SE_ENTERAREA | SE_HITGROUNDDAMAGE | SE_HITENT | SE_HITGROUNDAREA | SE_STUCK | SE_GAP, goal->areanum, +#ifdef _DEBUG + qtrue ) ) +#else + qfalse ) ) +#endif + { + // + // check for a good stop event + // + switch ( move.stopevent ) { + case SE_ENTERAREA: + case SE_HITENT: + case SE_HITGROUNDAREA: + memset( moveresult, 0, sizeof( *moveresult ) ); + VectorNormalize( dir ); + VectorCopy( dir, moveresult->movedir ); + if ( dist < 200 ) { + trap_EA_Move( bs->client, dir, 400 - ( 320.0f * ( 128.0f - dist ) / 128.0f ) ); + } else { + trap_EA_Move( bs->client, dir, 400 ); + } + // check against other players/bots + // TAT 2/3/2003 - you can be blocked by stuff that isn't a player - changing trace mask to include other stuff + trap_Trace( &tr, bs->origin, bs->cur_ps.mins, bs->cur_ps.maxs, goal->origin, bs->client, MASK_SHOT /*CONTENTS_BODY*/ ); + if ( tr.fraction < .99f && VectorDistanceSquared( bs->origin, tr.endpos ) < SQR( 30 ) && tr.entityNum != ENTITYNUM_WORLD ) { // best not be worldspawn + // blocked, avoid them + moveresult->blocked = qtrue; + moveresult->blockentity = tr.entityNum; + } + moveresult->flags |= MOVERESULT_DIRECTMOVE; + // + return qtrue; + } + } + // + return qfalse; +} +/* +================= +BotEntityTargetClassnameMatch +================= +*/ +qboolean BotEntityTargetClassnameMatch( int entityNum, const char *classname ) { + gentity_t *ent; + // + if ( entityNum < 0 || entityNum > level.num_entities ) { + return qfalse; + } + ent = BotGetEntity( entityNum ); + if ( !ent->inuse ) { + return qfalse; + } + if ( !ent->target ) { + return qfalse; + } + if ( !ent->target_ent ) { + return qfalse; + } + if ( !ent->target_ent->inuse ) { + return qfalse; + } + // + if ( !Q_stricmp( ent->target_ent->classname, classname ) ) { + return qtrue; + } + // + return qfalse; +} + +/* +================ +BotGetReachableEntityArea +================ +*/ +qboolean BotGetReachableEntityArea( bot_state_t *bs, int entityNum, bot_goal_t *goal ) { + vec3_t brushPos, vec, center, mins, maxs; + //int list[256], numList; + int oldestTime = 0, i, oldest = 0; + //float bestDist, dist; + gentity_t *ent; + trace_t tr; + + ent = BotGetEntity( entityNum ); + + if ( VectorDistanceSquared( ent->r.absmin, ent->r.absmax ) > ( 16 * 16 ) ) { + VectorAdd( ent->r.absmin, ent->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + + oldest = BotReachableBBoxAreaNum( bs, ent->r.absmin, ent->r.absmax ); + if ( !oldest ) { + VectorCopy( ent->r.absmax, maxs ); + maxs[2] += 32; + oldest = BotReachableBBoxAreaNum( bs, ent->r.absmin, maxs ); + } + + if ( oldest ) { + oldestTime = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, oldest, bs->tfl ); + } + } else { // use entity point + // TODO + i = BotGetArea( entityNum ); + oldestTime = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, i, bs->tfl ); + } + + if ( oldestTime <= 0 ) { + return qfalse; + } + + BotClearGoal( goal ); + // use this as the goal origin + if ( !trap_AAS_AreaWaypoint( oldest, center ) ) { + trap_AAS_AreaCenter( oldest, center ); + } + + // if the entity is a trigger, then we must make sure we are within the brush + if ( ent->r.contents & CONTENTS_TRIGGER ) { + VectorCopy( center, vec ); + VectorAdd( center, bs->cur_ps.mins, mins ); + VectorAdd( center, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, ent ) ) { + VectorCopy( brushPos, center ); + center[2] = vec[2]; + VectorCopy( brushPos, vec ); + vec[2] -= 512; + // trace to the ground + trap_Trace( &tr, center, bs->cur_ps.mins, bs->cur_ps.maxs, vec, -1, MASK_PLAYERSOLID & ~CONTENTS_BODY ); + VectorCopy( tr.endpos, center ); + // test this spot + VectorAdd( center, bs->cur_ps.mins, mins ); + VectorAdd( center, bs->cur_ps.maxs, maxs ); + if ( !trap_EntityContactCapsule( mins, maxs, ent ) ) { + return qfalse; + } + oldest = BotPointAreaNum( bs->client, center ); + if ( !oldest ) { + return qfalse; + } + } + } + VectorCopy( center, goal->origin ); + VectorCopy( bs->cur_ps.mins, goal->mins ); + VectorCopy( bs->cur_ps.maxs, goal->maxs ); + goal->areanum = oldest; + goal->entitynum = ent->s.number; + goal->flags = GFL_NOSLOWAPPROACH; + + return qtrue; +} + +/* +==================== +BotIsConstructible + + returns qtrue if the contructible attached to the given target_objective_info can be built +==================== +*/ +qboolean BotIsConstructible( team_t team, int toiNum ) { + gentity_t* ent; + gentity_t *toi = &g_entities[toiNum]; + + // we dont wanna build this + if ( toi->aiInactive & ( 1 << team ) ) { + return qfalse; + } + + if ( !( ent = G_ConstructionForTeam( toi, team ) ) ) { + return qfalse; + } + + if ( G_ConstructionIsFullyBuilt( ent ) ) { + return qfalse; + } + + if ( G_ConstructionIsPartlyBuilt( ent ) ) { + return qtrue; + } + + if ( ent->chain && G_ConstructionBegun( ent->chain ) ) { + return qfalse; + } + + return qtrue; +} + +/* +================= +BotCanSnipe + + returns WP_NONE if we cant snipe, otherwise returns the weapon we can snipe with +================= +*/ +int BotCanSnipe( bot_state_t *bs, qboolean checkAmmo ) { + int sniperWeapons[] = {WP_GARAND_SCOPE, WP_K43_SCOPE, WP_FG42SCOPE, -1}; + int i, best, bestAmmo, thisAmmo; + + // Gordon: early out if not covert ops only they have sniper weapons + if ( bs->cur_ps.stats[STAT_PLAYER_CLASS] != PC_COVERTOPS ) { + return WP_NONE; + } + + best = WP_NONE; + bestAmmo = 0; + for ( i = 0; sniperWeapons[i] > -1; i++ ) { + if ( !COM_BitCheck( bs->cur_ps.weapons, sniperWeapons[i] ) ) { + continue; + } + + thisAmmo = BotGotEnoughAmmoForWeapon( bs, sniperWeapons[i] ); + if ( checkAmmo && !thisAmmo ) { + continue; + } + if ( best > -1 && !thisAmmo ) { + continue; + } + + if ( best == -1 || ( !bestAmmo && thisAmmo ) ) { + best = sniperWeapons[i]; + bestAmmo = thisAmmo; + } + } + + return best; +} + +/* +================= +BotHealthScale +================= +*/ +float BotHealthScale( int entnum ) { + return g_entities[entnum].health / (float)g_entities[entnum].client->ps.stats[STAT_MAX_HEALTH]; +} + +/* +================== +EnemyIsCloseEnoughToFight() +================== + +Returns true if we can do the battle fight. +Returns false if enemy is too far or not visible + +*/ +qboolean EnemyIsCloseEnoughToFight +( + bot_state_t *bs +) { + // Local Variables //////////////////////////////////////////////////////// + aas_entityinfo_t entinfo; + /////////////////////////////////////////////////////////////////////////// + + // Get location of enemy and other info. + BotEntityInfo( bs->enemy, &entinfo ); + + // If we've got the knife, the enemy isn't visible, or the enemy is too far, we + // can't use battle fight. + if ( ( bs->weaponnum == WP_KNIFE ) + || !BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) + || ( VectorDistanceSquared( bs->origin, entinfo.origin ) > SQR( kBOT_CHASE_RANGE ) ) + ) { + return qfalse; + } + + // Otherwise, we're good to fight + return qtrue; + +} + +// TAT 11/21/2002 +// This is silly - almost all the single player ai nodes do the same thing +// Find an enemy and try to attack it +void BotFindAndAttackEnemy( bot_state_t *bs ) { + // check for enemies + if ( bs->enemy < 0 ) { + BotFindEnemyMP( bs, -1, qfalse ); + } + if ( bs->enemy >= 0 ) { + aas_entityinfo_t entinfo; + BotEntityInfo( bs->enemy, &entinfo ); + //if the enemy is dead + if ( bs->enemydeath_time ) { + if ( bs->enemydeath_time < trap_AAS_Time() - 0.3 ) { + bs->enemydeath_time = 0; + + bs->enemy = -1; + } + } else { + if ( EntityIsDead( &entinfo ) ) { + bs->enemydeath_time = trap_AAS_Time(); + } + } + // + if ( bs->enemy >= 0 ) { + // attack and keep moving + if ( BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy, NULL ) ) { + //choose the best weapon to fight with + BotChooseWeapon( bs ); + + BotAimAtEnemy( bs ); + + //attack the enemy if possible + if ( bs->weaponnum == bs->cur_ps.weapon ) { + BotCheckAttack( bs ); + } + } else { + bs->enemy = -1; + } + } + } +} + +void BotUpdateViewAngles( bot_state_t *bs, bot_goal_t *goal, bot_moveresult_t moveresult ) { + vec3_t target; + vec3_t dir; + + if ( bs->enemy < 0 && !( bs->flags & BFL_IDEALVIEWSET ) ) { + //FIXME: look at cluster portals? + if ( VectorLengthSquared( moveresult.movedir ) ) { + vectoangles( moveresult.movedir, bs->ideal_viewangles ); + } else if ( trap_BotMovementViewTarget( bs->ms, &goal, bs->tfl, 300, target ) ) { + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + } else if ( random() < bs->thinktime * 0.8 ) { + BotRoamGoal( bs, target ); + VectorSubtract( target, bs->origin, dir ); + vectoangles( dir, bs->ideal_viewangles ); + bs->ideal_viewangles[2] *= 0.5; + } + bs->ideal_viewangles[2] *= 0.5; + } +} + +// Gorodn: set whether a bot is a POW or not +void BotSetPOW( int entityNum, qboolean isPOW ) { + botstates[entityNum].isPOW = isPOW; +} + +/* +================== +BotBestTargetWeapon +================== +*/ +int BotBestTargetWeapon( bot_state_t *bs, int targetNum ) { + int bestWeapon, i, *ammo, validWeapons[2]; + float wantScale, bestWantScale, dist; + gentity_t *ent = &g_entities[targetNum]; + + memset( validWeapons, 0, sizeof( validWeapons ) ); + + // this function currently only supports the following types of target entities + if ( ent->s.eType == ET_MOVER ) { + if ( ent->health > 0 ) { + // explosive weapons are always valid + COM_BitSet( validWeapons, WP_PANZERFAUST ); + COM_BitSet( validWeapons, WP_GRENADE_LAUNCHER ); + COM_BitSet( validWeapons, WP_GRENADE_PINEAPPLE ); + COM_BitSet( validWeapons, WP_SMOKE_MARKER ); + + if ( bs->sess.playerType == PC_FIELDOPS ) { + COM_BitSet( validWeapons, WP_BINOCULARS ); + } + + COM_BitSet( validWeapons, WP_MORTAR ); + COM_BitSet( validWeapons, WP_GPG40 ); + COM_BitSet( validWeapons, WP_M7 ); + + if ( !( ent->spawnflags & 4 ) ) { // allow other weapons + // use any of these + COM_BitSet( validWeapons, WP_MP40 ); + COM_BitSet( validWeapons, WP_THOMPSON ); + COM_BitSet( validWeapons, WP_KAR98 ); + COM_BitSet( validWeapons, WP_CARBINE ); + COM_BitSet( validWeapons, WP_MOBILE_MG42 ); + COM_BitSet( validWeapons, WP_K43 ); + COM_BitSet( validWeapons, WP_FG42 ); + } + } + } else if ( ent->s.eType == ET_CONSTRUCTIBLE ) { + if ( ent->health > 0 ) { + if ( ent->spawnflags & 16 ) { + // explosive + COM_BitSet( validWeapons, WP_PANZERFAUST ); + COM_BitSet( validWeapons, WP_GRENADE_LAUNCHER ); + COM_BitSet( validWeapons, WP_GRENADE_PINEAPPLE ); + COM_BitSet( validWeapons, WP_SMOKE_MARKER ); + + if ( bs->sess.playerType == PC_FIELDOPS ) { + COM_BitSet( validWeapons, WP_BINOCULARS ); + } + + COM_BitSet( validWeapons, WP_MORTAR ); + COM_BitSet( validWeapons, WP_GPG40 ); + COM_BitSet( validWeapons, WP_M7 ); + } + } + } + + // fast out, if we simply have none of these weapons + if ( !( validWeapons[0] & bs->cur_ps.weapons[0] ) && !( validWeapons[1] & bs->cur_ps.weapons[0] ) ) { + return WP_NONE; + } + + ammo = bs->cur_ps.ammo; + bestWantScale = 0.0; + bestWeapon = WP_NONE; // if nothing it found, return WP_NONE + + dist = VectorDistanceSquared( bs->origin, BotGetOrigin( ent->s.number ) ); + + for ( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ ) { + if ( COM_BitCheck( bs->cur_ps.weapons, i ) && COM_BitCheck( validWeapons, i ) ) { + float range; + + // check that our ammo is enough + if ( !BotGotEnoughAmmoForWeapon( bs, i ) ) { + continue; + } + + // within range? + range = BotWeaponRange( bs, i ) /*+ 512*/; + if ( SQR( range ) < dist ) { + continue; + } + + // get the wantScale for this weapon given the current circumstances (0.0 - 1.0) + wantScale = BotWeaponWantScale( bs, i ); + if ( wantScale >= bestWantScale ) { + bestWeapon = i; + bestWantScale = wantScale; + } + } + } + + return bestWeapon; +} + +/* +==================== +BotGetVisibleDamagableScriptMover +==================== +*/ +gentity_t *BotGetVisibleDamagableScriptMover( bot_state_t *bs ) { + gentity_t *trav; + int i, wpn; + + for ( i = MAX_CLIENTS, trav = g_entities + MAX_CLIENTS; i < level.num_entities; i++, trav++ ) { + if ( !trav->inuse ) { + continue; + } + + if ( trav->s.eType != ET_MOVER && trav->s.eType != ET_CONSTRUCTIBLE ) { + continue; + } + + // is it disabled? + if ( trav->aiInactive & ( 1 << bs->sess.sessionTeam ) ) { + continue; + } + + // is it damagable? + if ( trav->health <= 0 ) { + continue; + } + + if ( trav->s.eType == ET_MOVER ) { + // is it an enemy item? + if ( bs->sess.sessionTeam == TEAM_ALLIES && ( trav->spawnflags & 32 ) ) { + continue; // it's ours + } + if ( bs->sess.sessionTeam == TEAM_AXIS && ( trav->spawnflags & 64 ) ) { + continue; // it's ours + } + } else { + if ( !( trav->spawnflags & 16 ) ) { + continue; + } + // is it an enemy item? + if ( bs->sess.sessionTeam == TEAM_ALLIES && ( trav->spawnflags & 8 ) ) { + continue; // it's ours + } + if ( bs->sess.sessionTeam == TEAM_AXIS && ( trav->spawnflags & 4 ) ) { + continue; // it's ours + } + } + + // do we have a weapon that could hurt it? + if ( ( wpn = BotBestTargetWeapon( bs, i ) ) == WP_NONE ) { + continue; + } + + if ( !BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, i, NULL ) ) { + continue; + } + +/* // it's an enemy mover, can we see it? + if (!trap_InPVS( bs->eye, BotGetOrigin( i ) )) { + continue; + } + + trap_Trace( &tr, bs->eye, vec3_origin, vec3_origin, BotGetOrigin( i ), -1, MASK_PLAYERSOLID & ~(CONTENTS_BODY) ); + if (tr.entityNum != i) { + continue; + }*/ + + // + // we found one + return trav; + } + + return NULL; +} + +/* +================ +BotCountLandMines +================ +*/ +void BotCountLandMines( void ) { + gentity_t *trav, *mine; + vec3_t org; + static int lasttime; + + // only check every second + if ( lasttime && lasttime < level.time && lasttime > level.time - 1000 ) { + return; + } + lasttime = level.time; + + // reset counts + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINE_AREA ) ) ) { + trav->count2 = 0; + VectorClear( trav->pos3 ); + } + + mine = g_entities + level.maxclients; + while ( ( mine = G_FindLandmine( mine ) ) ) { + // doesn't matter if it's not armed, so that we dont drop too many landmines at once + + VectorCopy( mine->r.currentOrigin, org ); + org[2] += 16; + + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINE_AREA ) ) ) { + // are we within range? + if ( PointInBounds( org, trav->r.absmin, trav->r.absmax ) ) { + trav->count2++; + // add this position to the average point + VectorAdd( trav->r.currentOrigin, trav->pos3, trav->pos3 ); + // dont break here or else if we have overlapping areas, all mines in the overlapping section will only count to the first area + } + } + } + + // finalize average points + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_BOT_LANDMINE_AREA ) ) ) { + if ( trav->count2 ) { + VectorSubtract( trav->pos3, BotGetOrigin( trav->s.number ), trav->pos3 ); + VectorScale( trav->pos3, -1.0f / trav->count2, trav->pos3 ); + } + } +} + +// TAT 1/6/2003 - Bot picks up a new weapon +void BotPickupWeapon( int client, int weaponnum, qboolean alreadyHave ) { + // if we didn't have any weapon before, we want to use this one + bot_state_t *bs = &botstates[client]; + gentity_t *player; + int i; + + if ( !bs->inuse ) { + return; + } + + if ( !alreadyHave && ( bs->weaponnum == WP_NONE ) ) { + bs->weaponnum = weaponnum; + } + + // force an update of our weapon + BotChooseWeapon( bs ); + + // make sure we tell all the clients that we have a weapon + for ( i = 0; i < level.numConnectedClients; i++ ) + { + // send the noweapon command with who as the 1st param, and 0 meaning we don't have no weapon + player = g_entities + level.sortedClients[i]; + if ( player->inuse && player->client->sess.sessionTeam == bs->mpTeam ) { + trap_SendServerCommand( player->s.number, va( "nwp %i 0", bs->client ) ); + } + } + +} + +/* +================== +BotEntityWithinView +================== +*/ +qboolean BotEntityWithinView( bot_state_t *bs, int viewEnt ) { + vec3_t dir, ang; + pmoveExt_t *pmExt; + float arcMin, arcMax, arcDiff, yawDiff, pitchDiff; + float pitchMax = 40.f; + // + if ( viewEnt >= level.maxclients ) { + return qfalse; + } + if ( level.clients[viewEnt].pers.connected != CON_CONNECTED ) { + return qfalse; + } + // + VectorSubtract( BotGetOrigin( viewEnt ), bs->origin, dir ); + VectorNormalize( dir ); + vectoangles( dir, ang ); + // + pmExt = &( level.clients[bs->client].pmext ); + // + if ( BG_PlayerMounted( bs->cur_ps.eFlags ) ) { + + // limit harc and varc + + // pitch (varc) + arcMax = pmExt->varc; + if ( bs->cur_ps.eFlags & EF_AAGUN_ACTIVE ) { + arcMin = 0; + } else if ( bs->cur_ps.eFlags & EF_MOUNTEDTANK ) { + // FIXME: fix this at allow min angle clamp... + arcMin = 20; + arcMax = 50; + } else { + arcMin = pmExt->varc / 2; + } + arcDiff = AngleNormalize180( ang[PITCH] - pmExt->centerangles[PITCH] ); + + if ( arcDiff > arcMin ) { + return qfalse; + } else if ( arcDiff < -arcMax ) { + return qfalse; + } + + if ( !( bs->cur_ps.eFlags & EF_MOUNTEDTANK ) ) { + // yaw (harc) + arcMin = arcMax = pmExt->harc; + arcDiff = AngleNormalize180( ang[YAW] - pmExt->centerangles[YAW] ); + + if ( arcDiff > arcMin ) { + return qfalse; + } else if ( arcDiff < -arcMax ) { + return qfalse; + } + } + } else if ( bs->cur_ps.weapon == WP_MORTAR_SET ) { + // yaw + yawDiff = ang[YAW] - pmExt->mountedWeaponAngles[YAW]; + + if ( yawDiff > 180 ) { + yawDiff -= 360; + } else if ( yawDiff < -180 ) { + yawDiff += 360; + } + + if ( yawDiff > 30 ) { + return qfalse; + } else if ( yawDiff < -30 ) { + return qfalse; + } + + // pitch + pitchDiff = ang[PITCH] - pmExt->mountedWeaponAngles[PITCH]; + + if ( pitchDiff > 180 ) { + pitchDiff -= 360; + } else if ( pitchDiff < -180 ) { + pitchDiff += 360; + } + + if ( pitchDiff > ( pitchMax - 10.f ) ) { + return qfalse; + } else if ( pitchDiff < -( pitchMax ) ) { + return qfalse; + } + } else if ( bs->cur_ps.eFlags & EF_PRONE ) { + + // Check if we are allowed to rotate to there + if ( bs->cur_ps.weapon == WP_MOBILE_MG42_SET ) { + pitchMax = 20.f; + + // yaw + yawDiff = ang[YAW] - pmExt->mountedWeaponAngles[YAW]; + + if ( yawDiff > 180 ) { + yawDiff -= 360; + } else if ( yawDiff < -180 ) { + yawDiff += 360; + } + + if ( yawDiff > 20 ) { + return qfalse; + } else if ( yawDiff < -20 ) { + return qfalse; + } + } + + // pitch + pitchDiff = ang[PITCH] - pmExt->mountedWeaponAngles[PITCH]; + + if ( pitchDiff > 180 ) { + pitchDiff -= 360; + } else if ( pitchDiff < -180 ) { + pitchDiff += 360; + } + + if ( pitchDiff > pitchMax ) { + return qfalse; + } else if ( pitchDiff < -pitchMax ) { + return qfalse; + } + } + + return qtrue; +} diff --git a/src/botai/ai_dmq3.h b/src/botai/ai_dmq3.h new file mode 100644 index 0000000..8e35a94 --- /dev/null +++ b/src/botai/ai_dmq3.h @@ -0,0 +1,313 @@ +/* +=========================================================================== + +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: ai_dmq3.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + + + +// How many targets we'll let the bots choose between? +#define BOT_MAX_POTENTIAL_TARGETS 10 + +// This value stands for an un-inited/bad target +#define BOT_INVALID_TARGET ( -1 ) + +// Penalties and bonusses used in choosing targets +#define BOT_TARGET_ALREADY_TARGETTED_PENALTY ( 5 ) +#define BOT_TARGET_PERSISTENT_ENEMY_BONUS ( -2 ) +#define BOT_TARGET_SHOT_ME_BONUS ( -9 ) +#define BOT_TARGET_VISUAL_BONUS ( -2 ) + +/////////////////////////////////////////////////////////////////////////////// +// +// STRUCT BotTarget +// +typedef struct BotTarget_s +{ + // Who is this target? + int m_target; + + // Distance from bot + float m_distance; + + // Did he shoot us? + qboolean m_thisGuyShotUsDammit; + + // Did we see him? + qboolean m_canSeeTarget; + + // Did we hear him? + qboolean m_canHearTarget; + + // Did we hear footsteps? + qboolean m_heardFootSteps; + + // Did we hear shooting? + qboolean m_heardShooting; + +} BotTarget_t; +// +// STRUCT AI_Team +// +/////////////////////////////////////////////////////////////////////////////// + +// Did this bot just shoot me? +qboolean BotJustShotMe( bot_state_t *bs, int suspect ); + +void BotCountLandMines( void ); +int BotBestTargetWeapon( bot_state_t *bs, int targetNum ); +gentity_t *BotGetVisibleDamagableScriptMover( bot_state_t *bs ); +qboolean BotEnemyCarryingFlag( int entnum ); +float BotHealthScale( int entnum ); +int BotGetNumVisibleSniperSpots( bot_state_t *bs ); +int BotCanSnipe( bot_state_t *bs, qboolean checkAmmo ); +qboolean BotIsConstructible( team_t botTeam, int toiNum ); +qboolean BotGetReachableEntityArea( bot_state_t *bs, int entityNum, bot_goal_t *goal ); +qboolean BotEntityTargetClassnameMatch( int entityNum, const char *classname ); +qboolean BotDirectMoveToGoal( bot_state_t *bs, bot_goal_t *goal, bot_moveresult_t *moveresult ); +int BotLastHurt( bot_state_t *bs ); +qboolean BotBattleNewNode( bot_state_t *bs ); +int BotLastAttacked( bot_state_t *bs ); +int BotTravelTimeToEntity( bot_state_t *bs, int entnum ); +int BotReduceListByTravelTime( int *list, int numList, vec3_t destpos, int destarea, int traveltime ); +qboolean BotCanAttack( bot_state_t *bs ); +float *BotGetOrigin( int entnum ); +float *BotGetEye( int entnum ); +int BotGetArea( int entnum ); +qboolean BotSeekCover( bot_state_t *bs ); +void BotShareLastAttacked( bot_state_t *bs ); +char *BotStringForMovementAutonomy( int value ); +char *BotStringForWeaponAutonomy( int value ); +qboolean BotCanPursue( bot_state_t *bs, int enemy ); +int BotGetMovementAutonomyLevel( bot_state_t *bs ); +void BotEnemyFire( bot_state_t *bs ); +void BotDefaultNode( bot_state_t *bs ); +qboolean BotGetMovementAutonomyPos( bot_state_t *bs, vec3_t pos ); +qboolean BotCheckEmergencyTargets( bot_state_t *bs ); +qboolean BotFindSpecialGoals( bot_state_t *bs ); +void BotCheckVoiceChats( bot_state_t *bs ); +int BotWeaponAutonomyForString( char *string ); +int BotMovementAutonomyForString( char *string ); +int BotScriptAutonomyForString( char *string ); +float BotGetRawMovementAutonomyRange( bot_state_t *bs ); +float BotGetMovementAutonomyRange( bot_state_t *bs, bot_goal_t *goal ); + +// Start - TAT 9/18/2002 +// What distance should a bot be from its leader during a follow order? Based on autonomy +float BotGetFollowAutonomyDist( bot_state_t *bs ); + +// Is a bot within the desired distance of its leader? +qboolean BotWithinLeaderFollowDist( bot_state_t *bs ); +// End - TAT 9/18/2002 + +// Start TAT 9/23/2002 +// Update recon state information for a bot +void BotUpdateReconInfo( bot_state_t *bs ); + +qboolean BotPointWithinMovementAutonomy( bot_state_t *bs, bot_goal_t *goal, vec3_t point ); +qboolean BotPointWithinRawMovementAutonomy( bot_state_t *bs, vec3_t point ); +qboolean BotGoalWithinMovementAutonomy( bot_state_t *bs, bot_goal_t *goal, int urgency ); +qboolean BotCheckMovementAutonomy( bot_state_t *bs, bot_goal_t *goal ); +qboolean BotGoalForEntity( bot_state_t *bs, int entityNum, bot_goal_t *goal, int urgency ); +qboolean BotDangerousGoal( bot_state_t *bs, bot_goal_t *goal ); +qboolean BotCarryingFlag( int client ); +qboolean BotFindNearbyGoal( bot_state_t *bs ); + +float BotWeaponWantScale( bot_state_t *bs, weapon_t weapon ); +qboolean BotWeaponCharged( bot_state_t *bs, int weapon ); +void BotRecordTeamChange( int client ); +void BotRecordKill( int client, int enemy ); +void BotRecordPain( int client, int enemy, int mod ); +void BotRecordDeath( int client, int enemy ); +void BotGetAimAccuracySkill( bot_state_t *bs, float *outAimAccuracy, float *outSkill ); +int BotBestSniperSpot( bot_state_t *bs ); +int BotBestLandmineSpotingSpot( bot_state_t *bs ); +int BotGetRandomVisibleSniperSpot( bot_state_t *bs ); +void BotClearGoal( bot_goal_t *goal ); +qboolean BotGotEnoughAmmoForWeapon( bot_state_t *bs, int weapon ); +int BotReachableBBoxAreaNum( bot_state_t *bs, vec3_t absmin, vec3_t absmax ); +void BotDropFlag( bot_state_t *bs ); +int BotBestMG42Spot( bot_state_t *bs, qboolean force ); + +//setup the deathmatch AI +void BotSetupDeathmatchAI( void ); +//shutdown the deathmatch AI +void BotShutdownDeathmatchAI( void ); +//let the bot live within it's deathmatch AI net +void BotDeathmatchAI( bot_state_t *bs, float thinktime ); +//free waypoints +void BotFreeWaypoints( bot_waypoint_t *wp ); +//choose a weapon +void BotChooseWeapon( bot_state_t *bs ); + +// TAT 11/14/2002 -Bot cycles to next weapon in inventory, and will use it until told to cycle again +void BotCycleWeapon( bot_state_t *bs ); + +// Gordon: set pow status +void BotSetPOW( int entityNum, qboolean isPOW ); + +//setup movement stuff +void BotSetupForMovement( bot_state_t *bs ); +//update the inventory +void BotUpdateInventory( bot_state_t *bs ); +//update the inventory during battle +void BotUpdateBattleInventory( bot_state_t *bs, int enemy ); +//use holdable items during battle +void BotBattleUseItems( bot_state_t *bs ); +//return true if the bot is dead +qboolean BotIsDead( bot_state_t *bs ); +//returns true if the bot is in observer mode +qboolean BotIsPOW( bot_state_t *bs ); +//Gordon: returns true if the bot is a prisoner of war +qboolean BotIsObserver( bot_state_t *bs ); +//returns true if the bot is in the intermission +qboolean BotIntermission( bot_state_t *bs ); +//returns true if the bot is in lava +qboolean BotInLava( bot_state_t *bs ); +//returns true if the bot is in slime +qboolean BotInSlime( bot_state_t *bs ); +//returns true if the entity is dead +qboolean EntityIsDead( aas_entityinfo_t *entinfo ); +//returns true if the entity is in limbo +qboolean EntityInLimbo( aas_entityinfo_t *entinfo ); +//returns true if the entity is invisible +qboolean EntityIsInvisible( aas_entityinfo_t *entinfo ); +//returns true if the entity is shooting +qboolean EntityIsShooting( aas_entityinfo_t *entinfo ); +//returns the name of the client +char *ClientName( int client, char *name, int size ); +//returns an simplyfied client name +char *EasyClientName( int client, char *name, int size ); +//returns the skin used by the client +char *ClientSkin( int client, char *skin, int size ); +//returns the aggression of the bot in the range [0, 100] +float BotAggression( bot_state_t *bs ); +//returns true if the bot wants to retreat +int BotWantsToRetreat( bot_state_t *bs ); +//returns true if the bot wants to chase +int BotWantsToChase( bot_state_t *bs ); +//returns true if the bot wants to help +int BotWantsToHelp( bot_state_t *bs ); +//returns true if the bot can and wants to rocketjump +int BotCanAndWantsToRocketJump( bot_state_t *bs ); +//returns true if the bot wants to and goes camping +int BotWantsToCamp( bot_state_t *bs ); +//the bot will perform attack movements +bot_moveresult_t BotAttackMove( bot_state_t *bs, int tfl ); +//returns true if the bot and the entity are in the same team +int BotSameTeam( bot_state_t *bs, int entnum ); + +//returns true if teamplay is on +int TeamPlayIsOn( void ); + +// Set up our danger spots +void BotFindEnemies +( + bot_state_t *bs, + int *dangerSpots, + int *dangerSpotCount +); + +// Returns true if we can do the battle fight. +// Returns false if enemy is too far or not visible +qboolean EnemyIsCloseEnoughToFight( bot_state_t *bs ); + +// TAT 11/21/2002 +// Find an enemy and try to attack it +void BotFindAndAttackEnemy( bot_state_t *bs ); +// Update the viewangle +void BotUpdateViewAngles( bot_state_t *bs, bot_goal_t *goal, bot_moveresult_t moveresult ); + +//returns true and sets the .enemy field when an enemy is found +int BotFindEnemyMP( bot_state_t *bs, int curenemy, qboolean ignoreViewRestrictions ); + +//returns true if the entity is within our view restrictions +qboolean BotEntityWithinView( bot_state_t *bs, int viewEnt ); + +float BotWeaponRange( bot_state_t *bs, int weaponnum ); +qboolean BotScopedWeapon( int weapon ); + +//returns a roam goal +void BotRoamGoal( bot_state_t *bs, vec3_t goal ); +//returns entity visibility in the range [0, 1] +float BotEntityVisible( int viewer, vec3_t eye, vec3_t viewangles, float fov, int ent, vec3_t entorigin ); +//the bot will aim at the current enemy +void BotAimAtEnemy( bot_state_t *bs ); +//the bot will aim at the current enemy +void BotAimAtEnemySP( bot_state_t *bs ); +//check if the bot should attack +qboolean BotCheckAttack( bot_state_t *bs ); +//AI when the bot is blocked +void BotAIBlocked( bot_state_t *bs, bot_moveresult_t *moveresult, int activate ); +//create a new waypoint +bot_waypoint_t *BotCreateWayPoint( char *name, vec3_t origin, int areanum ); +//find a waypoint with the given name +bot_waypoint_t *BotFindWayPoint( bot_waypoint_t *waypoints, char *name ); +//strstr but case insensitive +char *stristr( char *str, char *charset ); +// +int BotPointAreaNum( int entnum, vec3_t origin ); +// +void BotMapScripts( bot_state_t *bs ); +// +qboolean BotMoveWhileFiring( int weapon ); + +qboolean ChangeBotAlertState( bot_state_t *bs, aistateEnum_t newAlertState, qboolean force ); + +//ctf flags +#define CTF_FLAG_NONE 0 +#define CTF_FLAG_RED 1 +#define CTF_FLAG_BLUE 2 +//CTF skins +#define CTF_SKIN_REDTEAM "red" +#define CTF_SKIN_BLUETEAM "blue" +//CTF teams +#define CTF_TEAM_NONE 0 +#define CTF_TEAM_AXIS 1 +#define CTF_TEAM_ALLIES 2 + +extern int gametype; //game type + +// Rafael gameskill +extern int gameskill; +// done + +extern vmCvar_t bot_grapple; +extern vmCvar_t bot_rocketjump; +extern vmCvar_t bot_fastchat; +extern vmCvar_t bot_nochat; +extern vmCvar_t bot_testrchat; + +extern bot_goal_t ctf_redflag; +extern bot_goal_t ctf_blueflag; diff --git a/src/botai/ai_func_decs.h b/src/botai/ai_func_decs.h new file mode 100644 index 0000000..ddb0cf6 --- /dev/null +++ b/src/botai/ai_func_decs.h @@ -0,0 +1,474 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +extern int BotObjectiveFunc ( bot_state_t * bs ) ; +extern int BotBehaviourFunc ( bot_state_t * bs ) ; +extern int BotEngagementFunc ( bot_state_t * bs ) ; +extern void BotSetLeaderTagEnt ( bot_state_t * bs ) ; +extern int EntGetNumBotFollowers ( int entNum ) ; +extern int BotGetLeader ( bot_state_t * bs , qboolean onlyRequested ) ; +extern qboolean G_RequestedFollow ( bot_state_t * bs , int client ) ; +extern void BotSpawnSpecialEntities ( void ) ; +extern void BotVoiceChatAfterTeamIdleTime ( int client , const char * id , int mode , int delay , qboolean voiceonly , int idleTime , qboolean forceIfDead ) ; +extern void AI_Team_Init_All_Teams ( ) ; +extern void AI_Team_Init ( AI_Team_t * thisOne ) ; +extern void BotVoiceChatAfterIdleTime ( int client , const char * id , int mode , int delay , qboolean voiceonly , int idleTime , qboolean forceIfDead ) ; +extern void BotSendVoiceChat ( bot_state_t * bs , const char * id , int mode , int delay , qboolean voiceonly , qboolean forceIfDead ) ; +extern void BotDelayedVoiceChat ( gentity_t * ent ) ; +extern void BotCheckVoiceChatResponse ( bot_state_t * bs ) ; +extern void BotRecordVoiceChat ( int client , int destclient , const char * id , int mode , qboolean noResponse ) ; +extern qboolean BotClass_LtCheckGiveAmmo ( bot_state_t * bs , int maxTravelTime , bot_goal_t * goal ) ; +extern qboolean G_RequestedAmmo ( bot_state_t * bs , int client , qboolean clear ) ; +extern qboolean BotClass_MedicCheckGiveHealth ( bot_state_t * bs , int maxTravelTime , bot_goal_t * goal ) ; +extern qboolean G_RequestedHealth ( bot_state_t * bs , int client , qboolean clearRequest ) ; +extern qboolean BotClass_MedicCheckRevives ( bot_state_t * bs , int maxtravel , bot_goal_t * goal , qboolean lookForBots ) ; +extern qboolean BotClass_CovertOpsCheckDisguises ( bot_state_t * bs , int maxTravel , bot_goal_t * goal ) ; +extern void BotTeamOrders ( bot_state_t * bs ) ; +extern void BotCTFOrders_BothFlagsAtBase ( bot_state_t * bs ) ; +extern void BotCTFOrders_EnemyFlagNotAtBase ( bot_state_t * bs ) ; +extern void BotCTFOrders_FlagNotAtBase ( bot_state_t * bs ) ; +extern void BotCTFOrders_BothFlagsNotAtBase ( bot_state_t * bs ) ; +extern int BotSuggestWeapon ( bot_state_t * bs , team_t team ) ; +extern int BotSuggestClass ( bot_state_t * bs , team_t team ) ; +extern qboolean BotCheckNeedEngineer ( bot_state_t * bs , team_t team ) ; +extern int BotNumTeamClasses ( team_t team , int mpClass , int ignore ) ; +extern int BotNumTeamMembers ( int team ) ; +extern int BotGetConstructibles ( team_t team , int * list , int listSize , qboolean ignoreBuilt ) ; +extern int BotGetTargetDynamite ( int * list , int listSize , gentity_t * target ) ; +extern int GetTargetExplosives ( team_t team , qboolean ignoreDynamite ) ; +extern int BotGetTargetExplosives ( team_t team , int * list , int listSize , qboolean ignoreDynamite ) ; +extern int BotGetTargetsForSatchelCharge ( team_t team , int * list , int listSize , qboolean ignoreSatchelCharge ) ; +extern gentity_t * G_FindSatchelChargeTargetForTeam ( gentity_t * trav , team_t team ) ; +extern gentity_t * G_FindDynamiteTargetForTeam ( gentity_t * trav , team_t team ) ; +extern gentity_t * G_IsConstructible ( team_t team , gentity_t * toi ) ; +extern gentity_t * G_ConstructionForTeam ( gentity_t * toi , team_t team ) ; +extern qboolean G_ConstructionIsDestroyable ( gentity_t * ent ) ; +extern qboolean G_ConstructionIsPartlyBuilt ( gentity_t * ent ) ; +extern qboolean G_ConstructionIsFullyBuilt ( gentity_t * ent ) ; +extern qboolean G_ConstructionBegun ( gentity_t * ent ) ; +extern gentity_t * G_FindSatchels ( gentity_t * start ) ; +extern gentity_t * G_FindLandmine ( gentity_t * start ) ; +extern gentity_t * G_FindSmokeBomb ( gentity_t * start ) ; +extern gentity_t * G_FindDynamite ( gentity_t * start ) ; +extern gentity_t * G_FindMissile ( gentity_t * start , weapon_t weap ) ; +extern int BotFindSparseDefendArea ( bot_state_t * bs , bot_goal_t * goal , qboolean force ) ; +extern qboolean BotFindDroppedFlag ( gentity_t * * returnEnt ) ; +extern void BotSayTeamOrder ( bot_state_t * bs , int toclient ) ; +extern int BotSortTeamMatesByBaseTravelTime ( bot_state_t * bs , int * teammates , int maxteammates ) ; +extern int BotClientTravelTimeToGoal ( int client , bot_goal_t * goal ) ; +extern int BotFlagAtBase ( int team , gentity_t * * returnEnt ) ; +extern int BotGetTeamFlagCarrier ( bot_state_t * bs ) ; +extern gentity_t * BotGetEnemyFlagCarrier ( bot_state_t * bs ) ; +extern float * BotSortPlayersByTraveltime ( int areanum , int * list , int numList ) ; +extern float * BotSortPlayersByDistance ( vec3_t target , int * list , int numList ) ; +extern int BotNumTeamMatesWithTargetAndCloser ( bot_state_t * bs , int targetEntity , int targetArea , int * list , int maxList , int playerType ) ; +extern int BotNumTeamMatesWithTargetByClass ( bot_state_t * bs , int targetEntity , int * list , int maxList , int playerType ) ; +extern int BotNumTeamMatesWithTarget ( bot_state_t * bs , int targetEntity , int * list , int maxList ) ; +extern int BotNumTeamMates ( bot_state_t * bs , int * list , int maxList ) ; +extern int BotValidTeamLeader ( bot_state_t * bs ) ; +extern qboolean Bot_ScriptAction_SetCivilian ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetAmmoAmount ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetScriptAutonomy ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_FireAtTarget ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Announce ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetActiveWeapon ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetSpeedCoefficient ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetCloseHearingRange ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetFarSeeingRange ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetVisionRange ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetFireCycleTime ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetFireRate ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_BotDebugging ( gentity_t * ent , char * params ) ; +extern qboolean Bot_ScriptAction_PrintGlobalAccum ( gentity_t * ent , char * params ) ; +extern qboolean Bot_ScriptAction_PrintAccum ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetProne ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetCrouch ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetHearingRange ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetFieldOfView ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_ResetScript ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_NoTarget ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_MovementAutonomy ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetMovementAutonomy ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Cvar ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_FollowLeader ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_GlobalAccum ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetClass ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetWeapon ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_PlaySoundAtPlayer ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_MountMG42 ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetAttribute ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_AbortIfWarmup ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Logging ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Trigger ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_MoveToMarker ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Wait ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Accum ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SpawnBot ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_SetAccumToPlayerCount ( bot_state_t * bs , char * params ) ; +extern qboolean Bot_ScriptAction_Print ( bot_state_t * bs , char * params ) ; +extern void Bot_ScriptError ( bot_state_t * bs , char * fmt , ... ) ; +extern qboolean Bot_ScriptAction_SetSpeed ( bot_state_t * bs , char * params ) ; +extern void Bot_ScriptThink ( void ) ; +extern void Bot_ScriptLog_Entry ( bot_state_t * bs , qboolean showDetails , char * preText , char * fmt , ... ) ; +extern int Bot_Script_GetCurrentLine ( bot_state_t * bs ) ; +extern qboolean Bot_ScriptRun ( bot_state_t * bs , qboolean force ) ; +extern void Bot_TeamScriptEvent ( int team , char * eventStr , char * params ) ; +extern void Bot_ForceScriptEvent ( int entityNum , char * eventStr , char * params ) ; +extern void Bot_ScriptEvent ( int entityNum , char * eventStr , char * params ) ; +extern void Bot_ScriptChange ( bot_state_t * bs , int newScriptNum ) ; +extern char * Bot_LineText ( char * text ) ; +extern qboolean Bot_ScriptInitBot ( int entnum ) ; +extern void Bot_ScriptParse ( bot_script_data_t * bsd , char * * text ) ; +extern void Bot_ScriptParseAllCharacters ( ) ; +extern void Bot_ScriptLoad ( void ) ; +extern bot_script_stack_action_t * Bot_ActionForString ( char * string ) ; +extern int Bot_EventForString ( char * string ) ; +extern qboolean Bot_EventMatch_IntInRange ( bot_script_event_t * event , char * eventParm ) ; +extern qboolean Bot_EventMatch_StringEqual ( bot_script_event_t * event , char * eventParm ) ; +extern int Bot_FindSriptGlobalData ( bot_script_data_t * data ) ; +extern void BotDebugViewClient ( int client ) ; +extern void BotInitMovementAutonomyPos ( gentity_t * bot ) ; +extern void BotSetIdealViewAngles ( int clientNum , vec3_t angle ) ; +extern void GetBotAmmo ( int clientNum , int * weapon , int * ammo , int * ammoclip ) ; +extern void GetBotAutonomies ( int clientNum , int * weapAutonomy , int * moveAutonomy ) ; +extern void BotDebug ( int clientNum ) ; +extern void BotRecordAttack ( int src , int dest ) ; +extern void BotRecordTeamDeath ( int client ) ; +extern void G_SetAASBlockingEntity ( gentity_t * ent , int blocking ) ; +extern qboolean BotCoop ( ) ; +extern qboolean BotSinglePlayer ( ) ; +extern gentity_t * BotFindEntityForName ( char * name ) ; +extern gentity_t * BotFindNextStaticEntity ( gentity_t * start , botStaticEntityEnum_t entityEnum ) ; +extern void BotBuildStaticEntityCache ( void ) ; +extern gentity_t * BotGetEntity ( int entityNum ) ; +extern bot_state_t * FindBotByName ( char * botName ) ; +extern gentity_t * BotFindEntity ( gentity_t * from , int fieldofs , char * match ) ; +extern int BotAIShutdown ( int restart ) ; +extern int BotAISetup ( int restart ) ; +extern int BotInitLibrary ( void ) ; +extern int BotAIStartFrame ( int time ) ; +extern int BotAIThinkFrame ( int time ) ; +extern void BotPreProcessAI ( ) ; +extern int BotAILoadMap ( int restart ) ; +extern void BotResetState ( bot_state_t * bs ) ; +extern int BotAIShutdownClient ( int client ) ; +extern int BotAISetupClient ( int client , struct bot_settings_s * settings ) ; +extern void BotSetUpCharacter ( bot_state_t * bs ) ; +extern void BotSetCharacterAttributes ( bot_state_t * bs , BotDefaultAttributes_t * defaults ) ; +extern void ParseBotDefaultAttributes ( char * fileName ) ; +extern void BotGetInitialAttributes ( bot_state_t * bs ) ; +extern void BotScheduleBotThink ( void ) ; +extern int BotAI ( int client , float thinktime ) ; +extern int BotTravelFlagsForClient ( int client ) ; +extern void BotAIRegularUpdate ( void ) ; +extern void BotUpdateInput ( bot_state_t * bs , int time ) ; +extern void BotInputToUserCommand ( bot_state_t * bs , bot_input_t * bi , usercmd_t * ucmd , int delta_angles [ 3 ] , int time ) ; +extern void BotSpeedBonus ( int clientNum ) ; +extern void BotChangeViewAngles ( bot_state_t * bs , float thinktime ) ; +extern float BotChangeViewAngle ( float angle , float ideal_angle , float speed ) ; +extern float AngleDifference ( float ang1 , float ang2 ) ; +extern void BotAI_SetNumBots ( int numbots ) ; +extern int BotAI_GetNumBots ( void ) ; +extern void BotEntityInfo ( int entnum , aas_entityinfo_t * info ) ; +extern void BotInterbreeding ( void ) ; +extern void QDECL BotAI_BotInitialChat ( bot_state_t * bs , char * type , ... ) ; +extern int BotAI_GetSnapshotEntity ( int clientNum , int sequence , entityState_t * state ) ; +extern int BotAI_GetEntityState ( int entityNum , entityState_t * state ) ; +extern int BotAI_GetClientState ( int clientNum , playerState_t * state ) ; +extern void BotAI_Trace ( bsp_trace_t * bsptrace , vec3_t start , vec3_t mins , vec3_t maxs , vec3_t end , int passent , int contentmask ) ; +extern void QDECL BotAI_Print ( int type , char * fmt , ... ) ; +extern qboolean BotEntityWithinView ( bot_state_t * bs , int viewEnt ) ; +extern void BotPickupWeapon ( int client , int weaponnum , qboolean alreadyHave ) ; +extern void BotCountLandMines ( void ) ; +extern gentity_t * BotGetVisibleDamagableScriptMover ( bot_state_t * bs ) ; +extern int BotBestTargetWeapon ( bot_state_t * bs , int targetNum ) ; +extern void BotSetPOW ( int entityNum , qboolean isPOW ) ; +extern void BotUpdateViewAngles ( bot_state_t * bs , bot_goal_t * goal , bot_moveresult_t moveresult ) ; +extern void BotFindAndAttackEnemy ( bot_state_t * bs ) ; +extern qboolean EnemyIsCloseEnoughToFight ( bot_state_t * bs ) ; +extern float BotHealthScale ( int entnum ) ; +extern int BotCanSnipe ( bot_state_t * bs , qboolean checkAmmo ) ; +extern qboolean BotIsConstructible ( team_t team , int toiNum ) ; +extern qboolean BotGetReachableEntityArea ( bot_state_t * bs , int entityNum , bot_goal_t * goal ) ; +extern qboolean BotEntityTargetClassnameMatch ( int entityNum , const char * classname ) ; +extern qboolean BotDirectMoveToGoal ( bot_state_t * bs , bot_goal_t * goal , bot_moveresult_t * moveresult ) ; +extern qboolean BotBattleNewNode ( bot_state_t * bs ) ; +extern int BotTravelTimeToEntity ( bot_state_t * bs , int entnum ) ; +extern int BotReduceListByTravelTime ( int * list , int numList , vec3_t destpos , int destarea , int traveltime ) ; +extern int BotGetArea ( int entnum ) ; +extern float * BotGetOrigin ( int entnum ) ; +extern float * BotGetEye ( int entnum ) ; +extern qboolean BotSeekCover ( bot_state_t * bs ) ; +extern int BotLastHurt ( bot_state_t * bs ) ; +extern int BotLastAttacked ( bot_state_t * bs ) ; +extern qboolean BotEnemyCarryingFlag ( int entnum ) ; +extern void BotShareLastAttacked ( bot_state_t * bs ) ; +extern void BotEnemyFire ( bot_state_t * bs ) ; +extern void BotCheckVoiceChats ( bot_state_t * bs ) ; +extern void BotDefaultNode ( bot_state_t * bs ) ; +extern qboolean BotFindSpecialGoals ( bot_state_t * bs ) ; +extern qboolean BotCheckEmergencyTargets ( bot_state_t * bs ) ; +extern int BotScriptAutonomyForString ( char * string ) ; +extern char * BotStringForWeaponAutonomy ( int value ) ; +extern int BotWeaponAutonomyForString ( char * string ) ; +extern char * BotStringForMovementAutonomy ( int value ) ; +extern int BotMovementAutonomyForString ( char * string ) ; +extern void BotCalculateMg42Spots ( void ) ; +extern void BotDropFlag ( bot_state_t * bs ) ; +extern void BotClearGoal ( bot_goal_t * goal ) ; +extern int BotGetRandomVisibleSniperSpot ( bot_state_t * bs ) ; +extern int BotGetNumVisibleSniperSpots ( bot_state_t * bs ) ; +extern int BotBestMG42Spot ( bot_state_t * bs , qboolean force ) ; +extern int BotBestLandmineSpotingSpot ( bot_state_t * bs ) ; +extern int BotBestSniperSpot ( bot_state_t * bs ) ; +extern void BotGetAimAccuracySkill ( bot_state_t * bs , float * outAimAccuracy , float * outAimSkill ) ; +extern void BotRecordDeath ( int client , int enemy ) ; +extern void BotRecordPain ( int client , int enemy , int mod ) ; +extern void BotRecordKill ( int client , int enemy ) ; +extern void BotRecordTeamChange ( int client ) ; +extern void BotMoveToIntermission ( int client ) ; +extern void BotSetBlockEnt ( int client , int blocker ) ; +extern void BotShutdownDeathmatchAI ( void ) ; +extern void BotSetupDeathmatchAI ( void ) ; +extern void BotDeathmatchAI ( bot_state_t * bs , float thinktime ) ; +extern void BotPowThink ( bot_state_t * bs ) ; +extern void BotDeathmatchAIFirstCalled ( bot_state_t * bs ) ; +extern void BotDumpNodeSwitches ( bot_state_t * bs ) ; +extern void BotResetNodeSwitches ( void ) ; +extern void BotUpdateReconInfo ( bot_state_t * bs ) ; +extern void BotCheckAlert ( bot_state_t * bs ) ; +extern void BotCheckAir ( bot_state_t * bs ) ; +extern void BotCheckSnapshot ( bot_state_t * bs ) ; +extern void BotCheckEvents ( bot_state_t * bs , entityState_t * state ) ; +extern void BotAIBlocked ( bot_state_t * bs , bot_moveresult_t * moveresult , int activate ) ; +extern void BotModelMinsMaxs ( int modelindex , vec3_t mins , vec3_t maxs ) ; +extern void BotSetMovedir ( vec3_t angles , vec3_t movedir ) ; +extern int BotEntityToActivate ( int entitynum ) ; +extern void BotMapScripts ( bot_state_t * bs ) ; +extern qboolean BotCheckAttack ( bot_state_t * bs ) ; +extern qboolean BotScopedWeapon ( int weapon ) ; +extern qboolean BotThrottleWeapon ( int weapon ) ; +extern qboolean BotMoveWhileFiring ( int weapon ) ; +extern void BotAimAtEnemySP ( bot_state_t * bs ) ; +extern void BotAimAtEnemy ( bot_state_t * bs ) ; +extern float swayrand ( float x , float y ) ; +extern float BotGetReactionTime ( bot_state_t * bs ) ; +extern int BotFindEnemyMP ( bot_state_t * bs , int curenemy , qboolean ignoreViewRestrictions ) ; +extern void BotSortClientsByDistance ( vec3_t srcpos , int * sorted , qboolean hasPanzer ) ; +extern qboolean BotHasWeaponWithRange ( bot_state_t * bs , float dist ) ; +extern float BotWeaponRange ( bot_state_t * bs , int weaponnum ) ; +extern void BotFindEnemies ( bot_state_t * bs , int * dangerSpots , int * dangerSpotCount ) ; +extern qboolean BotIsValidTarget ( bot_state_t * bs , int target , int curenemy ) ; +extern float BotGetEntitySurfaceSoundCoefficient ( int clientNum ) ; +extern void BotUpdateAlertStateSquadSensingInfo ( bot_state_t * bs , qboolean canSeeTarget , qboolean heardFootSteps , qboolean heardShooting ) ; +extern qboolean ChangeBotAlertState ( bot_state_t * bs , aistateEnum_t newAlertState , qboolean force ) ; +extern float BotNoLeaderPenalty ( bot_state_t * bs ) ; +extern qboolean BotDangerousGoal ( bot_state_t * bs , bot_goal_t * goal ) ; +extern void BotIgnoreGoal ( bot_state_t * bs , bot_goal_t * goal , int duration ) ; +extern qboolean BotCheckMovementAutonomy ( bot_state_t * bs , bot_goal_t * goal ) ; +extern void sAdjustPointTowardsPlayer ( vec3_t playerLoc , vec3_t endPos , qboolean shouldLoop , vec3_t outPos ) ; +extern float sAngleBetweenVectors ( vec3_t a , vec3_t b ) ; +extern void botindicator_think ( gentity_t * ent ) ; +extern qboolean BotGoalForEntity ( bot_state_t * bs , int entityNum , bot_goal_t * goal , int urgency ) ; +extern qboolean BotGoalWithinMovementAutonomy ( bot_state_t * bs , bot_goal_t * goal , int urgency ) ; +extern qboolean BotPointWithinRawMovementAutonomy ( bot_state_t * bs , vec3_t point ) ; +extern qboolean BotPointWithinMovementAutonomy ( bot_state_t * bs , bot_goal_t * goal , vec3_t point ) ; +extern qboolean BotWithinLeaderFollowDist ( bot_state_t * bs ) ; +extern float BotGetFollowAutonomyDist ( bot_state_t * bs ) ; +extern float BotGetMovementAutonomyRange ( bot_state_t * bs , bot_goal_t * goal ) ; +extern float BotGetRawMovementAutonomyRange ( bot_state_t * bs ) ; +extern qboolean BotGetMovementAutonomyPos ( bot_state_t * bs , vec3_t pos ) ; +extern int BotGetMovementAutonomyLevel ( bot_state_t * bs ) ; +extern qboolean BotCheckAttackAtPos ( int entnum , int enemy , vec3_t pos , qboolean ducking , qboolean allowHitWorld ) ; +extern qboolean BotVisibleFromPos ( vec3_t srcorigin , int srcnum , vec3_t destorigin , int destent , qboolean dummy ) ; +extern float BotEntityVisible ( int viewer , vec3_t eye , vec3_t viewangles , float fov , int ent , vec3_t entorigin ) ; +extern qboolean BotEntInvisibleBySmokeBomb ( vec3_t start , vec3_t end ) ; +extern qboolean InFieldOfVision ( vec3_t viewangles , float fov , vec3_t angles ) ; +extern int BotSameTeam ( bot_state_t * bs , int entnum ) ; +extern bot_moveresult_t BotAttackMove ( bot_state_t * bs , int tfl ) ; +extern void BotRoamGoal ( bot_state_t * bs , vec3_t goal ) ; +extern void BotGoForPowerups ( bot_state_t * bs ) ; +extern void BotDontAvoid ( bot_state_t * bs , char * itemname ) ; +extern void BotGoCamp ( bot_state_t * bs , bot_goal_t * goal ) ; +extern int BotCanAndWantsToRocketJump ( bot_state_t * bs ) ; +extern int BotWantsToHelp ( bot_state_t * bs ) ; +extern int BotWantsToChase ( bot_state_t * bs ) ; +extern int BotWantsToRetreat ( bot_state_t * bs ) ; +extern float BotAggression ( bot_state_t * bs ) ; +extern int TeamPlayIsOn ( void ) ; +extern void BotInitWaypoints ( void ) ; +extern void BotFreeWaypoints ( bot_waypoint_t * wp ) ; +extern bot_waypoint_t * BotFindWayPoint ( bot_waypoint_t * waypoints , char * name ) ; +extern bot_waypoint_t * BotCreateWayPoint ( char * name , vec3_t origin , int areanum ) ; +extern qboolean EntityHasQuad ( aas_entityinfo_t * entinfo ) ; +extern qboolean EntityIsChatting ( aas_entityinfo_t * entinfo ) ; +extern qboolean EntityIsShooting ( aas_entityinfo_t * entinfo ) ; +extern qboolean EntityIsInvisible ( aas_entityinfo_t * entinfo ) ; +extern qboolean EntityInLimbo ( aas_entityinfo_t * entinfo ) ; +extern qboolean EntityIsDead ( aas_entityinfo_t * entinfo ) ; +extern qboolean BotInSlime ( bot_state_t * bs ) ; +extern qboolean BotInLava ( bot_state_t * bs ) ; +extern qboolean BotIntermission ( bot_state_t * bs ) ; +extern qboolean BotIsObserver ( bot_state_t * bs ) ; +extern qboolean BotIsPOW ( bot_state_t * bs ) ; +extern qboolean BotIsDead ( bot_state_t * bs ) ; +extern void BotSetTeleportTime ( bot_state_t * bs ) ; +extern void BotBattleUseItems ( bot_state_t * bs ) ; +extern void BotUpdateBattleInventory ( bot_state_t * bs , int enemy ) ; +extern void BotUpdateInventory ( bot_state_t * bs ) ; +extern void BotSetupForMovement ( bot_state_t * bs ) ; +extern void BotCycleWeapon ( bot_state_t * bs ) ; +extern void BotChooseWeapon ( bot_state_t * bs ) ; +extern int BotBestFightWeapon ( bot_state_t * bs ) ; +extern float BotWeaponWantScale ( bot_state_t * bs , weapon_t weapon ) ; +extern int BotTeamMatesNearEnemy ( bot_state_t * bs ) ; +extern float BotWeaponClosestDist ( int weaponnum ) ; +extern qboolean BotWeaponOnlyUseIfInInRange ( int weaponnum ) ; +extern qboolean BotWeaponCharged ( bot_state_t * bs , int weapon ) ; +extern qboolean G_WeaponCharged ( playerState_t * ps , team_t team , int weapon , int * skill ) ; +extern qboolean BotGotEnoughAmmoForWeapon ( bot_state_t * bs , int weapon ) ; +extern char * EasyClientName ( int client , char * buf , int size ) ; +extern char * stristr ( char * str , char * charset ) ; +extern int ClientFromName ( char * name ) ; +extern char * ClientSkin ( int client , char * skin , int size ) ; +extern char * ClientName ( int client , char * name , int size ) ; +extern qboolean BotFindNearbyGoal ( bot_state_t * bs ) ; +extern qboolean BotFindNearbyTriggerGoal ( bot_state_t * bs ) ; +extern int BotReachableBBoxAreaNum ( bot_state_t * bs , vec3_t absmin , vec3_t absmax ) ; +extern int BotPointAreaNum ( int entnum , vec3_t origin ) ; +extern int BotFirstLadderArea ( int entnum , int * areas , int numareas ) ; +extern int BotFirstReachabilityArea ( int entnum , vec3_t origin , int * areas , int numareas , qboolean distCheck ) ; +extern qboolean BotCarryingFlag ( int client ) ; +extern int AINode_MP_NavigateFromVoid ( bot_state_t * bs ) ; +extern void AIEnter_MP_NavigateFromVoid ( bot_state_t * bs ) ; +extern int AINode_MP_MoveToAutonomyRange ( bot_state_t * bs ) ; +extern void AIEnter_MP_MoveToAutonomyRange ( bot_state_t * bs ) ; +extern int AINode_MP_Script_MoveToMarker ( bot_state_t * bs ) ; +extern void AIEnter_MP_Script_MoveToMarker ( bot_state_t * bs ) ; +extern int AINode_MP_Battle_Retreat ( bot_state_t * bs ) ; +extern void AIEnter_MP_Battle_Retreat ( bot_state_t * bs ) ; +extern int AINode_MP_Battle_Chase ( bot_state_t * bs ) ; +extern void AIEnter_MP_Battle_Chase ( bot_state_t * bs ) ; +extern int AINode_MP_Battle_Fight ( bot_state_t * bs ) ; +extern void AIEnter_MP_Battle_Fight ( bot_state_t * bs ) ; +extern int AINode_MP_DisarmDynamite ( bot_state_t * bs ) ; +extern void AIEnter_MP_DisarmDynamite ( bot_state_t * bs ) ; +extern int AINode_MP_PlantMine ( bot_state_t * bs ) ; +extern void AIEnter_MP_PlantMine ( bot_state_t * bs ) ; +extern int AINode_MP_ConstructibleTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_ConstructibleTarget ( bot_state_t * bs ) ; +extern int AINode_MP_DynamiteTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_DynamiteTarget ( bot_state_t * bs ) ; +extern int AINode_MP_SatchelChargeTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_SatchelChargeTarget ( bot_state_t * bs ) ; +extern int AINode_MP_TouchTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_TouchTarget ( bot_state_t * bs ) ; +extern int AINode_MP_DefendTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_DefendTarget ( bot_state_t * bs ) ; +extern int AINode_MP_SniperSpot ( bot_state_t * bs ) ; +extern void AIEnter_MP_SniperSpot ( bot_state_t * bs ) ; +extern int AINode_MP_ScanForLandmines ( bot_state_t * bs ) ; +extern void AIEnter_MP_ScanForLandmines ( bot_state_t * bs ) ; +extern int AINode_MP_MG42Mount ( bot_state_t * bs ) ; +extern void AIEnter_MP_MG42Mount ( bot_state_t * bs ) ; +extern int AINode_MP_MG42Scan ( bot_state_t * bs ) ; +extern void AIEnter_MP_MG42Scan ( bot_state_t * bs ) ; +extern int AINode_MP_Battle_MobileMG42 ( bot_state_t * bs ) ; +extern void AIEnter_MP_Battle_MobileMG42 ( bot_state_t * bs ) ; +extern int AINode_MP_FixMG42 ( bot_state_t * bs ) ; +extern void AIEnter_MP_FixMG42 ( bot_state_t * bs ) ; +extern int AINode_MP_AttackTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_AttackTarget ( bot_state_t * bs ) ; +extern int AINode_MP_PanzerTarget ( bot_state_t * bs ) ; +extern void AIEnter_MP_PanzerTarget ( bot_state_t * bs ) ; +extern int AINode_MP_MedicRevive ( bot_state_t * bs ) ; +extern void AIEnter_MP_MedicRevive ( bot_state_t * bs ) ; +extern int AINode_MP_MedicGiveHealth ( bot_state_t * bs ) ; +extern void AIEnter_MP_MedicGiveHealth ( bot_state_t * bs ) ; +extern int AINode_MP_GiveAmmo ( bot_state_t * bs ) ; +extern void AIEnter_MP_GiveAmmo ( bot_state_t * bs ) ; +extern int AINode_MP_AvoidDanger ( bot_state_t * bs ) ; +extern void AIEnter_MP_AvoidDanger ( bot_state_t * bs ) ; +extern int AINode_MP_Seek_NBG ( bot_state_t * bs ) ; +extern void AIEnter_MP_Seek_NBG ( bot_state_t * bs ) ; +extern int AINode_MP_Seek_ActivateEntity ( bot_state_t * bs ) ; +extern void AIEnter_MP_Seek_ActivateEntity ( bot_state_t * bs ) ; +extern int AINode_MP_Respawn ( bot_state_t * bs ) ; +extern void AIEnter_MP_Respawn ( bot_state_t * bs ) ; +extern int AINode_MP_Stand ( bot_state_t * bs ) ; +extern void AIEnter_MP_Stand ( bot_state_t * bs ) ; +extern int AINode_MP_Observer ( bot_state_t * bs ) ; +extern void AIEnter_MP_Observer ( bot_state_t * bs ) ; +extern int AINode_MP_Intermission ( bot_state_t * bs ) ; +extern void AIEnter_MP_Intermission ( bot_state_t * bs ) ; +extern void BotMP_MoveToGoal ( bot_state_t * bs , bot_moveresult_t * result , int movestate , bot_goal_t * goal , int travelflags ) ; +extern qboolean BotMP_FindGoal ( bot_state_t * bs ) ; +extern qboolean BotMP_FindGoal_New ( bot_state_t * bs ) ; +extern qboolean BotMP_AlreadyDoing_FastOut ( bot_state_t * bs , botgoalFind_t * bg ) ; +extern void BotMP_FindGoal_PostProcessGoal ( bot_state_t * bs , botgoalFind_t * bg , bot_goal_t * goal ) ; +extern botMPpg_t BotMP_FindGoal_ProcessGoal ( bot_state_t * bs , botgoalFind_t * bg , bot_goal_t * target_goal ) ; +extern int BotMP_FindGoal_ClassForGoalType ( botgoalFindType_t type ) ; +extern int QDECL BotMP_FindGoals_Sort_CovertOps ( const void * a , const void * b ) ; +extern int QDECL BotMP_FindGoals_Sort_Engineer ( const void * a , const void * b ) ; +extern int QDECL BotMP_FindGoals_Sort_Standard ( const void * a , const void * b ) ; +extern int BotMP_FindGoal_BuildGoalList ( bot_state_t * bs , botgoalFind_t * pGoals , int maxGoals ) ; +extern qboolean BotMP_CheckEmergencyGoals ( bot_state_t * bs ) ; +extern qboolean BotMP_CheckClassActions ( bot_state_t * bs ) ; +extern int BotMatchMessage ( bot_state_t * bs , char * message ) ; +extern void BotMatch_Kill ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_LeadTheWay ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_WhereAreYou ( bot_state_t * bs , bot_match_t * match ) ; +extern float BotNearestVisibleItem ( bot_state_t * bs , char * itemname , bot_goal_t * goal ) ; +extern void BotMatch_WhatIsMyCommand ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_WhatAreYouDoing ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_WhoIsTeamLeader ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_StopTeamLeaderShip ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_StartTeamLeaderShip ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_Dismiss ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_FormationSpace ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_CheckPoint ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_WhichTeam ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_LeaveSubteam ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_JoinSubteam ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_ReturnFlag ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_RushBase ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_GetFlag ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_Patrol ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_Camp ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_GetItem ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_DefendKeyArea ( bot_state_t * bs , bot_match_t * match ) ; +extern void BotMatch_HelpAccompany ( bot_state_t * bs , bot_match_t * match ) ; +extern int BotGPSToPosition ( char * buf , vec3_t position ) ; +extern int BotAddressedToBot ( bot_state_t * bs , bot_match_t * match ) ; +extern int BotGetPatrolWaypoints ( bot_state_t * bs , bot_match_t * match ) ; +extern int NumPlayersOnSameTeam ( bot_state_t * bs ) ; +extern int FindEnemyByName ( bot_state_t * bs , char * name ) ; +extern int FindClientByName ( char * name ) ; +extern float BotGetTime ( bot_match_t * match ) ; +extern int BotGetMessageTeamGoal ( bot_state_t * bs , char * goalname , bot_goal_t * goal ) ; +extern int BotGetItemTeamGoal ( char * goalname , bot_goal_t * goal ) ; diff --git a/src/botai/ai_funcs.h b/src/botai/ai_funcs.h new file mode 100644 index 0000000..0b1e6cc --- /dev/null +++ b/src/botai/ai_funcs.h @@ -0,0 +1,475 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +{"BotObjectiveFunc", (byte *)BotObjectiveFunc}, +{"BotBehaviourFunc", (byte *)BotBehaviourFunc}, +{"BotEngagementFunc", (byte *)BotEngagementFunc}, +{"BotSetLeaderTagEnt", (byte *)BotSetLeaderTagEnt}, +{"EntGetNumBotFollowers", (byte *)EntGetNumBotFollowers}, +{"BotGetLeader", (byte *)BotGetLeader}, +{"G_RequestedFollow", (byte *)G_RequestedFollow}, +{"BotSpawnSpecialEntities", (byte *)BotSpawnSpecialEntities}, +{"BotVoiceChatAfterTeamIdleTime", (byte *)BotVoiceChatAfterTeamIdleTime}, +{"AI_Team_Init_All_Teams", (byte *)AI_Team_Init_All_Teams}, +{"AI_Team_Init", (byte *)AI_Team_Init}, +{"BotVoiceChatAfterIdleTime", (byte *)BotVoiceChatAfterIdleTime}, +{"BotSendVoiceChat", (byte *)BotSendVoiceChat}, +{"BotDelayedVoiceChat", (byte *)BotDelayedVoiceChat}, +{"BotCheckVoiceChatResponse", (byte *)BotCheckVoiceChatResponse}, +{"BotRecordVoiceChat", (byte *)BotRecordVoiceChat}, +{"BotClass_LtCheckGiveAmmo", (byte *)BotClass_LtCheckGiveAmmo}, +{"G_RequestedAmmo", (byte *)G_RequestedAmmo}, +{"BotClass_MedicCheckGiveHealth", (byte *)BotClass_MedicCheckGiveHealth}, +{"G_RequestedHealth", (byte *)G_RequestedHealth}, +{"BotClass_MedicCheckRevives", (byte *)BotClass_MedicCheckRevives}, +{"BotClass_CovertOpsCheckDisguises", (byte *)BotClass_CovertOpsCheckDisguises}, +{"BotTeamOrders", (byte *)BotTeamOrders}, +{"BotCTFOrders_BothFlagsAtBase", (byte *)BotCTFOrders_BothFlagsAtBase}, +{"BotCTFOrders_EnemyFlagNotAtBase", (byte *)BotCTFOrders_EnemyFlagNotAtBase}, +{"BotCTFOrders_FlagNotAtBase", (byte *)BotCTFOrders_FlagNotAtBase}, +{"BotCTFOrders_BothFlagsNotAtBase", (byte *)BotCTFOrders_BothFlagsNotAtBase}, +{"BotSuggestWeapon", (byte *)BotSuggestWeapon}, +{"BotSuggestClass", (byte *)BotSuggestClass}, +{"BotCheckNeedEngineer", (byte *)BotCheckNeedEngineer}, +{"BotNumTeamClasses", (byte *)BotNumTeamClasses}, +{"BotNumTeamMembers", (byte *)BotNumTeamMembers}, +{"BotGetConstructibles", (byte *)BotGetConstructibles}, +{"BotGetTargetDynamite", (byte *)BotGetTargetDynamite}, +{"GetTargetExplosives", (byte *)GetTargetExplosives}, +{"BotGetTargetExplosives", (byte *)BotGetTargetExplosives}, +{"BotGetTargetsForSatchelCharge", (byte *)BotGetTargetsForSatchelCharge}, +{"G_FindSatchelChargeTargetForTeam", (byte *)G_FindSatchelChargeTargetForTeam}, +{"G_FindDynamiteTargetForTeam", (byte *)G_FindDynamiteTargetForTeam}, +{"G_IsConstructible", (byte *)G_IsConstructible}, +{"G_ConstructionForTeam", (byte *)G_ConstructionForTeam}, +{"G_ConstructionIsDestroyable", (byte *)G_ConstructionIsDestroyable}, +{"G_ConstructionIsPartlyBuilt", (byte *)G_ConstructionIsPartlyBuilt}, +{"G_ConstructionIsFullyBuilt", (byte *)G_ConstructionIsFullyBuilt}, +{"G_ConstructionBegun", (byte *)G_ConstructionBegun}, +{"G_FindSatchels", (byte *)G_FindSatchels}, +{"G_FindLandmine", (byte *)G_FindLandmine}, +{"G_FindSmokeBomb", (byte *)G_FindSmokeBomb}, +{"G_FindDynamite", (byte *)G_FindDynamite}, +{"G_FindMissile", (byte *)G_FindMissile}, +{"BotFindSparseDefendArea", (byte *)BotFindSparseDefendArea}, +{"BotFindDroppedFlag", (byte *)BotFindDroppedFlag}, +{"BotSayTeamOrder", (byte *)BotSayTeamOrder}, +{"BotSortTeamMatesByBaseTravelTime", (byte *)BotSortTeamMatesByBaseTravelTime}, +{"BotClientTravelTimeToGoal", (byte *)BotClientTravelTimeToGoal}, +{"BotFlagAtBase", (byte *)BotFlagAtBase}, +{"BotGetTeamFlagCarrier", (byte *)BotGetTeamFlagCarrier}, +{"BotGetEnemyFlagCarrier", (byte *)BotGetEnemyFlagCarrier}, +{"BotSortPlayersByTraveltime", (byte *)BotSortPlayersByTraveltime}, +{"BotSortPlayersByDistance", (byte *)BotSortPlayersByDistance}, +{"BotNumTeamMatesWithTargetAndCloser", (byte *)BotNumTeamMatesWithTargetAndCloser}, +{"BotNumTeamMatesWithTargetByClass", (byte *)BotNumTeamMatesWithTargetByClass}, +{"BotNumTeamMatesWithTarget", (byte *)BotNumTeamMatesWithTarget}, +{"BotNumTeamMates", (byte *)BotNumTeamMates}, +{"BotValidTeamLeader", (byte *)BotValidTeamLeader}, +{"Bot_ScriptAction_SetCivilian", (byte *)Bot_ScriptAction_SetCivilian}, +{"Bot_ScriptAction_SetAmmoAmount", (byte *)Bot_ScriptAction_SetAmmoAmount}, +{"Bot_ScriptAction_SetScriptAutonomy", (byte *)Bot_ScriptAction_SetScriptAutonomy}, +{"Bot_ScriptAction_FireAtTarget", (byte *)Bot_ScriptAction_FireAtTarget}, +{"Bot_ScriptAction_Announce", (byte *)Bot_ScriptAction_Announce}, +{"Bot_ScriptAction_SetActiveWeapon", (byte *)Bot_ScriptAction_SetActiveWeapon}, +{"Bot_ScriptAction_SetSpeedCoefficient", (byte *)Bot_ScriptAction_SetSpeedCoefficient}, +{"Bot_ScriptAction_SetCloseHearingRange", (byte *)Bot_ScriptAction_SetCloseHearingRange}, +{"Bot_ScriptAction_SetFarSeeingRange", (byte *)Bot_ScriptAction_SetFarSeeingRange}, +{"Bot_ScriptAction_SetVisionRange", (byte *)Bot_ScriptAction_SetVisionRange}, +{"Bot_ScriptAction_SetFireCycleTime", (byte *)Bot_ScriptAction_SetFireCycleTime}, +{"Bot_ScriptAction_SetFireRate", (byte *)Bot_ScriptAction_SetFireRate}, +{"Bot_ScriptAction_BotDebugging", (byte *)Bot_ScriptAction_BotDebugging}, +{"Bot_ScriptAction_PrintGlobalAccum", (byte *)Bot_ScriptAction_PrintGlobalAccum}, +{"Bot_ScriptAction_PrintAccum", (byte *)Bot_ScriptAction_PrintAccum}, +{"Bot_ScriptAction_SetProne", (byte *)Bot_ScriptAction_SetProne}, +{"Bot_ScriptAction_SetCrouch", (byte *)Bot_ScriptAction_SetCrouch}, +{"Bot_ScriptAction_SetHearingRange", (byte *)Bot_ScriptAction_SetHearingRange}, +{"Bot_ScriptAction_SetFieldOfView", (byte *)Bot_ScriptAction_SetFieldOfView}, +{"Bot_ScriptAction_ResetScript", (byte *)Bot_ScriptAction_ResetScript}, +{"Bot_ScriptAction_NoTarget", (byte *)Bot_ScriptAction_NoTarget}, +{"Bot_ScriptAction_MovementAutonomy", (byte *)Bot_ScriptAction_MovementAutonomy}, +{"Bot_ScriptAction_SetMovementAutonomy", (byte *)Bot_ScriptAction_SetMovementAutonomy}, +{"Bot_ScriptAction_Cvar", (byte *)Bot_ScriptAction_Cvar}, +{"Bot_ScriptAction_FollowLeader", (byte *)Bot_ScriptAction_FollowLeader}, +{"Bot_ScriptAction_GlobalAccum", (byte *)Bot_ScriptAction_GlobalAccum}, +{"Bot_ScriptAction_SetClass", (byte *)Bot_ScriptAction_SetClass}, +{"Bot_ScriptAction_SetWeapon", (byte *)Bot_ScriptAction_SetWeapon}, +{"Bot_ScriptAction_PlaySoundAtPlayer", (byte *)Bot_ScriptAction_PlaySoundAtPlayer}, +{"Bot_ScriptAction_MountMG42", (byte *)Bot_ScriptAction_MountMG42}, +{"Bot_ScriptAction_SetAttribute", (byte *)Bot_ScriptAction_SetAttribute}, +{"Bot_ScriptAction_AbortIfWarmup", (byte *)Bot_ScriptAction_AbortIfWarmup}, +{"Bot_ScriptAction_Logging", (byte *)Bot_ScriptAction_Logging}, +{"Bot_ScriptAction_Trigger", (byte *)Bot_ScriptAction_Trigger}, +{"Bot_ScriptAction_MoveToMarker", (byte *)Bot_ScriptAction_MoveToMarker}, +{"Bot_ScriptAction_Wait", (byte *)Bot_ScriptAction_Wait}, +{"Bot_ScriptAction_Accum", (byte *)Bot_ScriptAction_Accum}, +{"Bot_ScriptAction_SpawnBot", (byte *)Bot_ScriptAction_SpawnBot}, +{"Bot_ScriptAction_SetAccumToPlayerCount", (byte *)Bot_ScriptAction_SetAccumToPlayerCount}, +{"Bot_ScriptAction_Print", (byte *)Bot_ScriptAction_Print}, +{"Bot_ScriptError", (byte *)Bot_ScriptError}, +{"Bot_ScriptAction_SetSpeed", (byte *)Bot_ScriptAction_SetSpeed}, +{"Bot_ScriptThink", (byte *)Bot_ScriptThink}, +{"Bot_ScriptLog_Entry", (byte *)Bot_ScriptLog_Entry}, +{"Bot_Script_GetCurrentLine", (byte *)Bot_Script_GetCurrentLine}, +{"Bot_ScriptRun", (byte *)Bot_ScriptRun}, +{"Bot_TeamScriptEvent", (byte *)Bot_TeamScriptEvent}, +{"Bot_ForceScriptEvent", (byte *)Bot_ForceScriptEvent}, +{"Bot_ScriptEvent", (byte *)Bot_ScriptEvent}, +{"Bot_ScriptChange", (byte *)Bot_ScriptChange}, +{"Bot_LineText", (byte *)Bot_LineText}, +{"Bot_ScriptInitBot", (byte *)Bot_ScriptInitBot}, +{"Bot_ScriptParse", (byte *)Bot_ScriptParse}, +{"Bot_ScriptParseAllCharacters", (byte *)Bot_ScriptParseAllCharacters}, +{"Bot_ScriptLoad", (byte *)Bot_ScriptLoad}, +{"Bot_ActionForString", (byte *)Bot_ActionForString}, +{"Bot_EventForString", (byte *)Bot_EventForString}, +{"Bot_EventMatch_IntInRange", (byte *)Bot_EventMatch_IntInRange}, +{"Bot_EventMatch_StringEqual", (byte *)Bot_EventMatch_StringEqual}, +{"Bot_FindSriptGlobalData", (byte *)Bot_FindSriptGlobalData}, +{"BotDebugViewClient", (byte *)BotDebugViewClient}, +{"BotInitMovementAutonomyPos", (byte *)BotInitMovementAutonomyPos}, +{"BotSetIdealViewAngles", (byte *)BotSetIdealViewAngles}, +{"GetBotAmmo", (byte *)GetBotAmmo}, +{"GetBotAutonomies", (byte *)GetBotAutonomies}, +{"BotDebug", (byte *)BotDebug}, +{"BotRecordAttack", (byte *)BotRecordAttack}, +{"BotRecordTeamDeath", (byte *)BotRecordTeamDeath}, +{"G_SetAASBlockingEntity", (byte *)G_SetAASBlockingEntity}, +{"BotCoop", (byte *)BotCoop}, +{"BotSinglePlayer", (byte *)BotSinglePlayer}, +{"BotFindEntityForName", (byte *)BotFindEntityForName}, +{"BotFindNextStaticEntity", (byte *)BotFindNextStaticEntity}, +{"BotBuildStaticEntityCache", (byte *)BotBuildStaticEntityCache}, +{"BotGetEntity", (byte *)BotGetEntity}, +{"FindBotByName", (byte *)FindBotByName}, +{"BotFindEntity", (byte *)BotFindEntity}, +{"BotAIShutdown", (byte *)BotAIShutdown}, +{"BotAISetup", (byte *)BotAISetup}, +{"BotInitLibrary", (byte *)BotInitLibrary}, +{"BotAIStartFrame", (byte *)BotAIStartFrame}, +{"BotAIThinkFrame", (byte *)BotAIThinkFrame}, +{"BotPreProcessAI", (byte *)BotPreProcessAI}, +{"BotAILoadMap", (byte *)BotAILoadMap}, +{"BotResetState", (byte *)BotResetState}, +{"BotAIShutdownClient", (byte *)BotAIShutdownClient}, +{"BotAISetupClient", (byte *)BotAISetupClient}, +{"BotSetUpCharacter", (byte *)BotSetUpCharacter}, +{"BotSetCharacterAttributes", (byte *)BotSetCharacterAttributes}, +{"ParseBotDefaultAttributes", (byte *)ParseBotDefaultAttributes}, +{"BotGetInitialAttributes", (byte *)BotGetInitialAttributes}, +{"BotScheduleBotThink", (byte *)BotScheduleBotThink}, +{"BotAI", (byte *)BotAI}, +{"BotTravelFlagsForClient", (byte *)BotTravelFlagsForClient}, +{"BotAIRegularUpdate", (byte *)BotAIRegularUpdate}, +{"BotUpdateInput", (byte *)BotUpdateInput}, +{"BotInputToUserCommand", (byte *)BotInputToUserCommand}, +{"BotSpeedBonus", (byte *)BotSpeedBonus}, +{"BotChangeViewAngles", (byte *)BotChangeViewAngles}, +{"BotChangeViewAngle", (byte *)BotChangeViewAngle}, +{"AngleDifference", (byte *)AngleDifference}, +{"BotAI_SetNumBots", (byte *)BotAI_SetNumBots}, +{"BotAI_GetNumBots", (byte *)BotAI_GetNumBots}, +{"BotEntityInfo", (byte *)BotEntityInfo}, +{"BotInterbreeding", (byte *)BotInterbreeding}, +{"BotAI_BotInitialChat", (byte *)BotAI_BotInitialChat}, +{"BotAI_GetSnapshotEntity", (byte *)BotAI_GetSnapshotEntity}, +{"BotAI_GetEntityState", (byte *)BotAI_GetEntityState}, +{"BotAI_GetClientState", (byte *)BotAI_GetClientState}, +{"BotAI_Trace", (byte *)BotAI_Trace}, +{"BotAI_Print", (byte *)BotAI_Print}, +{"BotEntityWithinView", (byte *)BotEntityWithinView}, +{"BotPickupWeapon", (byte *)BotPickupWeapon}, +{"BotCountLandMines", (byte *)BotCountLandMines}, +{"BotGetVisibleDamagableScriptMover", (byte *)BotGetVisibleDamagableScriptMover}, +{"BotBestTargetWeapon", (byte *)BotBestTargetWeapon}, +{"BotSetPOW", (byte *)BotSetPOW}, +{"BotUpdateViewAngles", (byte *)BotUpdateViewAngles}, +{"BotFindAndAttackEnemy", (byte *)BotFindAndAttackEnemy}, +{"EnemyIsCloseEnoughToFight", (byte *)EnemyIsCloseEnoughToFight}, +{"BotHealthScale", (byte *)BotHealthScale}, +{"BotCanSnipe", (byte *)BotCanSnipe}, +{"BotIsConstructible", (byte *)BotIsConstructible}, +{"BotGetReachableEntityArea", (byte *)BotGetReachableEntityArea}, +{"BotEntityTargetClassnameMatch", (byte *)BotEntityTargetClassnameMatch}, +{"BotDirectMoveToGoal", (byte *)BotDirectMoveToGoal}, +{"BotBattleNewNode", (byte *)BotBattleNewNode}, +{"BotTravelTimeToEntity", (byte *)BotTravelTimeToEntity}, +{"BotReduceListByTravelTime", (byte *)BotReduceListByTravelTime}, +{"BotGetArea", (byte *)BotGetArea}, +{"BotGetOrigin", (byte *)BotGetOrigin}, +{"BotGetEye", (byte *)BotGetEye}, +{"BotSeekCover", (byte *)BotSeekCover}, +{"BotLastHurt", (byte *)BotLastHurt}, +{"BotLastAttacked", (byte *)BotLastAttacked}, +{"BotEnemyCarryingFlag", (byte *)BotEnemyCarryingFlag}, +{"BotShareLastAttacked", (byte *)BotShareLastAttacked}, +{"BotEnemyFire", (byte *)BotEnemyFire}, +{"BotCheckVoiceChats", (byte *)BotCheckVoiceChats}, +{"BotDefaultNode", (byte *)BotDefaultNode}, +{"BotFindSpecialGoals", (byte *)BotFindSpecialGoals}, +{"BotCheckEmergencyTargets", (byte *)BotCheckEmergencyTargets}, +{"BotScriptAutonomyForString", (byte *)BotScriptAutonomyForString}, +{"BotStringForWeaponAutonomy", (byte *)BotStringForWeaponAutonomy}, +{"BotWeaponAutonomyForString", (byte *)BotWeaponAutonomyForString}, +{"BotStringForMovementAutonomy", (byte *)BotStringForMovementAutonomy}, +{"BotMovementAutonomyForString", (byte *)BotMovementAutonomyForString}, +{"BotCalculateMg42Spots", (byte *)BotCalculateMg42Spots}, +{"BotDropFlag", (byte *)BotDropFlag}, +{"BotClearGoal", (byte *)BotClearGoal}, +{"BotGetRandomVisibleSniperSpot", (byte *)BotGetRandomVisibleSniperSpot}, +{"BotGetNumVisibleSniperSpots", (byte *)BotGetNumVisibleSniperSpots}, +{"BotBestMG42Spot", (byte *)BotBestMG42Spot}, +{"BotBestLandmineSpotingSpot", (byte *)BotBestLandmineSpotingSpot}, +{"BotBestSniperSpot", (byte *)BotBestSniperSpot}, +{"BotGetAimAccuracySkill", (byte *)BotGetAimAccuracySkill}, +{"BotRecordDeath", (byte *)BotRecordDeath}, +{"BotRecordPain", (byte *)BotRecordPain}, +{"BotRecordKill", (byte *)BotRecordKill}, +{"BotRecordTeamChange", (byte *)BotRecordTeamChange}, +{"BotMoveToIntermission", (byte *)BotMoveToIntermission}, +{"BotSetBlockEnt", (byte *)BotSetBlockEnt}, +{"BotShutdownDeathmatchAI", (byte *)BotShutdownDeathmatchAI}, +{"BotSetupDeathmatchAI", (byte *)BotSetupDeathmatchAI}, +{"BotDeathmatchAI", (byte *)BotDeathmatchAI}, +{"BotPowThink", (byte *)BotPowThink}, +{"BotDeathmatchAIFirstCalled", (byte *)BotDeathmatchAIFirstCalled}, +{"BotDumpNodeSwitches", (byte *)BotDumpNodeSwitches}, +{"BotResetNodeSwitches", (byte *)BotResetNodeSwitches}, +{"BotUpdateReconInfo", (byte *)BotUpdateReconInfo}, +{"BotCheckAlert", (byte *)BotCheckAlert}, +{"BotCheckAir", (byte *)BotCheckAir}, +{"BotCheckSnapshot", (byte *)BotCheckSnapshot}, +{"BotCheckEvents", (byte *)BotCheckEvents}, +{"BotAIBlocked", (byte *)BotAIBlocked}, +{"BotModelMinsMaxs", (byte *)BotModelMinsMaxs}, +{"BotSetMovedir", (byte *)BotSetMovedir}, +{"BotEntityToActivate", (byte *)BotEntityToActivate}, +{"BotMapScripts", (byte *)BotMapScripts}, +{"BotCheckAttack", (byte *)BotCheckAttack}, +{"BotScopedWeapon", (byte *)BotScopedWeapon}, +{"BotThrottleWeapon", (byte *)BotThrottleWeapon}, +{"BotMoveWhileFiring", (byte *)BotMoveWhileFiring}, +{"BotAimAtEnemySP", (byte *)BotAimAtEnemySP}, +{"BotAimAtEnemy", (byte *)BotAimAtEnemy}, +{"swayrand", (byte *)swayrand}, +{"BotGetReactionTime", (byte *)BotGetReactionTime}, +{"BotFindEnemyMP", (byte *)BotFindEnemyMP}, +{"BotSortClientsByDistance", (byte *)BotSortClientsByDistance}, +{"BotHasWeaponWithRange", (byte *)BotHasWeaponWithRange}, +{"BotWeaponRange", (byte *)BotWeaponRange}, +{"BotFindEnemies", (byte *)BotFindEnemies}, +{"BotIsValidTarget", (byte *)BotIsValidTarget}, +{"BotGetEntitySurfaceSoundCoefficient", (byte *)BotGetEntitySurfaceSoundCoefficient}, +{"BotUpdateAlertStateSquadSensingInfo", (byte *)BotUpdateAlertStateSquadSensingInfo}, +{"ChangeBotAlertState", (byte *)ChangeBotAlertState}, +{"BotNoLeaderPenalty", (byte *)BotNoLeaderPenalty}, +{"BotDangerousGoal", (byte *)BotDangerousGoal}, +{"BotIgnoreGoal", (byte *)BotIgnoreGoal}, +{"BotCheckMovementAutonomy", (byte *)BotCheckMovementAutonomy}, +{"sAdjustPointTowardsPlayer", (byte *)sAdjustPointTowardsPlayer}, +{"sAngleBetweenVectors", (byte *)sAngleBetweenVectors}, +{"botindicator_think", (byte *)botindicator_think}, +{"BotGoalForEntity", (byte *)BotGoalForEntity}, +{"BotGoalWithinMovementAutonomy", (byte *)BotGoalWithinMovementAutonomy}, +{"BotPointWithinRawMovementAutonomy", (byte *)BotPointWithinRawMovementAutonomy}, +{"BotPointWithinMovementAutonomy", (byte *)BotPointWithinMovementAutonomy}, +{"BotWithinLeaderFollowDist", (byte *)BotWithinLeaderFollowDist}, +{"BotGetFollowAutonomyDist", (byte *)BotGetFollowAutonomyDist}, +{"BotGetMovementAutonomyRange", (byte *)BotGetMovementAutonomyRange}, +{"BotGetRawMovementAutonomyRange", (byte *)BotGetRawMovementAutonomyRange}, +{"BotGetMovementAutonomyPos", (byte *)BotGetMovementAutonomyPos}, +{"BotGetMovementAutonomyLevel", (byte *)BotGetMovementAutonomyLevel}, +{"BotCheckAttackAtPos", (byte *)BotCheckAttackAtPos}, +{"BotVisibleFromPos", (byte *)BotVisibleFromPos}, +{"BotEntityVisible", (byte *)BotEntityVisible}, +{"BotEntInvisibleBySmokeBomb", (byte *)BotEntInvisibleBySmokeBomb}, +{"InFieldOfVision", (byte *)InFieldOfVision}, +{"BotSameTeam", (byte *)BotSameTeam}, +{"BotAttackMove", (byte *)BotAttackMove}, +{"BotRoamGoal", (byte *)BotRoamGoal}, +{"BotGoForPowerups", (byte *)BotGoForPowerups}, +{"BotDontAvoid", (byte *)BotDontAvoid}, +{"BotGoCamp", (byte *)BotGoCamp}, +{"BotCanAndWantsToRocketJump", (byte *)BotCanAndWantsToRocketJump}, +{"BotWantsToHelp", (byte *)BotWantsToHelp}, +{"BotWantsToChase", (byte *)BotWantsToChase}, +{"BotWantsToRetreat", (byte *)BotWantsToRetreat}, +{"BotAggression", (byte *)BotAggression}, +{"TeamPlayIsOn", (byte *)TeamPlayIsOn}, +{"BotInitWaypoints", (byte *)BotInitWaypoints}, +{"BotFreeWaypoints", (byte *)BotFreeWaypoints}, +{"BotFindWayPoint", (byte *)BotFindWayPoint}, +{"BotCreateWayPoint", (byte *)BotCreateWayPoint}, +{"EntityHasQuad", (byte *)EntityHasQuad}, +{"EntityIsChatting", (byte *)EntityIsChatting}, +{"EntityIsShooting", (byte *)EntityIsShooting}, +{"EntityIsInvisible", (byte *)EntityIsInvisible}, +{"EntityInLimbo", (byte *)EntityInLimbo}, +{"EntityIsDead", (byte *)EntityIsDead}, +{"BotInSlime", (byte *)BotInSlime}, +{"BotInLava", (byte *)BotInLava}, +{"BotIntermission", (byte *)BotIntermission}, +{"BotIsObserver", (byte *)BotIsObserver}, +{"BotIsPOW", (byte *)BotIsPOW}, +{"BotIsDead", (byte *)BotIsDead}, +{"BotSetTeleportTime", (byte *)BotSetTeleportTime}, +{"BotBattleUseItems", (byte *)BotBattleUseItems}, +{"BotUpdateBattleInventory", (byte *)BotUpdateBattleInventory}, +{"BotUpdateInventory", (byte *)BotUpdateInventory}, +{"BotSetupForMovement", (byte *)BotSetupForMovement}, +{"BotCycleWeapon", (byte *)BotCycleWeapon}, +{"BotChooseWeapon", (byte *)BotChooseWeapon}, +{"BotBestFightWeapon", (byte *)BotBestFightWeapon}, +{"BotWeaponWantScale", (byte *)BotWeaponWantScale}, +{"BotTeamMatesNearEnemy", (byte *)BotTeamMatesNearEnemy}, +{"BotWeaponClosestDist", (byte *)BotWeaponClosestDist}, +{"BotWeaponOnlyUseIfInInRange", (byte *)BotWeaponOnlyUseIfInInRange}, +{"BotWeaponCharged", (byte *)BotWeaponCharged}, +{"G_WeaponCharged", (byte *)G_WeaponCharged}, +{"BotGotEnoughAmmoForWeapon", (byte *)BotGotEnoughAmmoForWeapon}, +{"EasyClientName", (byte *)EasyClientName}, +{"stristr", (byte *)stristr}, +{"ClientFromName", (byte *)ClientFromName}, +{"ClientSkin", (byte *)ClientSkin}, +{"ClientName", (byte *)ClientName}, +{"BotFindNearbyGoal", (byte *)BotFindNearbyGoal}, +{"BotFindNearbyTriggerGoal", (byte *)BotFindNearbyTriggerGoal}, +{"BotReachableBBoxAreaNum", (byte *)BotReachableBBoxAreaNum}, +{"BotPointAreaNum", (byte *)BotPointAreaNum}, +{"BotFirstLadderArea", (byte *)BotFirstLadderArea}, +{"BotFirstReachabilityArea", (byte *)BotFirstReachabilityArea}, +{"BotCarryingFlag", (byte *)BotCarryingFlag}, +{"AINode_MP_NavigateFromVoid", (byte *)AINode_MP_NavigateFromVoid}, +{"AIEnter_MP_NavigateFromVoid", (byte *)AIEnter_MP_NavigateFromVoid}, +{"AINode_MP_MoveToAutonomyRange", (byte *)AINode_MP_MoveToAutonomyRange}, +{"AIEnter_MP_MoveToAutonomyRange", (byte *)AIEnter_MP_MoveToAutonomyRange}, +{"AINode_MP_Script_MoveToMarker", (byte *)AINode_MP_Script_MoveToMarker}, +{"AIEnter_MP_Script_MoveToMarker", (byte *)AIEnter_MP_Script_MoveToMarker}, +{"AINode_MP_Battle_Retreat", (byte *)AINode_MP_Battle_Retreat}, +{"AIEnter_MP_Battle_Retreat", (byte *)AIEnter_MP_Battle_Retreat}, +{"AINode_MP_Battle_Chase", (byte *)AINode_MP_Battle_Chase}, +{"AIEnter_MP_Battle_Chase", (byte *)AIEnter_MP_Battle_Chase}, +{"AINode_MP_Battle_Fight", (byte *)AINode_MP_Battle_Fight}, +{"AIEnter_MP_Battle_Fight", (byte *)AIEnter_MP_Battle_Fight}, +{"AINode_MP_DisarmDynamite", (byte *)AINode_MP_DisarmDynamite}, +{"AIEnter_MP_DisarmDynamite", (byte *)AIEnter_MP_DisarmDynamite}, +{"AINode_MP_PlantMine", (byte *)AINode_MP_PlantMine}, +{"AIEnter_MP_PlantMine", (byte *)AIEnter_MP_PlantMine}, +{"AINode_MP_ConstructibleTarget", (byte *)AINode_MP_ConstructibleTarget}, +{"AIEnter_MP_ConstructibleTarget", (byte *)AIEnter_MP_ConstructibleTarget}, +{"AINode_MP_DynamiteTarget", (byte *)AINode_MP_DynamiteTarget}, +{"AIEnter_MP_DynamiteTarget", (byte *)AIEnter_MP_DynamiteTarget}, +{"AINode_MP_SatchelChargeTarget", (byte *)AINode_MP_SatchelChargeTarget}, +{"AIEnter_MP_SatchelChargeTarget", (byte *)AIEnter_MP_SatchelChargeTarget}, +{"AINode_MP_TouchTarget", (byte *)AINode_MP_TouchTarget}, +{"AIEnter_MP_TouchTarget", (byte *)AIEnter_MP_TouchTarget}, +{"AINode_MP_DefendTarget", (byte *)AINode_MP_DefendTarget}, +{"AIEnter_MP_DefendTarget", (byte *)AIEnter_MP_DefendTarget}, +{"AINode_MP_SniperSpot", (byte *)AINode_MP_SniperSpot}, +{"AIEnter_MP_SniperSpot", (byte *)AIEnter_MP_SniperSpot}, +{"AINode_MP_ScanForLandmines", (byte *)AINode_MP_ScanForLandmines}, +{"AIEnter_MP_ScanForLandmines", (byte *)AIEnter_MP_ScanForLandmines}, +{"AINode_MP_MG42Mount", (byte *)AINode_MP_MG42Mount}, +{"AIEnter_MP_MG42Mount", (byte *)AIEnter_MP_MG42Mount}, +{"AINode_MP_MG42Scan", (byte *)AINode_MP_MG42Scan}, +{"AIEnter_MP_MG42Scan", (byte *)AIEnter_MP_MG42Scan}, +{"AINode_MP_Battle_MobileMG42", (byte *)AINode_MP_Battle_MobileMG42}, +{"AIEnter_MP_Battle_MobileMG42", (byte *)AIEnter_MP_Battle_MobileMG42}, +{"AINode_MP_FixMG42", (byte *)AINode_MP_FixMG42}, +{"AIEnter_MP_FixMG42", (byte *)AIEnter_MP_FixMG42}, +{"AINode_MP_AttackTarget", (byte *)AINode_MP_AttackTarget}, +{"AIEnter_MP_AttackTarget", (byte *)AIEnter_MP_AttackTarget}, +{"AINode_MP_PanzerTarget", (byte *)AINode_MP_PanzerTarget}, +{"AIEnter_MP_PanzerTarget", (byte *)AIEnter_MP_PanzerTarget}, +{"AINode_MP_MedicRevive", (byte *)AINode_MP_MedicRevive}, +{"AIEnter_MP_MedicRevive", (byte *)AIEnter_MP_MedicRevive}, +{"AINode_MP_MedicGiveHealth", (byte *)AINode_MP_MedicGiveHealth}, +{"AIEnter_MP_MedicGiveHealth", (byte *)AIEnter_MP_MedicGiveHealth}, +{"AINode_MP_GiveAmmo", (byte *)AINode_MP_GiveAmmo}, +{"AIEnter_MP_GiveAmmo", (byte *)AIEnter_MP_GiveAmmo}, +{"AINode_MP_AvoidDanger", (byte *)AINode_MP_AvoidDanger}, +{"AIEnter_MP_AvoidDanger", (byte *)AIEnter_MP_AvoidDanger}, +{"AINode_MP_Seek_NBG", (byte *)AINode_MP_Seek_NBG}, +{"AIEnter_MP_Seek_NBG", (byte *)AIEnter_MP_Seek_NBG}, +{"AINode_MP_Seek_ActivateEntity", (byte *)AINode_MP_Seek_ActivateEntity}, +{"AIEnter_MP_Seek_ActivateEntity", (byte *)AIEnter_MP_Seek_ActivateEntity}, +{"AINode_MP_Respawn", (byte *)AINode_MP_Respawn}, +{"AIEnter_MP_Respawn", (byte *)AIEnter_MP_Respawn}, +{"AINode_MP_Stand", (byte *)AINode_MP_Stand}, +{"AIEnter_MP_Stand", (byte *)AIEnter_MP_Stand}, +{"AINode_MP_Observer", (byte *)AINode_MP_Observer}, +{"AIEnter_MP_Observer", (byte *)AIEnter_MP_Observer}, +{"AINode_MP_Intermission", (byte *)AINode_MP_Intermission}, +{"AIEnter_MP_Intermission", (byte *)AIEnter_MP_Intermission}, +{"BotMP_MoveToGoal", (byte *)BotMP_MoveToGoal}, +{"BotMP_FindGoal", (byte *)BotMP_FindGoal}, +{"BotMP_FindGoal_New", (byte *)BotMP_FindGoal_New}, +{"BotMP_AlreadyDoing_FastOut", (byte *)BotMP_AlreadyDoing_FastOut}, +{"BotMP_FindGoal_PostProcessGoal", (byte *)BotMP_FindGoal_PostProcessGoal}, +{"BotMP_FindGoal_ProcessGoal", (byte *)BotMP_FindGoal_ProcessGoal}, +{"BotMP_FindGoal_ClassForGoalType", (byte *)BotMP_FindGoal_ClassForGoalType}, +{"BotMP_FindGoals_Sort_CovertOps", (byte *)BotMP_FindGoals_Sort_CovertOps}, +{"BotMP_FindGoals_Sort_Engineer", (byte *)BotMP_FindGoals_Sort_Engineer}, +{"BotMP_FindGoals_Sort_Standard", (byte *)BotMP_FindGoals_Sort_Standard}, +{"BotMP_FindGoal_BuildGoalList", (byte *)BotMP_FindGoal_BuildGoalList}, +{"BotMP_CheckEmergencyGoals", (byte *)BotMP_CheckEmergencyGoals}, +{"BotMP_CheckClassActions", (byte *)BotMP_CheckClassActions}, +{"BotMatchMessage", (byte *)BotMatchMessage}, +{"BotMatch_Kill", (byte *)BotMatch_Kill}, +{"BotMatch_LeadTheWay", (byte *)BotMatch_LeadTheWay}, +{"BotMatch_WhereAreYou", (byte *)BotMatch_WhereAreYou}, +{"BotNearestVisibleItem", (byte *)BotNearestVisibleItem}, +{"BotMatch_WhatIsMyCommand", (byte *)BotMatch_WhatIsMyCommand}, +{"BotMatch_WhatAreYouDoing", (byte *)BotMatch_WhatAreYouDoing}, +{"BotMatch_WhoIsTeamLeader", (byte *)BotMatch_WhoIsTeamLeader}, +{"BotMatch_StopTeamLeaderShip", (byte *)BotMatch_StopTeamLeaderShip}, +{"BotMatch_StartTeamLeaderShip", (byte *)BotMatch_StartTeamLeaderShip}, +{"BotMatch_Dismiss", (byte *)BotMatch_Dismiss}, +{"BotMatch_FormationSpace", (byte *)BotMatch_FormationSpace}, +{"BotMatch_CheckPoint", (byte *)BotMatch_CheckPoint}, +{"BotMatch_WhichTeam", (byte *)BotMatch_WhichTeam}, +{"BotMatch_LeaveSubteam", (byte *)BotMatch_LeaveSubteam}, +{"BotMatch_JoinSubteam", (byte *)BotMatch_JoinSubteam}, +{"BotMatch_ReturnFlag", (byte *)BotMatch_ReturnFlag}, +{"BotMatch_RushBase", (byte *)BotMatch_RushBase}, +{"BotMatch_GetFlag", (byte *)BotMatch_GetFlag}, +{"BotMatch_Patrol", (byte *)BotMatch_Patrol}, +{"BotMatch_Camp", (byte *)BotMatch_Camp}, +{"BotMatch_GetItem", (byte *)BotMatch_GetItem}, +{"BotMatch_DefendKeyArea", (byte *)BotMatch_DefendKeyArea}, +{"BotMatch_HelpAccompany", (byte *)BotMatch_HelpAccompany}, +{"BotGPSToPosition", (byte *)BotGPSToPosition}, +{"BotAddressedToBot", (byte *)BotAddressedToBot}, +{"BotGetPatrolWaypoints", (byte *)BotGetPatrolWaypoints}, +{"NumPlayersOnSameTeam", (byte *)NumPlayersOnSameTeam}, +{"FindEnemyByName", (byte *)FindEnemyByName}, +{"FindClientByName", (byte *)FindClientByName}, +{"BotGetTime", (byte *)BotGetTime}, +{"BotGetMessageTeamGoal", (byte *)BotGetMessageTeamGoal}, +{"BotGetItemTeamGoal", (byte *)BotGetItemTeamGoal}, +{0, 0} diff --git a/src/botai/ai_main.c b/src/botai/ai_main.c new file mode 100644 index 0000000..16e9455 --- /dev/null +++ b/src/botai/ai_main.c @@ -0,0 +1,2670 @@ +/* +=========================================================================== + +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: ai_main.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +#include "../game/g_local.h" +#include "../game/q_shared.h" +#include "../game/botlib.h" //bot lib interface +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" //bot ai interface + +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +#include "ai_team.h" +#include "ai_distances.h" +// +#include "chars.h" +#include "inv.h" +#include "syn.h" + +#ifndef MAX_PATH // LBO 1/26/05 +#define MAX_PATH 144 +#endif + +//bot states +bot_state_t botstates[MAX_CLIENTS]; +//number of bots +int ai_numbots; +//time to do a regular update +float regularupdate_time; +// +vmCvar_t bot_thinktime; +vmCvar_t bot_verbose; +vmCvar_t memorydump; +vmCvar_t bot_profile; +vmCvar_t bot_findgoal; + +/* +================== +BotAI_Print +================== +*/ +void QDECL BotAI_Print( int type, char *fmt, ... ) { + char str[2048]; + va_list ap; + + va_start( ap, fmt ); + Q_vsnprintf( str, sizeof( str ), fmt, ap ); + va_end( ap ); + + switch ( type ) { + case PRT_MESSAGE: { + trap_Cvar_Update( &bot_verbose ); + if ( bot_verbose.integer == 1 ) { + G_Printf( "%s", str ); + } + break; + } + case PRT_WARNING: { + G_Printf( S_COLOR_YELLOW "Warning: %s", str ); + break; + } + case PRT_ERROR: { + G_Printf( S_COLOR_RED "Error: %s", str ); + break; + } + case PRT_FATAL: { + G_Printf( S_COLOR_RED "Fatal: %s", str ); + break; + } + case PRT_EXIT: { + G_Error( S_COLOR_RED "Exit: %s", str ); + break; + } + default: { + G_Printf( "unknown print type\n" ); + break; + } + } +} + +/* +================== +BotAI_Trace +================== +*/ +void BotAI_Trace( bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask ) { + trace_t trace; + + trap_Trace( &trace, start, mins, maxs, end, passent, contentmask ); + //copy the trace information + bsptrace->allsolid = trace.allsolid; + bsptrace->startsolid = trace.startsolid; + bsptrace->fraction = trace.fraction; + VectorCopy( trace.endpos, bsptrace->endpos ); + bsptrace->plane.dist = trace.plane.dist; + VectorCopy( trace.plane.normal, bsptrace->plane.normal ); + bsptrace->plane.signbits = trace.plane.signbits; + bsptrace->plane.type = trace.plane.type; + bsptrace->surface.value = trace.surfaceFlags; + bsptrace->ent = trace.entityNum; + bsptrace->exp_dist = 0; + bsptrace->sidenum = 0; + bsptrace->contents = 0; +} + +/* +================== +BotAI_GetClientState +================== +*/ +int BotAI_GetClientState( int clientNum, playerState_t *state ) { + gentity_t* ent = &g_entities[clientNum]; + if ( !ent->inuse ) { + return qfalse; + } + if ( !ent->client ) { + return qfalse; + } + + memcpy( state, &ent->client->ps, sizeof( playerState_t ) ); + return qtrue; +} + +/* +================== +BotAI_GetEntityState +================== +*/ +int BotAI_GetEntityState( int entityNum, entityState_t *state ) { + gentity_t *ent; + + ent = BotGetEntity( entityNum ); + memset( state, 0, sizeof( entityState_t ) ); + if ( !ent->inuse ) { + return qfalse; + } + if ( !ent->r.linked ) { + return qfalse; + } + if ( ent->r.svFlags & SVF_NOCLIENT ) { + return qfalse; + } + memcpy( state, &ent->s, sizeof( entityState_t ) ); + return qtrue; +} + +/* +================== +BotAI_GetSnapshotEntity +================== +*/ +int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) { + int entNum; + + entNum = trap_BotGetSnapshotEntity( clientNum, sequence ); + if ( entNum == -1 ) { + memset( state, 0, sizeof( entityState_t ) ); + return -1; + } + + BotAI_GetEntityState( entNum, state ); + + return sequence + 1; +} + +/* +================== +BotAI_BotInitialChat +================== +*/ +void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) { + int i, mcontext; + va_list ap; + char *p; + char *vars[MAX_MATCHVARIABLES]; + +// RF, disabled + return; + + memset( vars, 0, sizeof( vars ) ); + va_start( ap, type ); + p = va_arg( ap, char * ); + for ( i = 0; i < MAX_MATCHVARIABLES; i++ ) { + if ( !p ) { + break; + } + vars[i] = p; + p = va_arg( ap, char * ); + } + va_end( ap ); + + mcontext = CONTEXT_NORMAL | CONTEXT_NEARBYITEM | CONTEXT_NAMES; + + trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] ); +} + +/* +============== +BotInterbreeding +============== +*/ +void BotInterbreeding( void ) { + float ranks[MAX_CLIENTS]; + int parent1, parent2, child; + int i, j; + + // get rankings for all the bots + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( botstates[j].inuse ) { + ranks[j] = botstates[i].num_kills * 2 - botstates[j].num_deaths; + } else { + ranks[j] = -1; + } + } + + if ( trap_GeneticParentsAndChildSelection( MAX_CLIENTS, ranks, &parent1, &parent2, &child ) ) { + trap_BotInterbreedGoalFuzzyLogic( botstates[parent1].gs, botstates[parent2].gs, botstates[child].gs ); + trap_BotMutateGoalFuzzyLogic( botstates[child].gs, 1 ); + } + // reset the kills and deaths + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( botstates[j].inuse ) { + botstates[j].num_kills = 0; + botstates[j].num_deaths = 0; + } + } +} + +/* +============== +BotEntityInfo +============== +*/ +void BotEntityInfo( int entnum, aas_entityinfo_t *info ) { + trap_AAS_EntityInfo( entnum, info ); +} + +/* +============== +BotAI_GetNumBots +============== +*/ +int BotAI_GetNumBots( void ) { + return ai_numbots; +} + +/* +============== +BotAI_SetNumBots +============== +*/ +void BotAI_SetNumBots( int numbots ) { + ai_numbots = numbots; +} + + +/* +============== +AngleDifference +============== +*/ +float AngleDifference( float ang1, float ang2 ) { + float diff; + + diff = ang1 - ang2; + if ( ang1 > ang2 ) { + if ( diff > 180.0 ) { + diff -= 360.0; + } + } else { + if ( diff < -180.0 ) { + diff += 360.0; + } + } + return diff; +} + +/* +============== +BotChangeViewAngle +============== +*/ +float BotChangeViewAngle( float angle, float ideal_angle, float speed ) { + float move; + + angle = AngleMod( angle ); + ideal_angle = AngleMod( ideal_angle ); + if ( angle == ideal_angle ) { + return angle; + } + move = ideal_angle - angle; + if ( ideal_angle > angle ) { + if ( move > 180.0 ) { + move -= 360.0; + } + } else { + if ( move < -180.0 ) { + move += 360.0; + } + } + if ( move > 0 ) { + if ( move > speed ) { + move = speed; + } + } else { + if ( move < -speed ) { + move = -speed; + } + } + return AngleMod( angle + move ); +} + +/* +============== +BotChangeViewAngles +============== +*/ +void BotChangeViewAngles( bot_state_t *bs, float thinktime ) { + float diff, factor, maxchange, anglespeed; + int i; + + if ( bs->ideal_viewangles[PITCH] > 180 ) { + bs->ideal_viewangles[PITCH] -= 360; + } + // + if ( bs->enemy >= 0 ) { + factor = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01, 1 ); + maxchange = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800 ); + } else { + factor = 0.15; + maxchange = 240; + } + maxchange *= thinktime; + for ( i = 0; i < 2; i++ ) { + diff = fabs( AngleDifference( bs->viewangles[i], bs->ideal_viewangles[i] ) ); + anglespeed = diff * factor; + if ( anglespeed > maxchange ) { + anglespeed = maxchange; + } + bs->viewangles[i] = BotChangeViewAngle( bs->viewangles[i], + bs->ideal_viewangles[i], anglespeed ); + //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` + //bs->viewangles[i] = bs->ideal_viewangles[i]; + } + if ( bs->viewangles[PITCH] > 180 ) { + bs->viewangles[PITCH] -= 360; + } + //elementary action: view + trap_EA_View( bs->client, bs->viewangles ); +} + + +/* +============== +BotSpeedBonus +============== +*/ +void BotSpeedBonus( int clientNum ) { +} + +/* +============== +BotInputToUserCommand +============== +*/ +void BotInputToUserCommand( bot_state_t *bs, bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time ) { + vec3_t angles, forward, right; + short temp; + int j; + int value = 0; + + //clear the whole structure + memset( ucmd, 0, sizeof( usercmd_t ) ); + // + //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed); + //the duration for the user command in milli seconds + ucmd->serverTime = time; + // + if ( bi->actionflags & ACTION_DELAYEDJUMP ) { + bi->actionflags |= ACTION_JUMP; + bi->actionflags &= ~ACTION_DELAYEDJUMP; + } + //set the buttons + //if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK; + if ( bi->actionflags & ACTION_ATTACK ) { + ucmd->buttons |= BUTTON_ATTACK; + } + if ( bi->actionflags & ACTION_TALK ) { + ucmd->buttons |= BUTTON_TALK; + } + if ( bi->actionflags & ACTION_GESTURE ) { + ucmd->buttons |= BUTTON_GESTURE; + } + if ( bi->actionflags & ACTION_USE ) { + ucmd->buttons |= BUTTON_ACTIVATE; + } + if ( bi->actionflags & ACTION_WALK ) { + ucmd->buttons |= BUTTON_WALKING; + } + if ( bi->actionflags & ACTION_RELOAD ) { + ucmd->wbuttons |= WBUTTON_RELOAD; + } + + // START xkan, 9/16/2002 + // see if the bots should prone + // there are 3 cases here (from highest priority to lowest): + // 1. the script set the bot to prone + // 2. the player has ordered the bot to prone + // 3. the player is proning, and the bot is following his lead + if ( ( bi->actionflags & ACTION_PRONE || bs->script.flags & BSFL_PRONE ) + && !( g_entities[bs->client].client->ps.eFlags & EF_PRONE ) ) { + // xkan, 10/23/2002 - only set WBUTTON_PRONE if we are not proning already + // 'cause setting it again will cancel the proning + ucmd->wbuttons |= WBUTTON_PRONE; + } else if ( !( bi->actionflags & ACTION_PRONE ) + && !( bs->script.flags & BSFL_PRONE ) + && ( g_entities[bs->client].client->ps.eFlags & EF_PRONE ) ) { + // now see if we should unprone, setting WBUTTON_PRONE again while + // we are already proning will unprone - xkan, 10/23/2002 + ucmd->wbuttons |= WBUTTON_PRONE; + } + // END xkan, 9/16/2002 + + // Start - TAT 9/18/2002 + // Bots aren't zooming when they start using binoculars + // so if our new weapon is binoculars, do a start zoom + if ( ucmd->weapon != bi->weapon && bi->weapon == WP_BINOCULARS ) { + ucmd->wbuttons |= WBUTTON_ZOOM; + } + // End - TAT 9/18/2002 + +/* // Test having the bots lean left + if (bs->leanleft) + { + // Force them to lean left + ucmd->wbuttons |= WBUTTON_LEANLEFT; + + } // if (bs->leanleft)... + */ + + ucmd->weapon = bi->weapon; + // set the team elements + //set the view angles + //NOTE: the ucmd->angles are the angles WITHOUT the delta angles + ucmd->angles[PITCH] = ANGLE2SHORT( bi->viewangles[PITCH] ); + ucmd->angles[YAW] = ANGLE2SHORT( bi->viewangles[YAW] ); + ucmd->angles[ROLL] = ANGLE2SHORT( bi->viewangles[ROLL] ); + //subtract the delta angles + for ( j = 0; j < 3; j++ ) { + temp = ucmd->angles[j] - delta_angles[j]; + /*NOTE: disabled because temp should be mod first + if ( j == PITCH ) { + // don't let the player look up or down more than 90 degrees + if ( temp > 16000 ) temp = 16000; + else if ( temp < -16000 ) temp = -16000; + } + */ + ucmd->angles[j] = temp; + } + //NOTE: movement is relative to the REAL view angles + //get the horizontal forward and right vector + //get the pitch in the range [-180, 180] + if ( bi->dir[2] ) { + angles[PITCH] = bi->viewangles[PITCH]; + } else { angles[PITCH] = 0;} + angles[YAW] = bi->viewangles[YAW]; + angles[ROLL] = 0; + AngleVectors( angles, forward, right, NULL ); + //bot input speed is in the range [0, 400] + bi->speed = bi->speed * 127 / 400; + // SP: adjust speed according to current animation + /* + if (BotSinglePlayer() && !(bi->actionflags & ACTION_CROUCH)) { + animation_t *anim; + int animIndex; + bg_playerclass_t *classInfo; + // + classInfo = BG_GetPlayerClassInfo( bs->sess.sessionTeam, bs->sess.playerType ); + if (classInfo) { + animIndex = BG_GetAnimScriptAnimation( bs->client, classInfo, (ucmd->buttons & BUTTON_WALKING) ? ANIM_MT_WALK : ANIM_MT_RUN ); + if (animIndex >= 0) { + anim = BG_GetAnimationForIndex( classInfo, animIndex ); + if (anim->moveSpeed > 0) { + bi->speed *= (float)anim->moveSpeed / g_speed.value; + if (bi->speed > 1.0f) bi->speed = 1.0f; + } + } + } + } + */ +//Com_Printf( "BotInputToUserCommand: bi->dir %s\n", vtosf(bi->dir) ); + //set the view independent movement + ucmd->forwardmove = DotProduct( forward, bi->dir ) * bi->speed; + ucmd->rightmove = DotProduct( right, bi->dir ) * bi->speed; + ucmd->upmove = abs( forward[2] ) * bi->dir[2] * bi->speed; + //normal keyboard movement + if ( bi->actionflags & ACTION_MOVEFORWARD ) { + ucmd->forwardmove += 127; + } + if ( bi->actionflags & ACTION_MOVEBACK ) { + ucmd->forwardmove -= 127; + } + if ( bi->actionflags & ACTION_MOVELEFT ) { + ucmd->rightmove -= 127; + } + if ( bi->actionflags & ACTION_MOVERIGHT ) { + ucmd->rightmove += 127; + } + //jump/moveup + if ( bi->actionflags & ACTION_JUMP ) { + ucmd->upmove += 127; + } + //crouch/movedown + if ( ( bi->actionflags & ACTION_CROUCH ) + // START xkan, 8/23/2002 + // if the script says crouch, then crouch. + || ( bs->script.flags & BSFL_CROUCH ) + // END xkan, 8/23/2002 + ) { + ucmd->upmove -= 127; + } + + + // + //in single player, restrict movement speeds + value = -1; + if ( BotSinglePlayer() || BotCoop() ) { + // Axis bots should be slow + if ( bs->sess.sessionTeam == TEAM_AXIS ) { + if ( ucmd->buttons & BUTTON_WALKING ) { + if ( ucmd->forwardmove || ucmd->rightmove ) { + g_entities[bs->client].client->ps.friction = 0.1; + } else { + g_entities[bs->client].client->ps.friction = 1.0; + } + value = 24; // make sure feet dont slide in single player + } else { + g_entities[bs->client].client->ps.friction = 1.0; + value = 80; + } + } // if (bs->sess.sessionTeam == TEAM_AXIS)) ... + // Allied bots should keep up with you + else + { + if ( ucmd->buttons & BUTTON_WALKING ) { + if ( ucmd->forwardmove || ucmd->rightmove ) { + g_entities[bs->client].client->ps.friction = 0.1; + } else { + g_entities[bs->client].client->ps.friction = 1.0; + } + value = 64; // as fast as the player + } else { + g_entities[bs->client].client->ps.friction = 1.0; + value = 127; // as fast as the player + } // else... + } // if (BotSinglePlayer())... + + } else { + if ( ucmd->buttons & BUTTON_WALKING ) { + value = 64; // keep in line with player movements + } + } + // + //if (bs->leader == 0 && bs->leader_tagent == 0) + // G_Printf( "%s: %i, %i\n", g_entities[bs->client].client->pers.netname, ucmd->forwardmove, ucmd->rightmove ); + if ( value > 0 ) { + if ( ucmd->forwardmove > value ) { + ucmd->rightmove = (int)( (float)ucmd->rightmove * ( (float)value / (float)ucmd->forwardmove ) ); + ucmd->forwardmove = value; + } else if ( ucmd->forwardmove < -value ) { + ucmd->rightmove = (int)( (float)ucmd->rightmove * ( (float)-value / (float)ucmd->forwardmove ) ); + ucmd->forwardmove = -value; + } + // + if ( ucmd->rightmove > value ) { + ucmd->forwardmove = (int)( (float)ucmd->forwardmove * ( (float)value / (float)ucmd->rightmove ) ); + ucmd->rightmove = value; + } else if ( ucmd->rightmove < -value ) { + ucmd->forwardmove = (int)( (float)ucmd->forwardmove * ( (float)-value / (float)ucmd->rightmove ) ); + ucmd->rightmove = -value; + } + } + //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove); + //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime); +} + +/* +============== +BotUpdateInput +============== +*/ +void BotUpdateInput( bot_state_t *bs, int time ) { + bot_input_t bi; + int j; + + //add the delta angles to the bot's current view angles + for ( j = 0; j < 3; j++ ) { + bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); + } + // + BotChangeViewAngles( bs, (float) time / 1000 ); + trap_EA_GetInput( bs->client, (float) time / 1000, &bi ); + //respawn hack + if ( bi.actionflags & ACTION_RESPAWN ) { + if ( bs->lastucmd.buttons & BUTTON_ATTACK ) { + bi.actionflags &= ~( ACTION_RESPAWN | ACTION_ATTACK ); + } + } + // + BotInputToUserCommand( bs, &bi, &bs->lastucmd, bs->cur_ps.delta_angles, time ); + // sprint always if we have no enemy + if ( !( bs->lastucmd.buttons & BUTTON_WALKING ) ) { + if ( bs->flags & BFL_SPRINT ) { + bs->lastucmd.buttons |= BUTTON_SPRINT; + if ( level.clients[bs->client].pmext.sprintTime < 200 ) { + bs->flags &= ~BFL_SPRINT; + } + } else { + if ( level.clients[bs->client].pmext.sprintTime > 7000 ) { + // if we are trying to escape an enemy, or chasing them + if ( bs->enemy > -1 && bs->last_fire < level.time - 1000 ) { + bs->flags |= BFL_SPRINT; + } + // if we are defending someone, try to stay close + if ( bs->target_goal.entitynum == bs->leader && bs->leader >= 0 ) { + bs->flags |= BFL_SPRINT; + } + } + } + } + // if someone is trying to help us, walk + if ( g_entities[bs->client].missionLevel > level.time + 200 ) { + bs->lastucmd.buttons |= BUTTON_WALKING; + } + bs->lastucmd.serverTime = time; + //subtract the delta angles + for ( j = 0; j < 3; j++ ) { + bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); + } +} + +/* +============== +BotAIRegularUpdate +============== +*/ +void BotAIRegularUpdate( void ) { + if ( regularupdate_time < trap_AAS_Time() ) { + trap_BotUpdateEntityItems(); + regularupdate_time = trap_AAS_Time() + 1; + } +} + +/* +================ +BotTravelFlagsForClient + + Only returns special flags that are absolutely necessary for this level +================ +*/ +int BotTravelFlagsForClient( int client ) { + int tfl; + gclient_t *cl = &level.clients[client]; + // + if ( !cl || !cl->pers.connected == CON_CONNECTED ) { + return 0; + } + // + tfl = TFL_DEFAULT; + // + if ( cl->sess.sessionTeam == TEAM_ALLIES ) { + tfl |= TFL_TEAM_ALLIES; + } else if ( cl->sess.sessionTeam == TEAM_AXIS ) { + tfl |= TFL_TEAM_AXIS; + } + // + // if there are no team doors in the level, then always use defaults + if ( !level.doorAllowTeams ) { + return tfl; + } + // + if ( ( level.doorAllowTeams & TEAM_ALLIES ) && cl->sess.sessionTeam == TEAM_ALLIES ) { + tfl |= TFL_TEAM_ALLIES; + if ( ( level.doorAllowTeams & ALLOW_DISGUISED_CVOPS ) && cl->ps.powerups[PW_OPS_DISGUISED] ) { + tfl |= TFL_TEAM_ALLIES_DISGUISED; + } + } + if ( ( level.doorAllowTeams & TEAM_AXIS ) && cl->sess.sessionTeam == TEAM_AXIS ) { + tfl |= TFL_TEAM_AXIS; + if ( ( level.doorAllowTeams & ALLOW_DISGUISED_CVOPS ) && cl->ps.powerups[PW_OPS_DISGUISED] ) { + tfl |= TFL_TEAM_AXIS_DISGUISED; + } + } + // + return tfl; +} + +/* +============== +BotAI +============== +*/ +int BotAI( int client, float thinktime ) { + bot_state_t *bs; + char buf[1024]; + int j, areanum; + + trap_EA_ResetInput( client, NULL ); + + bs = &botstates[client]; + if ( !bs->inuse ) { + BotAI_Print( PRT_FATAL, "client %d hasn't been setup\n", client ); + return BLERR_AICLIENTNOTSETUP; + } + + //retrieve the current client state + BotAI_GetClientState( client, &bs->cur_ps ); + + // Mad Doctor I, 9/19/2002 + // If we haven't yet set up our initial desired weapon, do so now. + if ( bs->weaponnum == -1 ) { + // We at first want to use the weapon we spawned with + bs->weaponnum = bs->cur_ps.weapon; + + } // if (bs->weaponnum == -1)... + + //get the session data + bs->sess = level.clients[client].sess; + // set the team + bs->mpTeam = bs->sess.sessionTeam; + + //retrieve any waiting console messages + // Gordon: you MUST do this to acknowledge any commands sent + while ( trap_BotGetServerCommand( client, buf, sizeof( buf ) ) ) ; + + //add the delta angles to the bot's current view angles + for ( j = 0; j < 3; j++ ) { + bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); + } + //increase the local time of the bot + + // + bs->thinktime = thinktime; + //origin of the bot + VectorCopy( bs->cur_ps.origin, bs->origin ); + //eye coordinates of the bot + VectorCopy( bs->cur_ps.origin, bs->eye ); + bs->eye[2] += bs->cur_ps.viewheight; + + //set the travelflags for this bot + bs->tfl = BotTravelFlagsForClient( bs->client ); + + //get the area the bot is in + areanum = BotPointAreaNum( bs->client, bs->origin ); + if ( areanum ) { + bs->areanum = areanum; + } + + if ( bot_profile.integer == 3 ) { + int t = trap_Milliseconds(); + + // the real AI + BotDeathmatchAI( bs, thinktime ); + + t = trap_Milliseconds() - t; + + G_Printf( "Time for BotDeathmatchAI: %s: %i\n", level.clients[bs->client].pers.netname, t ); + } else { + BotDeathmatchAI( bs, thinktime ); + } + + level.clients[bs->client].sess.latchPlayerType = bs->mpClass; // FIXME: better place for this? + + //subtract the delta angles + for ( j = 0; j < 3; j++ ) { + bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); + } + + //everything was ok + return BLERR_NOERROR; +} + +/* +================== +BotScheduleBotThink +================== +*/ +void BotScheduleBotThink( void ) { + int i, j, botnum = 0; + int numbots = BotAI_GetNumBots(); + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( !botstates[j].inuse ) { + continue; + } + //initialize the bot think residual time + botstates[j].botthink_residual = bot_thinktime.integer * botnum / numbots; + botnum++; + } +} + +/* +============== +BotGetInitialAttributes +============== +*/ +void BotGetInitialAttributes( bot_state_t *bs ) { + if ( G_IsSinglePlayerGame() ) { + // Default for Allies + if ( bs->mpTeam == TEAM_ALLIES ) { + bs->attribs[BOT_REACTION_TIME] = 0.5f; + bs->attribs[BOT_AIM_ACCURACY] = 0.35f; + bs->attribs[BOT_WIMP_FACTOR] = 0.25f; + + } // if (bs->mp_team == TEAM_ALLIES)... + // Make Nazis wimpier + else + { + bs->attribs[BOT_REACTION_TIME] = 1.5f; + bs->attribs[BOT_AIM_ACCURACY] = 0.2f; + bs->attribs[BOT_WIMP_FACTOR] = 0.25f; + + } // else... + + } // if (G_IsSinglePlayerGame())... + else + { + + + + bs->attribs[BOT_REACTION_TIME] = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1 ); + bs->attribs[BOT_AIM_ACCURACY] = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1 ); + + + + + + // default to not wimpy at all + bs->attribs[BOT_WIMP_FACTOR] = 0.01f; + + } // else... + + +} + + +enum botDefaultKeyEnum +{ + e_BOT_NAME, + e_BOT_REACTION_TIME, + e_BOT_AIM_ACCURACY, + e_BOT_WIMP_FACTOR, + e_SETSPEEDCOEFFICIENT, + e_SETFIRERATE, + e_SETFIRECYCLETIME, + e_SETFIELDOFVIEW, + e_SETHEARINGRANGE, + e_SETCLOSEHEARINGRANGE, + e_SETVISIONRANGE, + e_SETFARSEEINGRANGE, + e_SETMOVEMENTAUTONOMY, + e_SETWEAPONAUTONOMY, + MAX_BOT_DEFAULT_KEYS +}; + + +const char *g_botDefaultKeys[] = +{ + "BOT", + "BOT_REACTION_TIME", + "BOT_AIM_ACCURACY", + "BOT_WIMP_FACTOR", + "SETSPEEDCOEFFICIENT", + "SETFIRERATE", + "SETFIRECYCLETIME", + "SETFIELDOFVIEW", + "SETHEARINGRANGE", + "SETCLOSEHEARINGRANGE", + "SETVISIONRANGE", + "SETFARSEEINGRANGE", + "SETMOVEMENTAUTONOMY", + "SETWEAPONAUTONOMY" +}; + + +// Globally store the bot defaults +BotDefaultAttributes_t g_botDefaultValues[MAX_BOT_DEFAULTS]; + +// Have the default attributes been loaded (only need to do this once!) +qboolean g_loadedDefaultBotAttributes = qfalse; + +// How many bots' default values have we loaded? +int g_botDefaultValueCount = 0; + +// +// ParseBotDefaultAttributes +// +// Description: Read in the default values for AI Characters +// Written: 1/11/2003 +// +void ParseBotDefaultAttributes +( + char *fileName +) { + // Local Variables //////////////////////////////////////////////////////// + + // The text file containing our default bot attributes + fileHandle_t botAttributeFile; + + // Have we found a new bot + qboolean foundBot = qfalse; + + // How many fields have we read for the bot? + int fieldCount = 0; + + // Index for keys + int j; + + // file length + int len; + + // file data + char data[MAX_BOT_DEFAULT_FILE_SIZE]; + char **p, *ptr; + char *token; + + // Level of autonomy setting + int level; + + /////////////////////////////////////////////////////////////////////////// + + // Bail if we've already loaded this + if ( g_loadedDefaultBotAttributes ) { + return; + } + + // Open the text file containing the AI default attributes + len = trap_FS_FOpenFile( fileName, &botAttributeFile, FS_READ ); + + // empty file? + if ( len <= 0 ) { + return; + } + + // too big file + if ( len >= ( sizeof( data ) - 1 ) ) { + Com_Printf( "File %s too long\n", fileName ); + return; + } + + // read all the data into the buffer + trap_FS_Read( data, len, botAttributeFile ); + data[len] = 0; + trap_FS_FCloseFile( botAttributeFile ); + + ptr = data; + p = &ptr; + + + while ( 1 ) + { + // Get the next token + token = COM_ParseExt( p, qtrue ); + + // if no more tokens, we're done looping + if ( !token || token[0] == 0 ) { + break; + } + + // Are we finished? + if ( !Q_stricmp( token, "DONE" ) ) { + // If we'd found a bot already, move on + if ( foundBot ) { + // Report an error if there were not enough fields + if ( fieldCount < MAX_BOT_DEFAULT_KEYS ) { + Com_Printf( "File %s has bot %s without enough fields\n", + fileName, g_botDefaultValues[g_botDefaultValueCount].m_botName ); + + } // if (fieldCount < MAX_BOT_DEFAULT_KEYS)... + + // Go on to the next bot + g_botDefaultValueCount++; + + } // if (foundBot)... + + break; + + } // if (!Q_stricmp(token, "DONE"))... + + + // Check to see if this was a special element + for ( j = e_BOT_NAME; j < MAX_BOT_DEFAULT_KEYS; j++ ) + { + // Does this line match one of our key lines? + if ( !Q_stricmp( token, g_botDefaultKeys[j] ) ) { + // Read the data for this key line + switch ( j ) + { + case e_BOT_NAME: + // If we'd found a bot already, move on + if ( foundBot ) { + // Report an error if there were not enough fields + if ( fieldCount < MAX_BOT_DEFAULT_KEYS ) { + Com_Printf( "File %s has bot %s without enough fields\n", + fileName, g_botDefaultValues[g_botDefaultValueCount].m_botName ); + + } // if (fieldCount < MAX_BOT_DEFAULT_KEYS)... + + // Go on to the next bot + g_botDefaultValueCount++; + + } // if (foundBot)... + + // Read the name of its target + token = COM_ParseExt( p, qtrue ); + strcpy( g_botDefaultValues[g_botDefaultValueCount].m_botName, token ); + + // We found a new bot + foundBot = qtrue; + + // We have one of the fields (name) filled + fieldCount = 1; + + break; + case e_BOT_REACTION_TIME: + // Read entity's name + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_reactionTime = atof( token ); + + // We've found another field + fieldCount++; + + break; + case e_BOT_AIM_ACCURACY: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_aimAccuracy = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_BOT_WIMP_FACTOR: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_wimpFactor = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETSPEEDCOEFFICIENT: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_speedCoefficient = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETFIRERATE: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_fireRate = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETFIRECYCLETIME: + // Read min fire rate + token = COM_ParseExt( p, qtrue ); + + g_botDefaultValues[g_botDefaultValueCount].m_minFireRateCycleTime = + atoi( token ); + + // Read max fire rate + token = COM_ParseExt( p, qtrue ); + + g_botDefaultValues[g_botDefaultValueCount].m_maxFireRateCycleTime = + atoi( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETFIELDOFVIEW: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_scriptedFieldOfView = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETHEARINGRANGE: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_scriptedHearingRange = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETCLOSEHEARINGRANGE: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_closeHearingRange = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETVISIONRANGE: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_visionRange = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETFARSEEINGRANGE: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + g_botDefaultValues[g_botDefaultValueCount].m_farSeeingRange = atof( token ); + + // We've found another field + fieldCount++; + + break; + + case e_SETMOVEMENTAUTONOMY: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + level = BotWeaponAutonomyForString( token ); + g_botDefaultValues[g_botDefaultValueCount].m_movementAutonomy = level; + + // We've found another field + fieldCount++; + + break; + + case e_SETWEAPONAUTONOMY: + // Read entity's classname + token = COM_ParseExt( p, qtrue ); + level = BotMovementAutonomyForString( token ); + g_botDefaultValues[g_botDefaultValueCount].m_weaponAutonomy = level; + + // We've found another field + fieldCount++; + + break; + + }; + + // We found a match, so don't keep checking + break; + + } // if (!Q_stricmp(token, keys[j]))... + + } // for ( j = e_BOT_NAME; j < MAX_BOT_DEFAULT_KEYS; j++)... + + } // while (1) + + // We've loaded this file. Don't do it again + g_loadedDefaultBotAttributes = qtrue; +} +// +// ParseBotDefaultAttributes +// + + + + +// +// BotSetCharacterAttributes +// +// Description: Set attributes for specific characters in code as a default +// these can all be overriden by scripts +// Written: 1/11/2003 +// +void BotSetCharacterAttributes +( + bot_state_t *bs, + + // What default attributes should we use? + BotDefaultAttributes_t *defaults +) { + // Local Variables //////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + // What delay is there before we react to an enemy? + bs->attribs[BOT_REACTION_TIME] = defaults->m_reactionTime; + + // How often do we hit our target? + bs->attribs[BOT_AIM_ACCURACY] = defaults->m_aimAccuracy; + + // How likely are we to run back to the player + bs->attribs[BOT_WIMP_FACTOR] = defaults->m_wimpFactor; + + // How fast do we move? + bs->speedCoefficient = defaults->m_speedCoefficient; + + // What % of the time will we fire? + bs->fireRate = defaults->m_fireRate; + + // Set the fire cycle length + bs->minFireRateCycleTime = defaults->m_minFireRateCycleTime; + bs->maxFireRateCycleTime = defaults->m_maxFireRateCycleTime; + + // What is our scripted FOV? + bs->scriptedFieldOfView = defaults->m_scriptedFieldOfView; + + // What is our scripted hearing range? + bs->scriptedHearingRange = defaults->m_scriptedHearingRange; + + // When an enemy is this close, we can hear them even outside our + // field of view + bs->closeHearingRange = defaults->m_closeHearingRange; + + // How far can this bot see? + bs->visionRange = defaults->m_visionRange; + + // This range is range to which we can see enemies, even if they + // are too far to attack + bs->farSeeingRange = defaults->m_farSeeingRange; + + // How independent? + bs->movementAutonomy = defaults->m_movementAutonomy; + bs->script.movementAutonomy = defaults->m_movementAutonomy; +} +// +// BotSetCharacterAttributes +// + + + + +// +// BotSetUpCharacter +// +// Description: Determine which character this is and set the attributes +// Written: 1/11/2003 +// +void BotSetUpCharacter +( + bot_state_t *bs +) { + // Local Variables //////////////////////////////////////////////////////// + int i; + /////////////////////////////////////////////////////////////////////////// + + // Make sure we've parsed the default attributes file + ParseBotDefaultAttributes( "botfiles\\botAttributes.bot" ); + + // Loop through the loaded bot default attributes and find out if we've got + // one of this character name. + for ( i = 0; i < g_botDefaultValueCount; i++ ) + { + // Is this the bot default to use? + if ( !Q_stricmp( g_entities[bs->client].scriptName, + g_botDefaultValues[i].m_botName ) ) { + // Use these defaults + BotSetCharacterAttributes( bs, &( g_botDefaultValues[i] ) ); + + } // if (!Q_stricmp(g_entities[bs->client].scriptName... + + } // for (i = 0; i < g_botDefaultValueCount; i++)... + + +} +// +// BotSetUpCharacter +// + + + +/* +============== +BotAISetupClient +============== +*/ +int BotAISetupClient( int client, struct bot_settings_s *settings ) { + char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; + bot_state_t *bs; + int errnum, i; + + bs = &botstates[client]; + if ( bs->inuse ) { + BotAI_Print( PRT_FATAL, "client %d already setup\n", client ); + return qfalse; + } + + if ( !trap_AAS_Initialized() ) { + BotAI_Print( PRT_FATAL, "AAS not initialized\n" ); + // return qfalse; + } + + //load the bot character + bs->character = trap_BotLoadCharacter( settings->characterfile, settings->skill ); + if ( !bs->character ) { + BotAI_Print( PRT_FATAL, "couldn't load skill %d from %s\n", settings->skill, settings->characterfile ); + return qfalse; + } + //copy the settings + memcpy( &bs->settings, settings, sizeof( bot_settings_t ) ); + //allocate a goal state + bs->gs = trap_BotAllocGoalState( client ); + //load the item weights + trap_Characteristic_String( bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH ); + errnum = trap_BotLoadItemWeights( bs->gs, filename ); + if ( errnum != BLERR_NOERROR ) { + trap_BotFreeGoalState( bs->gs ); + return qfalse; + } + //allocate a weapon state + bs->ws = trap_BotAllocWeaponState(); + //load the weapon weights + trap_Characteristic_String( bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH ); + errnum = trap_BotLoadWeaponWeights( bs->ws, filename ); + if ( errnum != BLERR_NOERROR ) { + trap_BotFreeGoalState( bs->gs ); + trap_BotFreeWeaponState( bs->ws ); + return qfalse; + } + //allocate a chat state + bs->cs = trap_BotAllocChatState(); + //load the chat file + trap_Characteristic_String( bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH ); + trap_Characteristic_String( bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH ); + errnum = trap_BotLoadChatFile( bs->cs, filename, name ); + if ( errnum != BLERR_NOERROR ) { + trap_BotFreeChatState( bs->cs ); + trap_BotFreeGoalState( bs->gs ); + trap_BotFreeWeaponState( bs->ws ); + return qfalse; + } + //get the gender characteristic + trap_Characteristic_String( bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH ); + //set the chat gender + if ( *gender == 'f' || *gender == 'F' ) { + trap_BotSetChatGender( bs->cs, CHAT_GENDERFEMALE ); + } else if ( *gender == 'm' || *gender == 'M' ) { + trap_BotSetChatGender( bs->cs, CHAT_GENDERMALE ); + } else { trap_BotSetChatGender( bs->cs, CHAT_GENDERLESS );} + + bs->inuse = qtrue; + bs->client = client; + bs->entitynum = client; + bs->setupcount = 4; + + bs->ms = trap_BotAllocMoveState(); + bs->walker = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_WALKER, 0, 1 ); + bs->enemy = -1; + bs->altenemy = -1; + bs->leader = -1; + bs->leader_tagent = -1; + bs->returnToLeader = -1; + bs->isBotSelectable = qtrue; + // setup initial teamplay selections + bs->mpTeam = ( !Q_stricmp( settings->team, "axis" ) ? TEAM_AXIS : TEAM_ALLIES ); + bs->mpClass = BotSuggestClass( bs, bs->mpTeam ); + bs->mpWeapon = BotSuggestWeapon( bs, bs->mpTeam ); + level.clients[bs->client].sess.latchPlayerType = bs->mpClass; // FIXME: better place for this? + level.clients[bs->client].sess.latchPlayerWeapon = bs->mpWeapon; // FIXME: better place for this? + level.clients[bs->client].sess.latchPlayerWeapon2 = 0; // FIXME: better place for this? + // + bs->movementAutonomy = BMA_NOVALUE; + bs->script.movementAutonomy = BMA_NOVALUE; + bs->script.weaponAutonomy = BWA_NOVALUE; + bs->scriptAutonomy = BSA_QUITSCRIPT; + + // TAT 11/14/2002 - no commanded weapon, use whatever you want + bs->commandedWeapon = -1; + +// START Mad Doctor I changes, 8/15/2002 + + // When did we enter the current AI Node? + bs->enteredCurrentAINodeTime = 0; + + // We've not seen an enemy yet + bs->alertState = AISTATE_RELAXED; + + // What is our scripted FOV? + bs->scriptedFieldOfView = 120; + + // What is our scripted hearing range? + bs->scriptedHearingRange = 750; + + // When an enemy is this close, we can hear them even outside our + // field of view + bs->closeHearingRange = 200; + + // What is our scripted speed coefficient + bs->speedCoefficient = 1; + + // Default to a longer vision range for Allied Bots + if ( bs->mpTeam == TEAM_ALLIES ) { + // How far can this bot see? + bs->visionRange = 1200; + + // Allies can see further off + bs->farSeeingRange = 1500; + + } // if (bs->mpTeam == TEAM_ALLIES)... + else + { + // How far can this bot see? + bs->visionRange = 750; + + // Nazis aren't far-sightes + bs->farSeeingRange = bs->visionRange; + + } // else... + + // Not doing any avoid goal yet + bs->avoid_goal.goalEndTime = 0; + + // Default to the scripts not telling us to use a specific weapon + bs->scriptedWeapon = -1; + + // Default to not scritped asleep + bs->scriptedSleep = qfalse; + + // We haven't failed a move yet. + bs->movementFailedBadly = qfalse; + + // We haven't overriden a movetomarker + bs->overrideMovementScripts = qfalse; + + // last time an anim was played using scripting + bs->scriptAnimTime = -1; + + // which anim was last played by the scripts + + + // We'll want to find a new combat spot right away + bs->whenToFindNewCombatSpot = 0; + + // We have no assigned seek cover spot + bs->seekCoverSpot = -1; + + // Are we currently hiding? + bs->amIHiding = qfalse; + + // When should we stop hiding + bs->toggleHidingTime = 0; + + // We have no scripted cover spot yet + bs->scriptedCoverSpot = NULL; + + for ( i = 0; i < MAX_STORED_SEEKCOVERS; i++ ) + bs->lastSeekCoverSpots[i] = -1; + + // We're not in a chain of cover spots yet + bs->coverSpotType = COVER_CHAIN_NONE; + +// END Mad Doctor I changes, 8/15/2002 + + // RF, note: all script variables should exist within bs->script structure + // START xkan, 8/21/2002 + // default to no crouch and no prone + bs->script.flags &= ~BSFL_CROUCH; + bs->script.flags &= ~BSFL_PRONE; + // END xkan, 8/21/2002 + + // Mad Doctor I, 9/19/2002. Init to "not inited"! + bs->weaponnum = -1; + + // + BotAI_SetNumBots( BotAI_GetNumBots() + 1 ); + + //NOTE: reschedule the bot thinking + BotScheduleBotThink(); + // get the attributes + BotGetInitialAttributes( bs ); + // no script data yet + bs->script.data = NULL; + // + + bs->fireRate = 1.0; // xkan, 10/23/2002 - init to normal(maximum) fire rate + + // We can check fire rate at start + bs->fireRateCheckTime = 0; + bs->minFireRateCycleTime = kBOT_MIN_FIRE_CYCLE_TIME; + bs->maxFireRateCycleTime = kBOT_MAX_FIRE_CYCLE_TIME; + + // We haven't arrived at our spot yet + bs->arrived = qfalse; + + return qtrue; +} + +/* +============== +BotAIShutdownClient +============== +*/ +int BotAIShutdownClient( int client ) { + bot_state_t *bs; + + bs = &botstates[client]; + if ( !bs->inuse ) { + // BotAI_Print(PRT_ERROR, "client %d already shutdown\n", client); + return BLERR_AICLIENTALREADYSHUTDOWN; + } + + // close log file + if ( bs->script.logFile && ( bs->script.flags & BSFL_LOGGING ) ) { + bs->script.flags &= ~BSFL_LOGGING; + trap_FS_FCloseFile( bs->script.logFile ); + bs->script.logFile = 0; + } + + trap_BotFreeMoveState( bs->ms ); + //free the goal state + trap_BotFreeGoalState( bs->gs ); + //free the chat file + trap_BotFreeChatState( bs->cs ); + //free the weapon weights + trap_BotFreeWeaponState( bs->ws ); + //free the bot character + trap_BotFreeCharacter( bs->character ); + // + BotFreeWaypoints( bs->checkpoints ); + BotFreeWaypoints( bs->patrolpoints ); + //clear the bot state + memset( bs, 0, sizeof( bot_state_t ) ); + //set the inuse flag to qfalse + bs->inuse = qfalse; + //there's one bot less + BotAI_SetNumBots( BotAI_GetNumBots() - 1 ); + //everything went ok + return BLERR_NOERROR; +} + +/* +============== +BotResetState + +called when a bot enters the intermission or observer mode and +when the level is changed +============== +*/ +void BotResetState( bot_state_t *bs ) { + int client, entitynum, inuse; + int movestate, goalstate, chatstate, weaponstate; + bot_settings_t settings; + int character; + playerState_t ps; //current player state +// float entergame_time; + + //save some things that should not be reset here + memcpy( &settings, &bs->settings, sizeof( bot_settings_t ) ); + memcpy( &ps, &bs->cur_ps, sizeof( playerState_t ) ); + inuse = bs->inuse; + client = bs->client; + entitynum = bs->entitynum; + character = bs->character; + movestate = bs->ms; + goalstate = bs->gs; + chatstate = bs->cs; + weaponstate = bs->ws; + + //free checkpoints and patrol points + BotFreeWaypoints( bs->checkpoints ); + BotFreeWaypoints( bs->patrolpoints ); + //reset the whole state + memset( bs, 0, sizeof( bot_state_t ) ); + //copy back some state stuff that should not be reset + bs->ms = movestate; + bs->gs = goalstate; + bs->cs = chatstate; + bs->ws = weaponstate; + memcpy( &bs->cur_ps, &ps, sizeof( playerState_t ) ); + memcpy( &bs->settings, &settings, sizeof( bot_settings_t ) ); + bs->inuse = inuse; + bs->client = client; + bs->entitynum = entitynum; + bs->character = character; + + //reset several states + if ( bs->ms ) { + trap_BotResetMoveState( bs->ms ); + } + if ( bs->gs ) { + trap_BotResetGoalState( bs->gs ); + } + if ( bs->ws ) { + trap_BotResetWeaponState( bs->ws ); + } + if ( bs->gs ) { + trap_BotResetAvoidGoals( bs->gs ); + } + if ( bs->ms ) { + trap_BotResetAvoidReach( bs->ms ); + } + + // Start TAT 9/27/2002 + // We should make sure the returnToLeader is -1, meaning no one + bs->returnToLeader = -1; + // End TAT 9/27/2002 +} + +/* +============== +BotAILoadMap +============== +*/ +int BotAILoadMap( int restart ) { + int i; + vmCvar_t mapname; + + if ( !restart ) { + trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); + trap_BotLibLoadMap( mapname.string ); + } else { + // NULL mapname is a restart + trap_BotLibLoadMap( NULL ); + } + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( botstates[i].inuse ) { + BotResetState( &botstates[i] ); + botstates[i].setupcount = 4; + } + } + + BotSetupDeathmatchAI(); + + BotSpawnSpecialEntities(); + + trap_BotLibStartFrame( (float) level.time / 1000 ); + + return BLERR_NOERROR; +} + + + +/* +================== +BotPreProcessAI + +Perform Pre-processing steps on the bots and entities +================== +*/ +void BotPreProcessAI() { +} + +int botTime_EmergencyGoals; +int botTime_FindGoals; +int botTime_FindEnemy; + +/* +================== +BotAIThinkFrame +================== +*/ +int BotAIThinkFrame( int time ) { + int i; + int elapsed_time, thinktime, thinkcount, lastthinkbot, botcount; + static int local_time; + static int botlib_residual; + static int lastbotthink_time; + static int lastbot; + int startTime /*, totalProfileTime*/; + if ( bot_profile.integer == 1 ) { + startTime = trap_Milliseconds(); + } + + trap_Cvar_Update( &bot_rocketjump ); + trap_Cvar_Update( &bot_grapple ); + trap_Cvar_Update( &bot_fastchat ); + trap_Cvar_Update( &bot_nochat ); + trap_Cvar_Update( &bot_testrchat ); + trap_Cvar_Update( &bot_thinktime ); + trap_Cvar_Update( &bot_profile ); + // Ridah, set the default AAS world + trap_AAS_SetCurrentWorld( 0 ); + trap_Cvar_Update( &memorydump ); + + botTime_EmergencyGoals = 0; + botTime_FindGoals = 0; + botTime_FindEnemy = 0; + + if ( memorydump.integer ) { + trap_BotLibVarSet( "memorydump", "1" ); + trap_Cvar_Set( "memorydump", "0" ); + } + + //if the bot think time changed we should reschedule the bots + if ( bot_thinktime.integer != lastbotthink_time ) { + lastbotthink_time = bot_thinktime.integer; + BotScheduleBotThink(); + } + + elapsed_time = time - local_time; + local_time = time; + + botlib_residual += elapsed_time; + + if ( elapsed_time > bot_thinktime.integer ) { + thinktime = elapsed_time; + } else { + thinktime = bot_thinktime.integer; + } + + thinkcount = 0; + lastthinkbot = lastbot; + + // execute scheduled bot AI + for ( i = lastbot + 1, botcount = 0; botcount < MAX_CLIENTS; i++, botcount++ ) { + // If we've gone past the end of the list, start over + if ( i >= MAX_CLIENTS ) { + // Start back at the beginning of the list + i = 0; + + // Perform Pre-processing steps on the bots and entities + BotPreProcessAI(); + } + + if ( !botstates[i].inuse ) { + continue; + } + + botstates[i].botthink_residual += elapsed_time + ( rand() % ( bot_thinktime.integer / 4 ) ); // randomize the times a bit so they are more likely to disperse amongst frames + + if ( botstates[i].botthink_residual >= thinktime * ( ( VectorLengthSquared( botstates[i].cur_ps.velocity ) < SQR( 10 ) ) ? 2 : 1 ) ) { + botstates[i].botthink_residual -= thinktime * ( ( VectorLengthSquared( botstates[i].cur_ps.velocity ) < SQR( 10 ) ) ? 2 : 1 ); + if ( botstates[i].botthink_residual > thinktime ) { + botstates[i].botthink_residual = thinktime; + } + +/* if (!trap_AAS_Initialized()) { + return BLERR_NOERROR; + }*/ + + if ( g_entities[i].client->pers.connected == CON_CONNECTED ) { + BotAI( i, thinktime / 1000.f ); + BotUpdateInput( &botstates[i], time ); + trap_BotUserCommand( botstates[i].client, &botstates[i].lastucmd ); + // + lastthinkbot = i; + } + + thinkcount++; + +/* if(thinkcount >= 4) { + break; + }*/ + } + } + + lastbot = lastthinkbot; + +/* if( bot_profile.integer == 1 ) { + totalProfileTime = trap_Milliseconds() - startTime; + G_Printf( "BotAIThinkFrame: %4i total (%4i em, %4i fg, %4i en) thinkcount: %i\n", totalProfileTime, botTime_EmergencyGoals, botTime_FindGoals, botTime_FindEnemy, thinkcount ); + }*/ + + return BLERR_NOERROR; +} + +/* +================== +BotAIStartFrame +================== +*/ +int BotAIStartFrame( int time ) { + int i; + gentity_t *ent; + bot_entitystate_t state; + //entityState_t entitystate; + //vec3_t mins = {-15, -15, -24}, maxs = {15, 15, 32}; + int elapsed_time, thinktime; + static int local_time; + static int botlib_residual; + static int lastbotthink_time; + + G_CheckBotSpawn(); + + trap_Cvar_Update( &bot_rocketjump ); + trap_Cvar_Update( &bot_grapple ); + trap_Cvar_Update( &bot_fastchat ); + trap_Cvar_Update( &bot_nochat ); + trap_Cvar_Update( &bot_testrchat ); + trap_Cvar_Update( &bot_thinktime ); + // Ridah, set the default AAS world + trap_AAS_SetCurrentWorld( 0 ); + trap_Cvar_Update( &memorydump ); + + if ( memorydump.integer ) { + trap_BotLibVarSet( "memorydump", "1" ); + trap_Cvar_Set( "memorydump", "0" ); + } + + //if the bot think time changed we should reschedule the bots + if ( bot_thinktime.integer != lastbotthink_time ) { + lastbotthink_time = bot_thinktime.integer; + BotScheduleBotThink(); + } + + elapsed_time = time - local_time; + local_time = time; + + botlib_residual += elapsed_time; + + if ( elapsed_time > bot_thinktime.integer ) { + thinktime = elapsed_time; + } else { + thinktime = bot_thinktime.integer; + } + + BotCountLandMines(); + + // update the bot library + if ( botlib_residual >= thinktime ) { + botlib_residual -= thinktime; + + trap_BotLibStartFrame( (float) time / 1000 ); + + // Ridah, only check the default world + trap_AAS_SetCurrentWorld( 0 ); + + if ( !trap_AAS_Initialized() ) { + return BLERR_NOERROR; + } + + //update entities in the botlib + for ( i = 0; i < level.num_entities; i++ ) { + // Ridah - WOLF, we only need client entity information + //if (i > level.maxclients) { + // break; + //} + + ent = &g_entities[i]; + if ( !ent->inuse ) { + continue; + } + if ( !ent->r.linked ) { + continue; + } + if ( ent->r.svFlags & SVF_NOCLIENT ) { + continue; + } + + memset( &state, 0, sizeof( bot_entitystate_t ) ); + + VectorCopy( BotGetOrigin( i ), state.origin ); + + if ( !VectorCompare( ent->r.currentAngles, vec3_origin ) ) { + VectorCopy( ent->r.currentAngles, state.angles ); + } else if ( ent->client ) { + VectorCopy( ent->client->ps.viewangles, state.angles ); + } else { + VectorCopy( ent->s.angles, state.angles ); + } + + VectorCopy( ent->s.origin2, state.old_origin ); + VectorCopy( ent->r.mins, state.mins ); + VectorCopy( ent->r.maxs, state.maxs ); + state.type = ent->s.eType; + state.flags = ent->s.eFlags; + + if ( ent->r.bmodel ) { + state.solid = SOLID_BSP; + } else { + state.solid = SOLID_BBOX; + } + + state.groundent = ent->s.groundEntityNum; + state.modelindex = ent->s.modelindex; + state.modelindex2 = ent->s.modelindex2; + state.frame = ent->s.frame; + state.powerups = ent->s.powerups; + state.legsAnim = ent->s.legsAnim; + state.torsoAnim = ent->s.torsoAnim; + state.weapon = ent->s.weapon; + + trap_BotLibUpdateEntity( i, &state ); + } + + BotAIRegularUpdate(); + + } + +#ifndef NO_BOT_SUPPORT + // let bots do their thinking (only if this is dedicated, or the local client hasnt processed bot thinks in a while) + if ( bot_enable.integer && ( g_dedicated.integer || ( level.lastClientBotThink < level.time - 200 ) ) ) { + BotAIThinkFrame( level.time ); + } +#endif // NO_BOT_SUPPORT + +// Since there is no interpolation on the client-side anymore, there is no need to move the bot at a set time-interval anymore +/* + // execute bot user commands every frame + for( i = 0; i < MAX_CLIENTS; i++ ) { + if( !botstates[i].inuse ) { + continue; + } + if( g_entities[i].client->pers.connected != CON_CONNECTED ) { + continue; + } + + BotUpdateInput( &botstates[i], time ); + trap_BotUserCommand(botstates[i].client, &botstates[i].lastucmd); + } +*/ + return BLERR_NOERROR; +} + +/* +============== +BotInitLibrary +============== +*/ +int BotInitLibrary( void ) { + char buf[144]; + + //set the maxclients and maxentities library variables before calling BotSetupLibrary + trap_Cvar_VariableStringBuffer( "sv_maxclients", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "8" ); + } + trap_BotLibVarSet( "maxclients", buf ); + Com_sprintf( buf, sizeof( buf ), "%d", MAX_GENTITIES ); + trap_BotLibVarSet( "maxentities", buf ); + //bsp checksum + trap_Cvar_VariableStringBuffer( "sv_mapChecksum", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "sv_mapChecksum", buf ); + } + //maximum number of aas links + trap_Cvar_VariableStringBuffer( "max_aaslinks", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "max_aaslinks", buf ); + } + //maximum number of items in a level + trap_Cvar_VariableStringBuffer( "max_levelitems", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "max_levelitems", buf ); + } + //automatically launch WinBSPC if AAS file not available + trap_Cvar_VariableStringBuffer( "autolaunchbspc", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "autolaunchbspc", "1" ); + } + // + trap_Cvar_VariableStringBuffer( "g_gametype", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "0" ); + } + trap_BotLibVarSet( "g_gametype", buf ); + + trap_Cvar_VariableStringBuffer( "bot_developer", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "0" ); + } + trap_BotLibVarSet( "bot_developer", buf ); + //log file + trap_Cvar_VariableStringBuffer( "bot_developer", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "0" ); + } + trap_BotLibVarSet( "log", buf ); + //no chatting + trap_Cvar_VariableStringBuffer( "bot_nochat", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "nochat", "0" ); + } + //forced clustering calculations + trap_Cvar_VariableStringBuffer( "forceclustering", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "forceclustering", buf ); + } + //forced reachability calculations + trap_Cvar_VariableStringBuffer( "forcereachability", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "forcereachability", buf ); + } + //force writing of AAS to file + trap_Cvar_VariableStringBuffer( "forcewrite", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "forcewrite", buf ); + } + //no AAS optimization + trap_Cvar_VariableStringBuffer( "nooptimize", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "nooptimize", buf ); + } + //number of reachabilities to calculate each frame + trap_Cvar_VariableStringBuffer( "framereachability", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "20" ); + } + trap_BotLibVarSet( "framereachability", buf ); + // + trap_Cvar_VariableStringBuffer( "bot_reloadcharacters", buf, sizeof( buf ) ); + if ( !strlen( buf ) ) { + strcpy( buf, "0" ); + } + trap_BotLibVarSet( "bot_reloadcharacters", buf ); + //base directory + trap_Cvar_VariableStringBuffer( "fs_basepath", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "basedir", buf ); + } + //game directory + trap_Cvar_VariableStringBuffer( "fs_game", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "gamedir", buf ); + } + //cd directory + trap_Cvar_VariableStringBuffer( "fs_cdpath", buf, sizeof( buf ) ); + if ( strlen( buf ) ) { + trap_BotLibVarSet( "cddir", buf ); + } + //setup the bot library + return trap_BotLibSetup(); +} + +/* +============== +BotAISetup +============== +*/ +int BotAISetup( int restart ) { + int errnum; + +#ifdef RANDOMIZE + srand( (unsigned)time( NULL ) ); +#endif //RANDOMIZE + + trap_Cvar_Register( &bot_verbose, "bot_verbose", "0", 0 ); + trap_Cvar_Register( &bot_thinktime, "bot_thinktime", "50", 0 ); + trap_Cvar_Register( &bot_profile, "bot_profile", "0", 0 ); + trap_Cvar_Register( &memorydump, "memorydump", "0", 0 ); + trap_Cvar_Register( &bot_findgoal, "bot_findgoal", "0", 0 ); + + //if the game is restarted for a tournament + if ( restart ) { + return BLERR_NOERROR; + } + + //initialize the bot states + memset( botstates, 0, sizeof( botstates ) ); + + errnum = BotInitLibrary(); + if ( errnum != BLERR_NOERROR ) { + return qfalse; + } + return BLERR_NOERROR; +} + +/* +============== +BotAIShutdown +============== +*/ +int BotAIShutdown( int restart ) { + + int i; + + //if the game is restarted for a tournament + if ( restart ) { + //shutdown all the bots in the botlib + for ( i = 0; i < level.numConnectedClients; i++ ) { + if ( botstates[level.sortedClients[i]].inuse ) { + BotAIShutdownClient( botstates[level.sortedClients[i]].client ); + } + } + //don't shutdown the bot library + } else { + trap_BotLibShutdown(); + } + return qtrue; +} + +//==================================================== +// BOT GAME ENTITIES +// +// Used for point entities that dont need to be sitting +// in the global list of entities, wasting valuable slots. + +// TAT 11/13/2002 NUM_BOTGAMEENTITIES defined in botlib.h +/* +gentity_t botGameEntities[NUM_BOTGAMEENTITIES]; +int numBotGameEntities; + +// TAT - bot_mg42_spot works, if we want to use this system +char *botGameEntityNames[] = { +// "ai_marker", +// "bot_sniper_spot", +// "bot_mg42_spot", +// "bot_seek_cover_spot", + NULL +}; + +void BotInitBotGameEntities(void) { + memset( botGameEntities, 0, sizeof(botGameEntities) ); + numBotGameEntities = 0; +} + +gentity_t *BotSpawnGameEntity(void) { + if (numBotGameEntities >= NUM_BOTGAMEENTITIES) return NULL; + // + botGameEntities[numBotGameEntities].s.number = MAX_GENTITIES + numBotGameEntities; + botGameEntities[numBotGameEntities].inuse = qtrue; + return &botGameEntities[numBotGameEntities++]; +} + +// TAT 11/12/2002 - we're going to want to check that something is from this list +// first thing when we spawn it +// And this returns a ptr to the new entity or to the old one if it isn't from our list +gentity_t *BotCheckBotGameEntity( gentity_t *ent ) +{ + int i; + gentity_t *botent; + int num; + + if (!ent->classname) return ent; + + // is this a bot game entity? + for (i=0; botGameEntityNames[i]; i++) { + if (!Q_stricmp( botGameEntityNames[i], ent->classname )) { + botent = BotSpawnGameEntity(); + if (!botent) { + G_Error( "BotCheckBotGameEntity: exceeded NUM_BOTGAMEENTITIES (%i)", NUM_BOTGAMEENTITIES ); + } + num = botent->s.number; + memcpy( botent, ent, sizeof(gentity_t) ); + botent->s.number = num; + // free the entity + G_FreeEntity( ent ); + + // in our list, return the new entity + return botent; + } + } + + // not in our list + return ent; +} +*/ + +gentity_t *BotFindEntity( gentity_t *from, int fieldofs, char *match ) { + return G_Find( from, fieldofs, match ); +} + + +/* +================= +FindBotByName +================= + +Get the bot state of a named bot + +*/ +bot_state_t *FindBotByName +( + // Name of the bot to look up + char * botName +) { + // Pointer to another bot that might be specified in the scripting + bot_state_t *otherBot = NULL; + + // Index for looping through bots + int botNum = 0; + + // Loop through all the bots + for ( botNum = 0; botNum < level.maxclients; botNum++ ) + { + // RF, make sure it's still in use + if ( !botstates[botNum].inuse ) { + continue; + } + + // Grab the next bot to check its name + otherBot = &botstates[botNum]; + + // Does this bot have the right name? + if ( !Q_stricmp( g_entities[otherBot->client].scriptName, botName ) ) { + // This is our bot, send it up the chain! + return otherBot; + + } // if (!Q_stricmp(g_entities[bs->client].scriptName, botName))... + + } // for (botNum=0; botNum= MAX_GENTITIES ) ) { + G_Printf( "^1BotGetEntity: Invalid entityNum\n" ); + return NULL; + } +#endif // _DEBUG + + return &g_entities[entityNum]; +} + +//==================================================== +// BOT STATIC ENTITY CACHE +// +// Used to speed up searching of common entities +// !!! NOTE: must be in synch with enum list in ai_main.h + +char *botStaticEntityStrings[NUM_BOTSTATICENTITY] = { + "team_WOLF_checkpoint", + "trigger_flagonly", + "misc_mg42", + "trigger_objective_info", + "team_CTF_redflag", + "team_CTF_blueflag", + "func_explosive", + "func_door", + "func_door_rotating", + "func_constructible", + "trigger_multiple", + "trigger_flagonly_multiple", + "bot_landmine_area", + "bot_attractor", + "bot_sniper_spot", + "bot_landminespot_spot", +}; + +gentity_t *botStaticEntityList[NUM_BOTSTATICENTITY]; + +/* +=============== +BotBuildStaticEntityCache +=============== +*/ +void BotBuildStaticEntityCache( void ) { + int i; + gentity_t *trav, *p; + // + memset( botStaticEntityList, 0, sizeof( botStaticEntityList ) ); + // + for ( i = 0; i < NUM_BOTSTATICENTITY; i++ ) { + trav = NULL; + while ( ( trav = G_Find( trav, FOFS( classname ), botStaticEntityStrings[i] ) ) ) { + trav->botNextStaticEntity = NULL; + p = botStaticEntityList[i]; + if ( !p ) { + botStaticEntityList[i] = trav; + } else { // add trav to the end of the list + while ( p->botNextStaticEntity ) p = p->botNextStaticEntity; + p->botNextStaticEntity = trav; + } + } + } + // + level.initStaticEnts = qtrue; +} + +/* +================ +BotFindNextStaticEntity +================ +*/ +gentity_t *BotFindNextStaticEntity( gentity_t *start, botStaticEntityEnum_t entityEnum ) { + gentity_t *trav; + + // Gordon: give stuff time to spawn, just in case + if ( level.time - level.startTime < FRAMETIME * 5 ) { + return NULL; + } + + if ( !level.initStaticEnts ) { + BotBuildStaticEntityCache(); + } + + trav = botStaticEntityList[entityEnum]; + while ( trav && start && trav->s.number <= start->s.number ) { + trav = trav->botNextStaticEntity; + } + + return trav; +} + +/* +=============== +BotFindEntityForName +=============== +*/ +gentity_t *BotFindEntityForName( char *name ) { + gentity_t *trav; + int i; + + for ( trav = g_entities, i = 0; i < level.maxclients; i++, trav++ ) { + if ( !trav->inuse ) { + continue; + } + if ( !trav->client ) { + continue; + } + if ( !trav->aiName ) { + continue; + } + if ( Q_stricmp( trav->aiName, name ) ) { + continue; + } + return trav; + } + return NULL; +} + +/* +================ +BotSinglePlayer +================ +*/ +qboolean BotSinglePlayer() { + if ( g_gametype.integer == GT_SINGLE_PLAYER ) { + return qtrue; + } + // + return qfalse; +} + +/* +================ +BotSinglePlayer +================ +*/ +qboolean BotCoop() { + if ( g_gametype.integer == GT_COOP ) { + return qtrue; + } + // + return qfalse; +} + + +/* +=============== +G_SetAASBlockingEntity + + Adjusts routing so AI knows it can't move through this entity +=============== +*/ +void G_SetAASBlockingEntity( gentity_t *ent, int blocking ) { + // Gordon: short circuit this as we dont need it + return; + + if ( blocking > 0 ) { + // always add first flag if we are not clearing the flags + blocking |= 1; + } + + // if we are not blocking now, and we were previously, always use the same box for removing the blocking areas + if ( !( blocking & 1 ) ) { + if ( ent->AASblocking & 1 ) { + // turn off old blocking areas + trap_AAS_SetAASBlockingEntity( ent->AASblocking_mins, ent->AASblocking_maxs, qfalse ); + ent->AASblocking = qfalse; + } + // we're done + return; + } + + // if we are currently blocking, and we are being asked to block again, but with a different box, turn off previous blocking + if ( blocking & 1 && ent->AASblocking & 1 && ( !VectorCompare( ent->r.absmin, ent->AASblocking_mins ) || !VectorCompare( ent->r.absmax, ent->AASblocking_maxs ) ) ) { + // turn off old blocking areas + trap_AAS_SetAASBlockingEntity( ent->AASblocking_mins, ent->AASblocking_maxs, qfalse ); + } + + // if we are blocking now and before, and the bounds are the same, ignore + if ( ( blocking == ent->AASblocking ) && ( VectorCompare( ent->r.absmin, ent->AASblocking_mins ) && VectorCompare( ent->r.absmax, ent->AASblocking_maxs ) ) ) { + return; + } + + // + // determine mover status + // + if ( ent->s.eType == ET_EXPLOSIVE ) { + blocking |= BLOCKINGFLAG_MOVER; + } else if ( ent->s.eType == ET_CONSTRUCTIBLE && !( ent->spawnflags & 1024 ) ) { + if ( ent->spawnflags & 128 ) { // must be blocking + if ( !( ent->spawnflags & 512 ) ) { // not ignored by AAS + blocking |= BLOCKINGFLAG_MOVER; + } + } + } + + // + // set the new blocking + // + ent->AASblocking = ( blocking & ~BLOCKINGFLAG_MOVER ); + trap_AAS_SetAASBlockingEntity( ent->r.absmin, ent->r.absmax, blocking ); + if ( blocking ) { + VectorCopy( ent->r.absmin, ent->AASblocking_mins ); + VectorCopy( ent->r.absmax, ent->AASblocking_maxs ); + } +} + +/* +=================== +BotRecordTeamDeath + + Allows the AAS to make dangerous routes more costly, therefore bots will avoid them when possible +=================== +*/ +void BotRecordTeamDeath( int client ) { + vec3_t org; + int area, travelflags, teamCount, team; + // +//RF, disabling for now, seems to cause routing loops + return; + + if ( G_IsSinglePlayerGame() ) { + return; + } + // + VectorCopy( BotGetOrigin( client ), org ); + area = BotGetArea( client ); + if ( !area ) { + return; + } + travelflags = BotTravelFlagsForClient( client ); + team = level.clients[client].sess.sessionTeam; + teamCount = TeamCount( -1, team ); + // + trap_AAS_RecordTeamDeathArea( org, area, team, teamCount, travelflags ); +} + + +/* +=============== +BotRecordAttack + + src has just attacked dest +=============== +*/ +void BotRecordAttack( int src, int dest ) { + g_entities[dest].botLastAttackedTime = level.time; + g_entities[dest].botLastAttackedEnt = src; +} + + +// START - Mad Doc - TDf +/* +=============== +BotDebug + + fills the appropriate cvars for showing a bot's "thought bubble" +=============== +*/ +void BotDebug( int clientNum ) { + char buf[256]; + + // get the botstate + bot_state_t *bs; + + bs = &botstates[clientNum]; + + if ( bs->inuse ) { + // TAT - print more detailed info for follow leader + if ( bs->leader > -1 ) { + trap_Cvar_Set( "bot_debug_curAINode", va( "%s: leader = %i tagent = %i", bs->ainodeText, bs->leader, bs->leader_tagent ) ); + } else { + trap_Cvar_Set( "bot_debug_curAINode", bs->ainodeText ); + } + switch ( bs->alertState ) + { + case AISTATE_RELAXED: + trap_Cvar_Set( "bot_debug_alertState", "RELAXED" ); + break; + case AISTATE_QUERY: + trap_Cvar_Set( "bot_debug_alertState", "QUERY" ); + break; + case AISTATE_ALERT: + trap_Cvar_Set( "bot_debug_alertState", "ALERT" ); + break; + case AISTATE_COMBAT: + trap_Cvar_Set( "bot_debug_alertState", "COMBAT" ); + break; + default: + trap_Cvar_Set( "bot_debug_alertState", "ERROR bad state" ); + break; + } + + { + playerState_t *ps = &bs->cur_ps; + animModelInfo_t *animModelInfo = BG_GetCharacterForPlayerstate( ps )->animModelInfo; + trap_Cvar_Set( "bot_debug_anim", va( "leg-%s torso-%s", + animModelInfo->animations[ps->legsAnim & ~ANIM_TOGGLEBIT]->name, + animModelInfo->animations[ps->torsoAnim & ~ANIM_TOGGLEBIT]->name ) ); + } + + trap_Cvar_Set( "bot_debug_pos", ( va( "(%f,%f,%f)", bs->origin[0], bs->origin[1], bs->origin[2] ) ) ); + + // curr script function handled differently, so nothing here about it + + Com_sprintf( buf, sizeof( buf ), "%i", BotGetMovementAutonomyLevel( bs ) ); + trap_Cvar_Set( "bot_debug_moveAut", buf ); + + // TAT 12/9/2002 - Throwing some extra info into the cover spot display + { + g_serverEntity_t *coverSpot = GetServerEntity( bs->seekCoverSpot ); + Com_sprintf( buf, sizeof( buf ), "%i(%s) Enemy = %i", bs->seekCoverSpot, coverSpot ? coverSpot->name : "", bs->enemy ); + trap_Cvar_Set( "bot_debug_cover_spot", buf ); + } + } else + { + trap_Cvar_Set( "bot_debug_curAINode", "NULL" ); + trap_Cvar_Set( "bot_debug_alertState", "NULL" ); + trap_Cvar_Set( "bot_debug_pos", "(--,--,--)" ); + trap_Cvar_Set( "bot_debug_scriptFunc", "NULL" ); + trap_Cvar_Set( "bot_debug_weapAut", "NULL" ); + trap_Cvar_Set( "bot_debug_moveAut", "NULL" ); + trap_Cvar_Set( "bot_debug_cover_spot", "NULL" ); + trap_Cvar_Set( "bot_debug_anim", "NULL" ); + } +} + + +// Mad Doc - TDF +/* +=============== +GetBotAutonomies + + stuffs the parms with the appropriate data +=============== +*/ +void GetBotAutonomies( int clientNum, int *weapAutonomy, int *moveAutonomy ) { + // get the botstate + bot_state_t *bs; + + bs = &botstates[clientNum]; + + if ( bs->inuse ) { + // use +1 so we can have 0 mean not found + *moveAutonomy = BotGetMovementAutonomyLevel( bs ) + 1; + } else { + *moveAutonomy = 0; + } +} + + +// Mad Doc - TDF +/* +=============== +GetBotAmmo + + stuffs the parms with the appropriate data +=============== +*/ +void GetBotAmmo( int clientNum, int *weapon, int *ammo, int *ammoclip ) { + gentity_t *ent; + + ent = &g_entities[clientNum]; + + *weapon = ent->client->ps.weapon; + *ammo = ent->client->ps.ammo[BG_FindAmmoForWeapon( *weapon )]; + *ammoclip = ent->client->ps.ammoclip[BG_FindClipForWeapon( *weapon )]; +} + +// END Mad Doc - TDF + +// xkan - sets the ideal view angles +void BotSetIdealViewAngles( int clientNum, vec3_t angle ) { + // get the botstate + bot_state_t *bs; + + bs = &botstates[clientNum]; + if ( bs->inuse ) { + VectorCopy( angle, bs->ideal_viewangles ); + } +} + +// TAT 1/14/2003 - init the bot's movement autonomy pos to it's current position +void BotInitMovementAutonomyPos( gentity_t *bot ) { + bot_state_t* bs = &botstates[bot->s.number]; + + if ( bs->inuse ) { + // TAT 1/14/2003 - set the autonomy position to the current position + VectorCopy( bot->client->ps.origin, bs->script.movementAutonomyPos ); + VectorCopy( bot->client->ps.origin, bs->movementAutonomyPos ); + } +} + +/* +=================== +BotDebugViewClient +=================== +*/ +void BotDebugViewClient( int client ) { + static int lastChange; + if ( bot_debug.integer != 10 ) { + return; + } + if ( !g_cheats.integer ) { + return; + } + if ( lastChange < level.time && lastChange > level.time - 5000 ) { + return; + } + if ( !level.clients[0].pers.connected == CON_CONNECTED ) { + return; + } + if ( g_entities[0].r.svFlags & SVF_BOT ) { + return; + } + if ( level.clients[0].sess.sessionTeam != TEAM_SPECTATOR ) { + return; + } + // + level.clients[0].sess.spectatorClient = client; +} diff --git a/src/botai/ai_main.h b/src/botai/ai_main.h new file mode 100644 index 0000000..76ff609 --- /dev/null +++ b/src/botai/ai_main.h @@ -0,0 +1,941 @@ +/* +=========================================================================== + +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: ai_main.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +//#define DEBUG +#define CTF + +// Uncomment the next line to get rid of massve repeat scrolling +// AI errors +//#define DONT_PRINT_REPEATED_AI_ERRORS + +#define BOT_DEBUG_FOLLOW_PLAYER 11 + +// Mad Doctor I, 9/1/2002. Get bots speeds tweaked +#define NORMALIZED_MOVEMENT_SPEED 255 + +#define MAX_ITEMS 256 +#define MAX_BOTAIWAYPOINTS 128 + +//bot flags +#define BFL_STRAFERIGHT 1 //strafe to the right +#define BFL_ATTACKED 2 //bot has attacked last ai frame +#define BFL_ATTACKJUMPED 4 //bot jumped during attack last frame +#define BFL_AIMATENEMY 8 //bot aimed at the enemy this frame +#define BFL_AVOIDRIGHT 16 //avoid obstacles by going to the right +#define BFL_IDEALVIEWSET 32 //bot has ideal view angles set +#define BFL_MISCFLAG 64 // different uses for each ainode +#define BFL_DISMOUNT_MG42 128 //wanting to dismount mg42 +#define BFL_SPRINT 256 // sprint +#define BFL_FIXED_MOVEMENT_AUTONOMY 512 // player has set movement autonomy +#define BFL_FIXED_WEAPON_AUTONOMY 1024 // player has set weapon autonomy +#define BFL_BATTLE_MODE 2048 // bot is in combat mode +#define BFL_SNIPING 4096 // bot is sniping +#define BFL_SCRIPTED_LEADER 8192 // leader is enforce via scripting +//long term goal types +#define LTG_TEAMHELP 1 //help a team mate +#define LTG_TEAMACCOMPANY 2 //accompany a team mate +#define LTG_DEFENDKEYAREA 3 //defend a key area +#define LTG_GETFLAG 4 //get the enemy flag +#define LTG_RUSHBASE 5 //rush to the base +#define LTG_RETURNFLAG 6 //return the flag +#define LTG_CAMP 7 //camp somewhere +#define LTG_CAMPORDER 8 //ordered to camp somewhere +#define LTG_PATROL 9 //patrol +#define LTG_GETITEM 10 //get an item +#define LTG_KILL 11 //kill someone +//some goal dedication times +#define TEAM_HELP_TIME 60 //1 minute teamplay help time +#define TEAM_ACCOMPANY_TIME 600 //10 minutes teamplay accompany time +#define TEAM_DEFENDKEYAREA_TIME 240 //4 minutes ctf defend base time +#define TEAM_CAMP_TIME 600 //10 minutes camping time +#define TEAM_PATROL_TIME 600 //10 minutes patrolling time +#define TEAM_LEAD_TIME 600 //10 minutes taking the lead +#define TEAM_GETITEM_TIME 60 //1 minute +#define TEAM_KILL_SOMEONE 180 //3 minute to kill someone +#define CTF_GETFLAG_TIME 240 //4 minutes ctf get flag time +#define CTF_RUSHBASE_TIME 120 //2 minutes ctf rush base time +#define CTF_RETURNFLAG_TIME 180 //3 minutes to return the flag +#define CTF_ROAM_TIME 60 //1 minute ctf roam time +//patrol flags +#define PATROL_LOOP 1 +#define PATROL_REVERSE 2 +#define PATROL_BACK 4 +//copied from the aas file header +#define PRESENCE_NONE 1 +#define PRESENCE_NORMAL 2 +#define PRESENCE_CROUCH 4 +//RF, misc defines +#define BOT_FLAG_CARRIER_DEFENDERS 3 +#define MAX_BOTLEADER_DIST 2048 +#define MAX_BOTLEADER_TRAVEL 1000 + +// group formations +#define BOT_FORM_SINGLEFILE 0 +#define BOT_FORM_DOUBLEFILE 1 +#define BOT_FORM_PATROL_LINE 2 +#define BOT_FORM_SEEK_COVER 3 +#define BOT_FORM_NUM 4 + +#define BOT_FOLLOW_BEHIND 0 +#define BOT_FOLLOW_LEFT 1 +#define BOT_FOLLOW_RIGHT 2 +#define BOT_FOLLOW_NUM 3 + +struct bot_state_s; + +//check points +typedef struct bot_waypoint_s +{ + int inuse; + char name[32]; + bot_goal_t goal; + struct bot_waypoint_s *next, *prev; +} bot_waypoint_t; + +#define BWPOFS( x ) ( (int)&( ( (bot_waypoint_t *)0 )->x ) ) + +#define MAX_VCHATS 16 + +typedef struct +{ + int time; + int id; + int client; // who sent it + int mode; +} bot_chat_t; + +//----------------------------------------------------------------------------------------- +// scripting +// +// defines +#define BOT_MAX_SCRIPT_ITEMS 512 // per character +#define BOT_MAX_SCRIPT_EVENTS 128 // per character +#define BOT_SIZE_STRING_POOL 16384 +// +// flags +#define BSFL_FIRST_CALL 1 +#define BSFL_LOGGING 2 +#define BSFL_MOUNT_MG42 4 +#define BSFL_FORCED_MOVEMENT_AUTONOMY 8 // scripting has enforced movement autonomy +#define BSFL_FORCED_WEAPON_AUTONOMY 16 // scripting has enforced weapon autonomy +// START xkan, 8/22/2002 +// flags that make bots crouch/prone through scripts +#define BSFL_CROUCH 32 +#define BSFL_PRONE 64 +//#define BSFL_TALK 128 // flag to make bot play talk animation +// END xkan, 8/22/2002 + +// +// frame flags (stay resident until next script frame) +#define BSFFL_MOVETOTARGET 1 +#define BSFFL_FOLLOW_LEADER 2 +#define BSFFL_DIRECTMOVE 4 // move directly towards marker + +// Force us to stand still +#define BSFFL_STAND 8 + +// +// movement autonomy +#define BMA_NOVALUE -1 +#define BMA_LOW 0 +#define BMA_MEDIUM 1 +#define BMA_HIGH 2 +#define NUM_BMA 3 +// +// weapon autonomy +#define BWA_NOVALUE -1 +#define BWA_LOW 0 +#define BWA_MEDIUM 1 +#define BWA_HIGH 2 +#define NUM_BWA 3 + +// How many people can each bot be scripted to watch? +#define MAX_PEOPLE_TO_WATCH 8 + +// TAT 12/5/2002 +// script autonomy values +typedef enum +{ + BSA_NOVALUE = -1, + BSA_IGNOREENEMIES = 0, + BSA_MAINTAINSCRIPT, + BSA_NOCHASE, + BSA_QUITSCRIPT +} scriptAutonomy_t; + +// TAT 12/5/2002 +// So the follow behavior is different, depending on what our leader is doing +// Possible follow modes: +typedef enum +{ + FOLLOW_LINE, // follow in a line + FOLLOW_LINE_CROUCH, // follow in a line, but crouched + FOLLOW_SEEKCOVER, // spread out and seek cover + FOLLOW_SEEKCOVER_FAR, // spread out further and seek cover +} followModes_t; + +// START Mad Doctor I changes, 8/15/2002 + +// +// Alert State +// xkan, 1/10/2003 - replaced ALERTSTATE with AISTATE_* +//#define ALERTSTATE_RELAXED 0 +//#define ALERTSTATE_ENGAGED 1 + +// END Mad Doctor I changes, 8/15/2002 + + +// Start TAT 9/23/2002 + +// Bot recon information +// Invalid - not doing recon +#define BOTRECON_INVALID 0 +// No enemies spotted +#define BOTRECON_ALLCLEAR 1 +// saw enemies +#define BOTRECON_ENEMYSPOTTED 2 +// under attack +#define BOTRECON_UNDERFIRE 3 + +// End TAT 9/23/2002 + +// +typedef enum { + BSMT_DEFAULT, + BSMT_WALKING, + BSMT_CROUCHING, +} botScriptMovetype_t; +// +typedef struct +{ + char *actionString; + qboolean ( *actionFunc )( struct bot_state_s *bs, char *params ); +} bot_script_stack_action_t; +// +typedef struct +{ + // set during script parsing + bot_script_stack_action_t *action; // points to an action to perform + char *params; + // debugging info + int lineNum; + char *text; // points to the item location in global script buffer +} bot_script_stack_item_t; +// +typedef struct +{ + int startIndex; // place in character items this stack begins + int numItems; // how many items in this stack +} bot_script_stack_t; +// +typedef struct +{ + int eventNum; // index in scriptEvents[] + char *params; // trigger targetname, etc + bot_script_stack_t stack; + // debugging info + int lineNum; + char *text; // points to the item location in global script buffer +} bot_script_event_t; +// +typedef struct +{ + char *eventStr; + qboolean ( *eventMatch )( bot_script_event_t *event, char *eventParm ); +} bot_script_event_define_t; +// +// Scripting Status (NOTE: this MUST NOT contain any pointer vars) +typedef struct +{ + int stackHead, stackChangeTime; + int eventIndex; // current event containing stack of actions to perform + bot_script_stack_item_t *currentItem; + int id; // incremented each time the script changes + vec3_t playanim_viewangles; + int scriptNoAttackTime; + int scriptNoMoveTime; + int playAnimViewlockTime; + // + // function-relative variables (lost after function finishes) + // ... +} bot_script_status_t; +// +// static parsed data +typedef struct +{ + int numEvents; + char stringPool[BOT_SIZE_STRING_POOL]; + bot_script_stack_item_t items[BOT_MAX_SCRIPT_ITEMS]; + bot_script_event_t events[BOT_MAX_SCRIPT_EVENTS]; // contains a list of actions to perform for each event type +} bot_script_data_t; +// +typedef struct +{ + int lineNum; + char *name; + char *params; + bot_script_data_t *data; +} bot_script_global_data_t; +// +typedef struct +{ + bot_script_data_t *data; // pointer to global list of scripted characters + // + bot_script_status_t status; // current status of scripting + int callIndex; // inc'd each time a script is called + // persistent across function calls + int flags; + int frameFlags; // cleared at start of each script frame + int entityNum; + botScriptMovetype_t moveType; + fileHandle_t logFile; + int mg42entnum; + int weaponType; + int pauseTime; // stop running script for some time + int weaponAutonomy; + int movementAutonomy; + vec3_t movementAutonomyPos; + int accumBuffer[MAX_SCRIPT_ACCUM_BUFFERS]; // the accumulation buffer +} bot_script_t; +//----------------------------------------------------------------------------------------- +// Bot orders (issued by another player) + + + + +#define MAX_BOT_DEFAULT_STRING 512 + +/////////////////////////////////////////////////////////////////////////////// +// +// STRUCT BotDefaultAttributes +// +typedef struct BotDefaultAttributes_s +{ + // Name of the bot + char m_botName[MAX_BOT_DEFAULT_STRING]; + // What delay is there before we react to an enemy? + float m_reactionTime; + // How often do we hit our target? + float m_aimAccuracy; + // How likely are we to run back to the player + float m_wimpFactor; + // How fast do we move? + float m_speedCoefficient; + // What % of the time will we fire? + float m_fireRate; + // Min fire cycle length + int m_minFireRateCycleTime; + // Max fire cycle length + int m_maxFireRateCycleTime; + // What is our scripted FOV? + float m_scriptedFieldOfView; + // What is our scripted hearing range? + float m_scriptedHearingRange; + // When an enemy is this close; we can hear them even outside our field of view + float m_closeHearingRange; + // How far can this bot see? + float m_visionRange; + // This range is range to which we can see enemies; even if they are too far to attack + float m_farSeeingRange; + // How independent? + int m_movementAutonomy; + // How likely to shoot + int m_weaponAutonomy; + + +} BotDefaultAttributes_t; +// +// STRUCT BotDefaultAttributes +// +/////////////////////////////////////////////////////////////////////////////// + + + + +// Allow a good number of default character attributes +#define MAX_BOT_DEFAULTS 64 + +#define MAX_BOT_DEFAULT_FILE_SIZE 50000 + +#define NUM_BOT_DEFAULT_SIZE 16 + + + +typedef enum +{ + BOT_COMMAND_NULL, + BOT_COMMAND_FOLLOWME, + BOT_COMMAND_SEEK_COVER, + BOT_COMMAND_OBJECTIVE, + BOT_COMMAND_SNIPER, + BOT_COMMAND_COMETOME, + BOT_COMMAND_PLANT_MINE, + BOT_COMMAND_STANDSTILLDAMMIT, + // TAT - adding new commands + BOT_COMMAND_HOLDPOSITION, + BOT_COMMAND_MOVETOLOC, // move to the location the player is looking at + BOT_COMMAND_REVIVE, // medic ordered to revive an ailing teammate + BOT_COMMAND_DISGUISE, // covert op ordered to get an enemy uniform + BOT_COMMAND_SUPPORTFIRE, // lieutenant ordered to call airstrike or artillery + BOT_COMMAND_RECON, // bot goes on recon looking for enemies + BOT_COMMAND_COVERTHAT, // bot covers a location, doesn't move to it + BOT_COMMAND_GIVEHEALTH, // making the medic give health command an explicit state + BOT_COMMAND_GIVEAMMO, // similarly, making lieutenant give ammo command explicit as well + BOT_COMMAND_TEAM_HEALTH_AMMO, // devote yourself to giving health or ammo to the whole team - dependent on your class + BOT_COMMAND_DETECTMINES, // covert op looks for mines in a specified area + BOT_COMMAND_DISARM, // engineer disarm mine or dynamite + BOT_COMMAND_ATTACK, // an attack order, means we shouldn't break off from attacking our current target as easily + BOT_COMMAND_KNIFEATTACK, // covert ops special knife attack + // end TAT add +} botCommand_t; + +//----------------------------------------------------------------------------------------- +// note: these must be kept in sync with the list in ai_script.c +typedef enum +{ + + + + BOT_REACTION_TIME, + BOT_AIM_ACCURACY, + + + + + + BOT_WIMP_FACTOR, + // + BOT_MAX_ATTRIBUTES +} botAttributes_t; + +// What is our position in our cover spot chain? Basically, +// are we unable tog o farther int he direction we want? +typedef enum +{ + COVER_CHAIN_NONE, + COVER_CHAIN_NORMAL, + COVER_CHAIN_NOWHERE_TO_GO +} coverChainSpot_t; + +#define MAX_IGNORE_GOALS 32 + +typedef struct +{ + int entityNum; + int areanum; // goal areanum + vec3_t autonomyPos; // avoid it only if our autonomy position is the same as this + int expireTime; +} botIgnoreGoal_t; + +typedef struct { + int ( *func )( struct bot_state_s *bs ); +} botMatrixFunc_t; + +typedef struct { + botMatrixFunc_t cells[NUM_BWA][NUM_BMA]; +} botMatrix_t; + +// TAT 1/8/2003 - we store this many of our previous seek cover seqs - used for retreating +#define MAX_STORED_SEEKCOVERS 3 + +//----------------------------------------------------------------------------------------- +//bot state +typedef struct bot_state_s +{ + int inuse; //true if this state is used by a bot client + int botthink_residual; //residual for the bot thinks + int client; //client number of the bot + int entitynum; //entity number of the bot + playerState_t cur_ps; //current player state + int last_eFlags; //last ps flags + usercmd_t lastucmd; //usercmd from last frame + int entityeventTime[1024]; //last entity event time + // + bot_settings_t settings; //several bot settings + int ( *ainode )( struct bot_state_s *bs ); //current AI node + char *ainodeText; + float thinktime; //time the bot thinks this frame + vec3_t origin; //origin of the bot + vec3_t velocity; //velocity of the bot +// int presencetype; //presence type of the bot + vec3_t eye; //eye coordinates of the bot + int areanum; //the number of the area the bot is in + int inventory[MAX_ITEMS]; //string with items amounts the bot has + int tfl; //the travel flags the bot uses + int flags; //several flags + int respawn_wait; //wait until respawned + int lasthealth; //health value previous frame + + + + + +// int enemysuicide; //true when the enemy of the bot suicides + int setupcount; //true when the bot has just been setup + + int num_deaths; //number of time this bot died + int num_kills; //number of kills of this bot +// int revenge_enemy; //the revenge enemy +// int revenge_kills; //number of kills the enemy made + int lastframe_health; //health value the last frame + +// int chatto; //chat to all or team + float walker; //walker charactertic +// float ltime; //local bot time + + + + float respawn_time; //time the bot takes to respawn + float respawnchat_time; //time the bot started a chat during respawn + float chase_time; //time the bot will chase the enemy + float enemyvisible_time; //time the enemy was last visible + float stand_time; //time the bot is standing still + float lastchat_time; //time the bot last selected a chat + float standfindenemy_time; //time to find enemy while standing + float attackstrafe_time; //time the bot is strafing in one dir + float attackcrouch_time; //time the bot will stop crouching + float attackchase_time; //time the bot chases during actual attack + float attackjump_time; //time the bot jumped during attack + float enemysight_time; //time before reacting to enemy + float enemydeath_time; //time the enemy died + float enemyposition_time; //time the position and velocity of the enemy were stored + float activate_time; //time to activate something +// float activatemessage_time; //time to show activate message + +// float defendaway_range; //max travel time away from defend area +// float rushbaseaway_time; //time away from rushing to the base + float ctfroam_time; //time the bot is roaming in ctf + + int arrive_time; //time arrived (at companion) + + float teleport_time; //last time the bot teleported + float camp_time; //last time camped +// float camp_range; //camp range + float weaponchange_time; //time the bot started changing weapons + float firethrottlewait_time; //amount of time to wait + float firethrottleshoot_time; //amount of time to shoot + vec3_t aimtarget; + vec3_t enemyvelocity; //enemy velocity 0.5 secs ago during battle + vec3_t enemyorigin; //enemy origin 0.5 secs ago during battle + // + int character; //the bot character + int ms; //move state of the bot + int gs; //goal state of the bot + int cs; //chat state of the bot + int ws; //weapon state of the bot + // + int enemy; //enemy entity number + int lastenemyareanum; //last reachability area the enemy was in + vec3_t lastenemyorigin; //last origin of the enemy in the reachability area + int weaponnum; //current weapon number + vec3_t viewangles; //current view angles + vec3_t ideal_viewangles; //ideal view angles + // + int ltgtype; //long term goal type + // + int teammate; //team mate + bot_goal_t teamgoal; //the team goal + float teammessage_time; //time to message team mates what the bot is doing + float teamgoal_time; //time to stop helping team mate +// float teammatevisible_time; //last time the team mate was NOT visible + // + + bot_goal_t lead_teamgoal; //team goal while leading + float lead_time; //time leading someone + + +// float leadbackup_time; //time backing up towards team mate + // + char teamleader[32]; //netname of the team leader +// float askteamleader_time; //time asked for team leader +// float becometeamleader_time; //time the bot will become the team leader +// float teamgiveorders_time; //time to give team orders + int numteammates; //number of team mates + + + + + int flagcarrier; //team mate carrying the enemy flag + char subteam[32]; //sub team name + +// char formation_teammate[16]; //netname of the team mate the bot uses for relative positioning +// float formation_angle; //angle relative to the formation team mate +// vec3_t formation_dir; //the direction the formation is moving in +// vec3_t formation_origin; //origin the bot uses for relative positioning +// bot_goal_t formation_goal; //formation goal + bot_waypoint_t *checkpoints; //check points + bot_waypoint_t *patrolpoints; //patrol points + bot_waypoint_t *curpatrolpoint; //current patrol point the bot is going for + + // + int mpTeam, mpClass, mpWeapon; //kinda like cvar's for real players + int lastClassCheck; + // + bot_goal_t target_goal; + bot_goal_t avoid_goal; // the item we are avoiding + bot_goal_t alt_goal; // alternative route goal + bot_goal_t nearbygoal; + bot_goal_t defendgoal; + bot_goal_t hidegoal; + bot_goal_t messageGoal; // the message we must deliver - xkan, 10/28/2002 + bot_goal_t activategoal; //goal to activate (buttons etc.) + // + clientSession_t sess; // copied from gclient_t for convenience + // + int viewtype, viewchangetime; + // + int ignore_specialgoal_time; + int give_health_time; + int blockent, blockentTime; + int last_fire; + // + bot_chat_t vchats[MAX_VCHATS]; // record of voice chats sent to us + // + int altenemy; + // + int last_findspecialgoal; + int last_voice_chat; + qboolean alreadySaidOutOfAmmo; // if we just ran out of ammo, have we said it? + // + int last_kill_time; + int shorterm_kill_count; + // + int last_checkvoice_health; + // + int last_helped_client; + int last_helped_time; + // + int last_checkemergencytargets; + int last_findspecialgoals; + int last_findenemy, last_findenemy_enemy; + int last_SparseDefense; + int last_avoiddangerarea; + int last_dangerousgoal; + int last_botcheckattack; + int last_botcheckattack_weapon; + int last_botcheckattack_enemy; + int last_pain; + int last_pain_client; + int last_enemysight; + int last_heardShooting; // xkan - the last time we heard shooting + int last_heardFootSteps; // xkan - the last time we heard people moving around + int last_attackShout; // xkan - the last time we shout attack/kill/etc. + int next_nearbygoal; + int mobileMG42ProneTime; + + int nextRetreatCheck; // when should we check to see if we should retreat to the player -- TAT 12/12/2002 + int lastRetreatTime; // the last time we retreated + float retreatLikeliness; // chance that we will retreat + qboolean shouldRetreat; // should we retreat to the player? -- TAT 12/12/2002 + + // + vec3_t defendgoal_origin; + // + int leader; + int leader_tagent; // follow this entity for formation only + + // TAT 12/13/2002 - Need to keep track of when the player last moved, so storing their position, + // and we'll update the timer whenever it changes - only relevant in FollowLeader + vec3_t lastLeaderPosition; + int lastLeaderMoveTime; + int leaderStanceChangeTime; // time leader changed stance + int lastLeaderStance; // standing, prone, crouched +// vec3_t leader_lastvel; // last non-zero velocity + // + int avoid_spawnCount; + + // + botIgnoreGoal_t ignoreGoals[MAX_IGNORE_GOALS]; + // + // ATTRIBUTES + // Mad Doctor I, 12/8/2002. Trimmed most of the unused attributes out. + float attribs[BOT_MAX_ATTRIBUTES]; + + // SCRIPTING SYSTEM + bot_script_t script; + + // + // COMMAND STATUS + int movementAutonomy; + vec3_t movementAutonomyPos; + + // Mad Doctor I changes, 8/15/2002 + + // When did we enter the current AI Node? + int enteredCurrentAINodeTime; + + // Has the bot seen an enemy yet? + aistateEnum_t alertState; + int alertStateChangeTime; // the time alert state was changed to the current state. + int alertStateSetTime; // the time we attempt to set alert state to the same state. + + // when are we free to change alertState - required by scripting system + int alertStateAllowChangeTime; + + // What is our scripted FOV? + float scriptedFieldOfView; + + // What is our scripted hearing range? + float scriptedHearingRange; + + // How far can this bot see for choosing attack targets? + float visionRange; + + // This range is range to which we can see enemies, even if they + // are to far to attack + float farSeeingRange; + + // When an enemy is this close, we can hear them even outside our + // field of view + float closeHearingRange; + + // What weapon are the scripts telling us to use? + int scriptedWeapon; + + // Have we been scripted to be asleep + qboolean scriptedSleep; + + // Did our latest path planning fail? + qboolean movementFailedBadly; + + // last time an anim was played using scripting + int scriptAnimTime; + + // which anim was last played by the scripts + + + // When should we change to a new seek cover spot? + int whenToFindNewCombatSpot; + + // The current spot we're looking for + int seekCoverSpot; + + // Are we currently hiding? + qboolean amIHiding; + + // When should we stop hiding + int toggleHidingTime; + + // Do we have a specfic scripted cover spot + g_serverEntity_t *scriptedCoverSpot; + + // Store the last few seek cover sequences -- TAT 1/8/2003 + int lastSeekCoverSpots[MAX_STORED_SEEKCOVERS]; + + // What sort of spot are we in? Is it an end spot? + coverChainSpot_t coverSpotType; + + // Scripted speed for the bot + float speedCoefficient; + + // Is this bot selectable - it might be "hidden" from the player -- TAT 10/28/2002 + qboolean isBotSelectable; + + // TAT changes 8/20/2002 + // Have we received a player command such that we should override any movement scripts? + qboolean overrideMovementScripts; + + // Each bot is allowed to have 1 indicator for where it is going + gentity_t* indicator; + + // Gordon: 27//11/02: are we a pow? + qboolean isPOW; + + // TAT 9/23/2002 + // keep track of recon info for a bot - have we seen an enemy, are we under attack + int reconInfo; + + int returnToLeader; // Return to following this leader when our current goal is completed -- TAT 9/27/2002 + int commandedWeapon; // if this bot has been commanded to use a particular weapon - this is it -- TAT 11/14/2002 + + // TAT 11/12/2002 + // For Listen up/go go go + // We need to store info about the next command to execute + // Do we have a queued action? what type? 0 means no action, 1 means location, 2 means vchat + int haveQueuedCmd; + int queuedAction; // the botaction index - also could be used to store voice chat id + int queuedEntity; // the entity + int queuedSource; // which player gave the cmd? + vec3_t queuedLoc; // the location + vec3_t queuedPlayerLoc; // the player's loc at the time of the queuing + + scriptAutonomy_t scriptAutonomy; // how much are we allowed to break from our scripted commands? -- TAT 12/5/2002 + + followModes_t followMode; // we don't want to switch between followmodes too frequently, so store the mode -- TAT 12/5/2002 + int nextFollowModeTime; // nest time to update the followMode -- TAT 12/5/2002 + + // + // Goal/AI Matrix + botMatrix_t *objectiveMatrix; + botMatrix_t *behaviourMatrix; + botMatrix_t *engagementMatrix; + // + int lastAttackShared; + // + float fireRate; // the ratio of actual firing speed vs. maximum(normal) firing speed + int fireRateCheckTime; // the next time we should check for fire rate -xkan + int minFireRateCycleTime; // during a fire rate cycle, bot will fire for a duration of fireRate * cylcleTime + int maxFireRateCycleTime; // and will not fire for the rest of the cycle - (1-fireRate) * cycleTime + qboolean arrived; // Have we gottent to our next spot yet? + + int clientCheckTime; // Gordon: pows check for enemies/friends nearby +} bot_state_t; + +#define BFOFS( x ) ( (int)&( ( (bot_state_t *)0 )->x ) ) + +// Used in ai_team.c for BotClosestSeekCoverSpot_r +typedef qboolean ( *fCoverSpotCheck )( bot_state_t *bs, int leader, vec3_t leaderPos, g_serverEntity_t *coverSpot, float *bestDist ); + +//resets the whole bot state +void BotResetState( bot_state_t *bs ); +//returns the number of bots in the game +int NumBots( void ); +//returns info about the entity +void BotEntityInfo( int entnum, aas_entityinfo_t *info ); + +int BotAI_GetNumBots( void ); +void BotAI_SetNumBots( int numbots ); + +gentity_t *BotFindEntityForName( char *name ); + +int BotTravelFlagsForClient( int client ); + +// BotScript declarations +// We need to support more than 64 bot scripts. Note that this doesn't mean more than +// 64 simultaneous bots, but separate bot scritps. + +#define MAX_BOT_SCRIPT_CHARACTERS 256 + +extern bot_script_global_data_t botCharacterScriptData[MAX_BOT_SCRIPT_CHARACTERS]; + +void Bot_ScriptLoad( void ); +void Bot_ScriptParse( bot_script_data_t *bsd, char **text ); +qboolean Bot_ScriptInitBot( int entnum ); +qboolean Bot_ScriptRun( bot_state_t *bs, qboolean force ); +void Bot_ScriptLog_Entry( bot_state_t *bs, qboolean showDetails, char *preText, char *fmt, ... ); +int Bot_Script_GetCurrentLine( bot_state_t *bs ); +void Bot_ScriptThink( void ); +int Bot_FindSriptGlobalData( bot_script_data_t *data ); +// done. +/* +// Ridah, defines for AI Cast system +int AICast_ShutdownClient(int client); +void AICast_Init (void); +void AICast_StartFrame ( int time); +// done. +*/ +//bot states +extern bot_state_t botstates[MAX_CLIENTS]; + +// from the game source +void QDECL BotAI_Print( int type, char *fmt, ... ); +void QDECL QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ); +void BotAI_Trace( bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask ); +int BotAI_GetClientState( int clientNum, playerState_t *state ); +int BotAI_GetEntityState( int entityNum, entityState_t *state ); +int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ); + +// the static entity cache, to reduce load on G_Find() +// !!! NOTE: must be in synch with string list in ai_main.c +typedef enum { + BOTSTATICENTITY_CHECKPOINT, + BOTSTATICENTITY_FLAGONLY, + BOTSTATICENTITY_MG42, + BOTSTATICENTITY_OBJECTIVE_INFO, + BOTSTATICENTITY_CTF_REDFLAG, + BOTSTATICENTITY_CTF_BLUEFLAG, + BOTSTATICENTITY_FUNC_EXPLOSIVE, + BOTSTATICENTITY_FUNC_DOOR, + BOTSTATICENTITY_FUNC_DOOR_ROTATING, + BOTSTATICENTITY_FUNC_CONSTRUCTIBLE, + BOTSTATICENTITY_TRIGGER_MULTIPLE, + BOTSTATICENTITY_FLAGONLY_MULTIPLE, + BOTSTATICENTITY_BOT_LANDMINE_AREA, + BOTSTATICENTITY_BOT_ATTRACTOR, + BOTSTATICENTITY_BOT_SNIPERSPOT, + BOTSTATICENTITY_BOT_LANDMINESPOTINGSPOT, + // + NUM_BOTSTATICENTITY +} botStaticEntityEnum_t; + +void BotBuildStaticEntityCache( void ); +gentity_t *BotFindNextStaticEntity( gentity_t *start, botStaticEntityEnum_t entityEnum ); +gentity_t *BotSpawnGameEntity( void ); +//void BotInitBotGameEntities(void); +gentity_t *BotGetEntity( int entityNum ); + +/* +================= +FindBotByName +================= + +Get the bot state of a named bot + +*/ +bot_state_t *FindBotByName( char * botName ); + +int BotGetTargetExplosives( team_t team, int *list, int listSize, qboolean ignoreDynamite ); +int BotGetTargetsForSatchelCharge( team_t team, int *list, int listSize, qboolean ignoreDynamite ); +int BotGetTargetDynamite( int *list, int listSize, gentity_t* target ); +void BotDebugViewClient( int client ); + +// profiling vars +extern int botTime_EmergencyGoals; +extern int botTime_FindGoals; +extern int botTime_FindEnemy; +extern vmCvar_t bot_profile; + +extern vmCvar_t bot_findgoal; + +typedef enum botgoalFindType_e { + /* *cough* */ + BFG_FOLLOW_LEADER = 0, + BFG_CONSTRUCT, + BFG_TRIGGER, + BFG_DESTRUCTION_EXPLOSIVE, + BFG_DESTRUCTION_BUILDING, + BFG_MG42_REPAIR, + BFG_MINE, + BFG_ATTRACTOR, + BFG_SNIPERSPOT, + BFG_MG42, + BFG_SCANFORMINES, + BFG_DESTRUCTION_SATCHEL, + BFG_NUMBOTGOALTYPES, +} botgoalFindType_t; + +extern int botgoalMaxCloser[BFG_NUMBOTGOALTYPES]; diff --git a/src/botai/ai_matrix.h b/src/botai/ai_matrix.h new file mode 100644 index 0000000..3ebbe37 --- /dev/null +++ b/src/botai/ai_matrix.h @@ -0,0 +1,60 @@ +/* +=========================================================================== + +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: ai_matrix.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +// +// matrix definitions +// + +extern botMatrix_t soldierBehaviourMatrix; +extern botMatrix_t soldierObjectiveMatrix; +extern botMatrix_t soldierEngagementMatrix; + +extern botMatrix_t medicBehaviourMatrix; +extern botMatrix_t medicObjectiveMatrix; +extern botMatrix_t medicEngagementMatrix; + +extern botMatrix_t ltBehaviourMatrix; +extern botMatrix_t ltObjectiveMatrix; +extern botMatrix_t ltEngagementMatrix; + +extern botMatrix_t covertopsBehaviourMatrix; +extern botMatrix_t covertopsObjectiveMatrix; +extern botMatrix_t covertopsEngagementMatrix; + +extern botMatrix_t engineerBehaviourMatrix; +extern botMatrix_t engineerObjectiveMatrix; +extern botMatrix_t engineerEngagementMatrix; diff --git a/src/botai/ai_script.c b/src/botai/ai_script.c new file mode 100644 index 0000000..a479efd --- /dev/null +++ b/src/botai/ai_script.c @@ -0,0 +1,1171 @@ +/* +=========================================================================== + +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: ai_script.c +// Function: Wolfenstein BOT Scripting +// Programmer: Ridah +// Tab Size: 4 (real tabs) +//=========================================================================== + +#include "../game/g_local.h" +#include "../game/q_shared.h" +#include "../game/botlib.h" //bot lib interface +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../botai/botai.h" //bot ai interface + +#include "ai_main.h" +#include "ai_dmq3.h" + +/* +Scripting that allows the designwers to control the behaviour of AI characters +according to each different scenario. +*/ + +// action functions need to be declared here so they can be accessed in the scriptAction table +qboolean Bot_ScriptAction_Print( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetAccumToPlayerCount( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SpawnBot( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_Accum( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_GlobalAccum( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_Wait( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_MoveToMarker( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_Trigger( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_Logging( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_AbortIfWarmup( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetAttribute( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_MountMG42( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetWeapon( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetClass( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetMovementAutonomy( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_FollowLeader( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_Cvar( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_MovementAutonomy( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_ResetScript( bot_state_t *bs, char *params ); + +// Mad Doctor I, 8/12/2002. Add a script function to set a bot as untargettable +qboolean Bot_ScriptAction_NoTarget( bot_state_t *bs, char *params ); + +// Mad Doctor I, 8/15/2002. Allow changing the FOV +qboolean Bot_ScriptAction_SetFieldOfView( bot_state_t *bs, char *params ); + +// Mad Doctor I, 8/15/2002. Change the hearing range +qboolean Bot_ScriptAction_SetHearingRange( bot_state_t *bs, char *params ); + +// Mad Doctor I changes, 8/19/2002. Play a sound right where the player is +qboolean Bot_ScriptAction_PlaySoundAtPlayer( bot_state_t *bs, char *params ); + +// xkan, 8/20/2002 +qboolean Bot_ScriptAction_SetCrouch( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_SetProne( bot_state_t *bs, char *params ); + +// Mad Doc - TDF +qboolean Bot_ScriptAction_PrintAccum( bot_state_t *bs, char *params ); +qboolean Bot_ScriptAction_PrintGlobalAccum( bot_state_t *bs, char *params ); + +qboolean Bot_ScriptAction_SetSpeed( bot_state_t *bs, char *params ); + +// Mad Doc - TDF +qboolean Bot_ScriptAction_BotDebugging( bot_state_t *bs, char *params ); + +// Mad Doctor I, 10/23/2002. Vision range settings. +qboolean Bot_ScriptAction_SetVisionRange( bot_state_t *bs, char *params ); + +// Mad Doctor I, 10/23/2002. Range for "spot & report", not attack. +qboolean Bot_ScriptAction_SetFarSeeingRange( bot_state_t *bs, char *params ); + +// Mad Doctor I, 10/23/2002. When an enemy is this close, you can sense them outside FOV. +qboolean Bot_ScriptAction_SetCloseHearingRange( bot_state_t *bs, char *params ); + +// Mad Doctor I, 11/26/2002. Set an individual bot's speed +qboolean Bot_ScriptAction_SetSpeedCoefficient( bot_state_t *bs, char *params ); + +// xkan, 10/23/2002 +qboolean Bot_ScriptAction_SetFireRate( bot_state_t *bs, char *params ); +// xkan, 12/19/2002 +qboolean Bot_ScriptAction_SetFireCycleTime( bot_state_t *bs, char *params ); + +// TAT 11/16/2002 - Set the selected weapon of the bot - does NOT change which weapons the bot is holding +qboolean Bot_ScriptAction_SetActiveWeapon( bot_state_t *bs, char *params ); + +// Gordon: 31/10/02, give players a centerprint message +qboolean Bot_ScriptAction_Announce( bot_state_t *bs, char *params ); + +// TAT 11/21/2002 - From SP, bot will fire at target for duration +qboolean Bot_ScriptAction_FireAtTarget( bot_state_t *bs, char *params ); + +// TAT 12/6/2002 - script autonomy is used to determine if bots will break their scripted actions to attack or chase +qboolean Bot_ScriptAction_SetScriptAutonomy( bot_state_t *bs, char *params ); + +// TAT 12/14/2002 - Set how much ammo we have for a particular weapon +// Doesn't change the current weapon loadout (which weapons we carry) +qboolean Bot_ScriptAction_SetAmmoAmount( bot_state_t *bs, char *params ); + +qboolean Bot_ScriptAction_SetCivilian( bot_state_t *bs, char *params ); + +// these are the actions that each event can call +static bot_script_stack_action_t botScriptActions[] = +{ + {"print", Bot_ScriptAction_Print}, + {"SetAccumToPlayerCount", Bot_ScriptAction_SetAccumToPlayerCount}, + {"SpawnBot", Bot_ScriptAction_SpawnBot}, + {"Accum", Bot_ScriptAction_Accum}, + {"GlobalAccum", Bot_ScriptAction_GlobalAccum}, + {"Wait", Bot_ScriptAction_Wait}, + {"MoveToMarker", Bot_ScriptAction_MoveToMarker}, + {"Trigger", Bot_ScriptAction_Trigger}, + {"Logging", Bot_ScriptAction_Logging}, + {"AbortIfWarmup", Bot_ScriptAction_AbortIfWarmup}, + {"SetAttribute", Bot_ScriptAction_SetAttribute}, + {"MountMG42", Bot_ScriptAction_MountMG42}, + {"SetWeapon", Bot_ScriptAction_SetWeapon}, + {"SetClass", Bot_ScriptAction_SetClass}, + {"SetMovementAutonomy", Bot_ScriptAction_SetMovementAutonomy}, + {"FollowLeader", Bot_ScriptAction_FollowLeader}, + {"Cvar", Bot_ScriptAction_Cvar}, + {"MovementAutonomy", Bot_ScriptAction_MovementAutonomy}, + {"NoTarget", Bot_ScriptAction_NoTarget}, + {"ResetScript", Bot_ScriptAction_ResetScript}, + + // Mad Doctor I, 8/15/2002 + {"SetFieldOfView", Bot_ScriptAction_SetFieldOfView}, + {"SetHearingRange", Bot_ScriptAction_SetHearingRange}, + + // Mad Doctor I, 8/19/2002 + {"PlaySoundAtPlayer", Bot_ScriptAction_PlaySoundAtPlayer}, + + // xkan 8/20/2002 + {"SetCrouch", Bot_ScriptAction_SetCrouch}, + {"SetProne", Bot_ScriptAction_SetProne}, + + // Mad Doc - TDF + {"PrintAccum", Bot_ScriptAction_PrintAccum}, + {"PrintGlobalAccum", Bot_ScriptAction_PrintGlobalAccum}, + + {"setspeed", Bot_ScriptAction_SetSpeed}, + + // Mad Doc - TDF, toggle bot thought bubbles + {"BotDebugging", Bot_ScriptAction_BotDebugging}, + + // Mad Doctor I, 10/23/2002. Vision range settings. + {"SetVisionRange", Bot_ScriptAction_SetVisionRange}, + + // Mad Doctor I, 10/23/2002. Range for "spot & report", not attack. + {"SetFarSeeingRange", Bot_ScriptAction_SetFarSeeingRange}, + + // Mad Doctor I, 10/23/2002. When an enemy is this close, you can sense them outside FOV. + {"SetCloseHearingRange", Bot_ScriptAction_SetCloseHearingRange}, + + // Mad Doctor I, 11/26/2002. Set an individual bot's speed + {"SetSpeedCoefficient", Bot_ScriptAction_SetSpeedCoefficient}, + + // xkan, 10/23/2002. Rate of slowdown/interruption to normally continuous firing + {"SetFireRate", Bot_ScriptAction_SetFireRate}, + // xkan, 12/19/2002, fire cycle time to break up based on fire rate. + {"SetFireCycleTime", Bot_ScriptAction_SetFireCycleTime}, + + // TAT 11/16/2002 - Set the selected weapon of the bot - does NOT change which weapons the bot is holding + {"SetActiveWeapon", Bot_ScriptAction_SetActiveWeapon}, + + // Gordon: just need this temporarily to give the player some messages, should remove later + {"wm_announce", Bot_ScriptAction_Announce}, + + // TAT 11/21/2002 - From SP, bot will fire at target for duration + {"FireAtTarget", Bot_ScriptAction_FireAtTarget}, + + // TAT 12/6/2002 - script autonomy is used to determine if bots will break their scripted actions to attack or chase + {"SetScriptAutonomy", Bot_ScriptAction_SetScriptAutonomy}, + + // TAT 12/14/2002 - Set how much ammo we have for a particular weapon + // Doesn't change the current weapon loadout (which weapons we carry) + {"SetAmmoAmount", Bot_ScriptAction_SetAmmoAmount}, + + // xkan, 1/6/2003 - mark this bot as whether he is civilian + {"SetCivilian", Bot_ScriptAction_SetCivilian}, + + {NULL, NULL} +}; + +qboolean Bot_EventMatch_StringEqual( bot_script_event_t *event, char *eventParm ); +qboolean Bot_EventMatch_IntInRange( bot_script_event_t *event, char *eventParm ); + +// the list of events that can start an action sequence +// NOTE!!: only append to this list, DO NOT INSERT!! +static bot_script_event_define_t botScriptEvents[] = +{ + {"spawn", NULL}, // called as each character is spawned into the game + {"trigger", Bot_EventMatch_StringEqual}, // something has triggered us (always followed by an identifier) + {"pain", Bot_EventMatch_IntInRange}, // we've been hurt + {"death", NULL}, // RIP + {"activate", Bot_EventMatch_StringEqual}, // "param" has just activated us + {"objective", Bot_EventMatch_StringEqual}, // something has occured involving the objective + {"respawn", Bot_EventMatch_StringEqual}, // respawned at "param" spawnpoint + {"enemysight", Bot_EventMatch_StringEqual}, // + {"revived", NULL}, // bot was revived by a comrade + + {NULL, NULL} +}; + +int numSecrets; + +/* +============================= +STATIC SCRIPT DATA STRUCTURES + + Holds parsed information for each scripted character. Each bot that enters the game chooses + their scripting data from one of the characters below. +============================= +*/ +static int numScriptCharacters = 0; + +bot_script_global_data_t botCharacterScriptData[MAX_BOT_SCRIPT_CHARACTERS]; + + +/* +=============== +Bot_FindSriptGlobalData +=============== +*/ +int Bot_FindSriptGlobalData( bot_script_data_t *data ) { + int i; + + for ( i = 0; i < numScriptCharacters; i++ ) { + if ( botCharacterScriptData[i].data == data ) { + return i; + } + } + + return -1; +} + +/* +=============== +Bot_EventMatch_StringEqual +=============== +*/ +qboolean Bot_EventMatch_StringEqual( bot_script_event_t *event, char *eventParm ) { + if ( !event->params || !event->params[0] || ( eventParm && !Q_stricmp( event->params, eventParm ) ) ) { + return qtrue; + } else { + return qfalse; + } +} + +/* +=============== +Bot_EventMatch_IntInRange +=============== +*/ +qboolean Bot_EventMatch_IntInRange( bot_script_event_t *event, char *eventParm ) { + char *pString, *token; + int int1, int2, eInt; + + // get the cast name + pString = eventParm; + token = COM_ParseExt( &pString, qfalse ); + int1 = atoi( token ); + token = COM_ParseExt( &pString, qfalse ); + int2 = atoi( token ); + + eInt = atoi( event->params ); + + if ( eventParm && eInt > int1 && eInt <= int2 ) { + return qtrue; + } else { + return qfalse; + } +} + +/* +=============== +Bot_EventForString +=============== +*/ +int Bot_EventForString( char *string ) { + int i; + + for ( i = 0; botScriptEvents[i].eventStr; i++ ) + { + if ( !Q_stricmp( string, botScriptEvents[i].eventStr ) ) { + return i; + } + } + + return -1; +} + +/* +=============== +Bot_ActionForString +=============== +*/ +bot_script_stack_action_t *Bot_ActionForString( char *string ) { + int i; + + for ( i = 0; botScriptActions[i].actionString; i++ ) + { + if ( !Q_stricmp( string, botScriptActions[i].actionString ) ) { + if ( !Q_stricmp( string, "foundsecret" ) ) { + numSecrets++; + } + return &botScriptActions[i]; + } + } + + return NULL; +} + +void Bot_ScriptParseAllCharacters(); + +/* +============= +Bot_ScriptLoad + + Loads the script for the current level into the buffer +============= +*/ +void Bot_ScriptLoad( void ) { +/* + char filename[MAX_QPATH]; + vmCvar_t mapname; + fileHandle_t f; + int len; + + level.botScriptBuffer = NULL; + + trap_Cvar_VariableStringBuffer( "bot_scriptName", filename, sizeof(filename) ); + if (strlen( filename ) > 0) { + trap_Cvar_Register( &mapname, "bot_scriptName", "", CVAR_ROM ); + } else { + trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); + } + Q_strncpyz( filename, "maps/", sizeof(filename) ); + Q_strcat( filename, sizeof(filename), mapname.string ); + Q_strcat( filename, sizeof(filename), ".botscript" ); + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + + // make sure we clear out the temporary scriptname + trap_Cvar_Set( "bot_scriptName", "" ); + + if (len < 0) + return; + + level.botScriptBuffer = G_Alloc( len ); + trap_FS_Read( level.botScriptBuffer, len, f ); + + trap_FS_FCloseFile( f ); + + // now parse the data for each of the characters + Bot_ScriptParseAllCharacters(); +*/ + Bot_ScriptParseAllCharacters(); + return; +} + +/* +============== +Bot_ScriptParseAllCharacters +============== +*/ +void Bot_ScriptParseAllCharacters() { + char *pScript; + char *token; + bot_script_global_data_t *bsd; + char params[MAX_TOKEN_CHARS]; + + if ( !level.scriptEntity ) { + return; + } + + pScript = level.scriptEntity; + COM_BeginParseSession( "Bot_ScriptParse" ); + numScriptCharacters = 0; + memset( botCharacterScriptData, 0, sizeof( botCharacterScriptData ) ); + + while ( 1 ) + { + token = COM_Parse( &pScript ); + // we are expecting a name here + if ( !token[0] ) { + // end of script + break; + } + if ( token[0] == '{' || token[0] == '}' ) { + G_Error( "Bot_ScriptParse(), Error (line %d): entry identifier expected, '%s' found.\n", 1 + COM_GetCurrentParseLine(), token ); + } + // is this a bot? + if ( Q_stricmp( token, "BOT" ) != 0 ) { + // not a bot, skip this whole entry + SkipRestOfLine( &pScript ); + // skip this section + SkipBracedSection( &pScript ); + // + continue; + } + // this is the name + if ( numScriptCharacters == MAX_BOT_SCRIPT_CHARACTERS ) { + G_Error( "Bot_ScriptParse(), Error (line %d): MAX_BOT_SCRIPT_CHARACTERS exceeded (%i), too many bot script characters\n", 1 + COM_GetCurrentParseLine(), MAX_BOT_SCRIPT_CHARACTERS ); + break; + } + bsd = &botCharacterScriptData[numScriptCharacters++]; + bsd->lineNum = 1 + COM_GetCurrentParseLine(); + // read the name + token = COM_Parse( &pScript ); + // we are expecting a name here + if ( !token[0] ) { + G_Error( "Bot_ScriptParse(), Error (line %d): name expected, end of line found.\n", 1 + COM_GetCurrentParseLine() ); + } + if ( token[0] == '{' || token[0] == '}' ) { + G_Error( "Bot_ScriptParse(), Error (line %d): name expected, '%s' found.\n", 1 + COM_GetCurrentParseLine(), token ); + } + // allocate the name + bsd->name = G_Alloc( strlen( token ) + 1 ); + Q_strncpyz( bsd->name, token, strlen( token ) + 1 ); + // read the params + memset( params, 0, sizeof( params ) ); + while ( ( token = COM_ParseExt( &pScript, qfalse ) ) && token[0] ) { + if ( strlen( params ) + strlen( token ) >= sizeof( params ) ) { + G_Error( "Bot_ScriptParse(), Error (line %d): parameters exceed maximum size\n", 1 + COM_GetCurrentParseLine() ); + } + if ( strlen( params ) > 0 ) { + Q_strcat( params, sizeof( params ), " " ); + } + Q_strcat( params, sizeof( params ), token ); + } + // allocate the params + bsd->params = G_Alloc( strlen( params ) + 1 ); + Q_strncpyz( bsd->params, params, strlen( params ) + 1 ); + // allocate memory for this character script + bsd->data = G_Alloc( sizeof( bot_script_data_t ) ); + memset( bsd->data, 0, sizeof( bot_script_data_t ) ); + // now parse the script data for this character + Bot_ScriptParse( bsd->data, &pScript ); + } +} + +/* +============== +Bot_ScriptParse + + Parses the script for the given character +============== +*/ +void Bot_ScriptParse( bot_script_data_t *bsd, char **text ) { + char *token; + qboolean inScript; + int eventNum; + bot_script_event_t events[BOT_MAX_SCRIPT_EVENTS]; + int numEventItems; + bot_script_event_t *curEvent; + char params[512]; + // + bot_script_stack_item_t items[BOT_MAX_SCRIPT_ITEMS]; + int numItems; + // + bot_script_stack_action_t *action; + int i; + int bracketLevel; + int strPoolCount; + + inScript = qfalse; // not inside the given bot's script + + bracketLevel = 0; + numEventItems = 0; + numItems = 0; + strPoolCount = 0; + + memset( events, 0, sizeof( events ) ); + memset( items, 0, sizeof( items ) ); + + while ( 1 ) + { + token = COM_Parse( text ); + + if ( !token[0] ) { + break; + } + + // end of script + if ( token[0] == '}' ) { + bracketLevel--; + if ( !bracketLevel ) { // we have already parsed the given bot, so get out of here + break; + } + if ( bracketLevel < 0 ) { + G_Error( "Bot_ScriptParse(), Error (line %d): '%s' found, name expected\n", 1 + COM_GetCurrentParseLine(), token ); + } + } else if ( token[0] == '{' ) { + bracketLevel++; + if ( bracketLevel > 1 ) { + G_Error( "Bot_ScriptParse(), Error (line %d): '%s' found, event name expected\n", 1 + COM_GetCurrentParseLine(), token ); + } + } else if ( bracketLevel == 1 ) { + eventNum = Bot_EventForString( token ); + if ( eventNum < 0 ) { + G_Error( "Bot_ScriptParse(), Error (line %d): unknown event: %s.\n", 1 + COM_GetCurrentParseLine(), token ); + } + if ( numEventItems >= BOT_MAX_SCRIPT_EVENTS ) { + G_Error( "Bot_ScriptParse(), Error (line %d): BOT_MAX_SCRIPT_EVENTS reached (%d)\n", 1 + COM_GetCurrentParseLine(), BOT_MAX_SCRIPT_EVENTS ); + } + + curEvent = &events[numEventItems]; + curEvent->eventNum = eventNum; + curEvent->stack.startIndex = numItems; + memset( params, 0, sizeof( params ) ); + + curEvent->lineNum = 1 + COM_GetCurrentParseLine(); + curEvent->text = *text - strlen( token ); + + // parse any event params before the start of this event's actions + while ( ( token = COM_Parse( text ) ) && ( token[0] != '{' ) ) + { + if ( !token[0] ) { + G_Error( "Bot_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", 1 + COM_GetCurrentParseLine() ); + } + + if ( strlen( params ) ) { // add a space between each param + Q_strcat( params, sizeof( params ), " " ); + } + Q_strcat( params, sizeof( params ), token ); + } + + if ( strlen( params ) ) { // copy the params into the event + curEvent->params = &bsd->stringPool[strPoolCount]; + Q_strncpyz( curEvent->params, params, BOT_SIZE_STRING_POOL - strPoolCount ); + if ( ( strPoolCount += strlen( params ) + 1 ) >= BOT_SIZE_STRING_POOL ) { + G_Error( "Bot_ScriptParse(), Error (line %d): string pool size exceeded (MAX = %i)\n", 1 + COM_GetCurrentParseLine(), BOT_SIZE_STRING_POOL ); + } + } + + // parse the actions for this event + while ( ( token = COM_Parse( text ) ) && ( token[0] != '}' ) ) + { + if ( !token[0] ) { + G_Error( "Bot_ScriptParse(), Error (line %d): '}' expected, end of script found.\n", 1 + COM_GetCurrentParseLine() ); + } + + action = Bot_ActionForString( token ); + if ( !action ) { + G_Error( "Bot_ScriptParse(), Error (line %d): unknown action: %s.\n", 1 + COM_GetCurrentParseLine(), token ); + } + + items[numItems].action = action; + items[numItems].lineNum = 1 + COM_GetCurrentParseLine(); + items[numItems].text = *text - strlen( token ); + + memset( params, 0, sizeof( params ) ); + token = COM_ParseExt( text, qfalse ); + for ( i = 0; token[0]; i++ ) + { + if ( strlen( params ) ) { // add a space between each param + Q_strcat( params, sizeof( params ), " " ); + } + + // Special case: playsound's need to be cached on startup to prevent in-game pauses + if ( i == 0 ) { + if ( !Q_stricmp( action->actionString, "playsound" ) ) { + G_SoundIndex( token ); + } + } + + if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word + Q_strcat( params, sizeof( params ), "\"" ); + } + + Q_strcat( params, sizeof( params ), token ); + + if ( strrchr( token,' ' ) ) { // need to wrap this param in quotes since it has more than one word + Q_strcat( params, sizeof( params ), "\"" ); + } + + token = COM_ParseExt( text, qfalse ); + } + + if ( strlen( params ) ) { // copy the params into the event + items[numItems].params = &bsd->stringPool[strPoolCount]; + Q_strncpyz( items[numItems].params, params, BOT_SIZE_STRING_POOL - strPoolCount ); + if ( ( strPoolCount += strlen( params ) + 1 ) >= BOT_SIZE_STRING_POOL ) { + G_Error( "Bot_ScriptParse(), Error (line %d): string pool size exceeded (MAX = %i)\n", 1 + COM_GetCurrentParseLine(), BOT_SIZE_STRING_POOL ); + } + } + + curEvent->stack.numItems++; + numItems++; + + if ( numItems >= BOT_MAX_SCRIPT_ITEMS ) { + G_Error( "Bot_ScriptParse(), Error (line %d): script exceeded BOT_MAX_SCRIPT_ITEMS (%d)\n", 1 + COM_GetCurrentParseLine(), BOT_MAX_SCRIPT_ITEMS ); + } + } + + numEventItems++; + } else { + G_Error( "Bot_ScriptParse(), Error (line %d): '%s' found, '{' expected\n", 1 + COM_GetCurrentParseLine(), token ); + } + } + + // alloc and copy the events into the structure for this bot + if ( numEventItems > 0 ) { + memcpy( bsd->events, events, sizeof( bot_script_event_t ) * numEventItems ); + bsd->numEvents = numEventItems; + memcpy( bsd->items, items, sizeof( bot_script_stack_item_t ) * numItems ); + } + +} + +/* +================ +Bot_ScriptInitBot +================ +*/ +qboolean Bot_ScriptInitBot( int entnum ) { + gentity_t *ent, *trav; + bot_state_t *bs; + char userinfo[MAX_INFO_STRING]; + bot_script_global_data_t *bsgd; + char *token, *p, *pBackup; + int i, val = 0; + int weapons[2]; + gitem_t *item = NULL; + char *name; + // + bs = &botstates[entnum]; + if ( !bs->inuse ) { + return qfalse; + } + if ( bs->script.data ) { + return qtrue; + } + // set starting defaults + bs->script.status.eventIndex = -1; + bs->script.data = NULL; + // + ent = BotGetEntity( bs->entitynum ); + trap_GetUserinfo( bs->entitynum, userinfo, sizeof( userinfo ) ); + name = Info_ValueForKey( userinfo, "scriptName" ); + if ( !name || !name[0] ) { + return qfalse; + } + + // find the script data for this bot + bsgd = botCharacterScriptData; + for ( i = 0; i < numScriptCharacters; i++, bsgd++ ) { + if ( Q_stricmp( name, bsgd->name ) != 0 ) { + continue; + } + // check params + p = bsgd->params; + // + // eliminate them with each condition not met + while ( qtrue ) { + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] ) { + // we're done, we found a match + break; + } + // + if ( token[0] != '/' ) { + G_Error( "BotScript, line %i: condition identifier expected, '%s' found\n", bsgd->lineNum, token ); + } + // + if ( !Q_stricmp( token, "/team" ) ) { + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + G_Error( "BotScript, line %i: unexpected end of /team parameter", bsgd->lineNum ); + } + // + if ( !Q_stricmp( token, "axis" ) ) { + val = TEAM_AXIS; + } else if ( !Q_stricmp( token, "allies" ) ) { + val = TEAM_ALLIES; + } else { + G_Error( "BotScript, line %i: unknown team \"%s\"", bsgd->lineNum, token ); + } + // eliminate player + if ( bs->mpTeam != val ) { + break; + } + } else + // + if ( !Q_stricmp( token, "/class" ) ) { + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + G_Error( "BotScript, line %i: unexpected end of /class parameter", bsgd->lineNum ); + } + // + val = Team_ClassForString( token ); + if ( val < 0 ) { + G_Error( "BotScript, line %i: unknown class \"%s\"", bsgd->lineNum, token ); + } + if ( bs->mpClass != val ) { + break; + } + } else + // + if ( !Q_stricmp( token, "/weapon" ) ) { + memset( weapons, 0, sizeof( weapons ) ); + // for each weapon + while ( qtrue ) { + // read the weapon + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + G_Error( "BotScript, line %i: unexpected end of /weapon parameter", bsgd->lineNum ); + } + // + if ( ( item = BG_FindItem( token ) ) ) { + if ( !item->giTag ) { + G_Error( "BotScript, line %i: unknown weapon \"%s\"", bsgd->lineNum, token ); + } + COM_BitSet( weapons, item->giTag ); + } else { + G_Error( "BotScript, line %i: unknown weapon \"%s\"", bsgd->lineNum, token ); + } + // + pBackup = p; + token = COM_ParseExt( &p, qfalse ); + if ( Q_stricmp( token, "or" ) != 0 ) { + // not OR, so drop out of here + p = pBackup; + break; + } + } + if ( !( ent->client->ps.weapons[0] & weapons[0] ) && !( ent->client->ps.weapons[1] & weapons[1] ) ) { + break; + } + } else + // + if ( !Q_stricmp( token, "/within_range" ) ) { + // targetname + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + G_Error( "BotScript, line %i: unexpected end of /within_range parameter", bsgd->lineNum ); + } + trav = G_FindByTargetname( NULL, token ); + if ( !trav ) { + G_Error( "BotScript, line %i: unknown spawn point \"%s\"", bsgd->lineNum, token ); + } + // range + token = COM_ParseExt( &p, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + G_Error( "BotScript, line %i: range expected, not found", bsgd->lineNum ); + } + // + // eliminate players + if ( VectorDistanceSquared( ent->r.currentOrigin, trav->s.origin ) > SQR( atof( token ) ) ) { + break; + } + } + } + // + // if there is a NOT a valid token waiting, then we passed all checks + if ( !token[0] ) { + break; + } + } + // + if ( i < numScriptCharacters ) { + // we found a script for this character + bs->script.data = bsgd->data; + return qtrue; + } + // + return qfalse; +} + +/* +================ +Bot_LineText +================ +*/ +char *Bot_LineText( char *text ) { + static char lineText[MAX_TOKEN_CHARS]; + int len; + // + len = strstr( text, "\n" ) - text; + if ( len <= 0 ) { + return ""; + } + if ( len >= sizeof( lineText ) - 1 ) { + G_Error( "Bot_LineText: max line length exceed (%i)", (int)sizeof( lineText ) ); + } + // + memset( lineText, 0, sizeof( lineText ) ); + Q_strncpyz( lineText, text, len ); + // + return lineText; +} + +/* +================ +Bot_ScriptChange +================ +*/ +void Bot_ScriptChange( bot_state_t *bs, int newScriptNum ) { + bot_script_status_t statusBackup; + + bs->script.callIndex++; + + // backup the current scripting + statusBackup = bs->script.status; + + // set the new script to this cast, and reset script status + bs->script.status.stackHead = 0; + bs->script.status.stackChangeTime = level.time; + bs->script.status.eventIndex = newScriptNum; + bs->script.status.id = statusBackup.id + 1; + + // first call + bs->script.flags |= BSFL_FIRST_CALL; + + Bot_ScriptLog_Entry( bs, qfalse, Bot_LineText( bs->script.data->events[bs->script.status.eventIndex].text ), "** NEW EVENT **\r\n" ); + + // try and run the script, if it doesn't finish, then abort the current script (discard backup) + if ( Bot_ScriptRun( bs, qtrue ) ) { + // completed successfully + bs->script.status.stackHead = statusBackup.stackHead; + bs->script.status.stackChangeTime = statusBackup.stackChangeTime; + bs->script.status.eventIndex = statusBackup.eventIndex; + bs->script.status.id = statusBackup.id; + // + bs->script.flags &= ~BSFL_FIRST_CALL; + // returned to previous event + if ( statusBackup.eventIndex > -1 ) { + Bot_ScriptLog_Entry( bs, qfalse, Bot_LineText( bs->script.data->events[statusBackup.eventIndex].text ), "**RESUMED**\r\n" ); + } + } else { + // still running, previous script is terminated + if ( statusBackup.eventIndex > -1 && statusBackup.eventIndex != bs->script.status.eventIndex ) { + Bot_ScriptLog_Entry( bs, qfalse, Bot_LineText( bs->script.data->events[statusBackup.eventIndex].text ), "**TERMINATED**\r\n" ); + } + } +} + +/* +================ +Bot_ScriptEvent + + An event has occured, for which a script may exist +================ +*/ +void Bot_ScriptEvent( int entityNum, char *eventStr, char *params ) { + int i, eventNum; + bot_state_t *bs; + + if ( entityNum < 0 || entityNum >= MAX_CLIENTS ) { + G_Error( "Bot_ScriptEvent: entityNum out of range (%i)", entityNum ); + } + + bs = &botstates[entityNum]; + if ( !bs->inuse ) { + return; + } + if ( !bs->script.data ) { + return; + } + + eventNum = -1; + + // find out which event this is + for ( i = 0; botScriptEvents[i].eventStr; i++ ) + { + if ( !Q_stricmp( eventStr, botScriptEvents[i].eventStr ) ) { // match found + eventNum = i; + break; + } + } + + // + // show debugging info + if ( g_scriptDebug.integer ) { + if ( g_entities[entityNum].scriptName ) { + G_Printf( "%i : (%s) GScript event: %s %s\n", level.time, g_entities[entityNum].scriptName, eventStr, params ? params : "" ); + } else { + G_Printf( "%i : (n/a) GScript event: %s %s\n", level.time, eventStr, params ? params : "" ); + } + } + // + + if ( eventNum < 0 ) { + if ( g_cheats.integer ) { // dev mode + G_Printf( "devmode-> Bot_ScriptEvent(), unknown event: %s\n", eventStr ); + } + } + + // see if this cast has this event + for ( i = 0; i < bs->script.data->numEvents; i++ ) + { + if ( bs->script.data->events[i].eventNum == eventNum ) { + if ( ( !bs->script.data->events[i].params ) + || ( !botScriptEvents[eventNum].eventMatch || botScriptEvents[eventNum].eventMatch( &bs->script.data->events[i], params ) ) ) { + Bot_ScriptChange( bs, i ); + break; + } + } + } +} + +/* +================ +Bot_ForceScriptEvent + + Definately run this event now, overriding any paused state +================ +*/ +void Bot_ForceScriptEvent( int entityNum, char *eventStr, char *params ) { + int oldPauseTime; + bot_state_t *bs; + + if ( entityNum >= MAX_CLIENTS ) { + return; + } + + bs = &botstates[entityNum]; + if ( !bs->inuse ) { + return; + } + if ( !bs->script.data ) { + return; + } + + oldPauseTime = bs->script.pauseTime; + bs->script.pauseTime = 0; + + Bot_ScriptEvent( entityNum, eventStr, params ); + + bs->script.pauseTime = oldPauseTime; +} + +/* +================ +Bot_TeamScriptEvent +================ +*/ +void Bot_TeamScriptEvent( int team, char *eventStr, char *params ) { + int i; + bot_state_t *bs; + // + for ( i = 0; i < level.numConnectedClients; i++ ) { + bs = &botstates[level.sortedClients[i]]; + if ( !bs->inuse ) { + continue; + } + if ( bs->sess.sessionTeam != team ) { + continue; + } + + Bot_ScriptEvent( level.sortedClients[i], eventStr, params ); + } +} + +/* +============= +Bot_ScriptRun + + returns qtrue if the script completed +============= +*/ +qboolean Bot_ScriptRun( bot_state_t *bs, qboolean force ) { + bot_script_stack_t *stack; + bot_script_stack_item_t *item; + int oldScriptId; + + if ( !bs->script.data ) { + return qtrue; + } + + // turn off flags that get set each frame while they are active + bs->script.frameFlags = 0; + + if ( bs->script.status.eventIndex < 0 ) { + return qtrue; + } + + if ( !bs->script.data->events ) { + bs->script.status.eventIndex = -1; + return qtrue; + } + + if ( !force && ( bs->script.pauseTime >= level.time ) ) { + return qtrue; + } + + stack = &bs->script.data->events[bs->script.status.eventIndex].stack; + + if ( !stack->numItems ) { + bs->script.status.eventIndex = -1; + return qtrue; + } + + while ( bs->script.status.stackHead < stack->numItems ) + { + item = &bs->script.data->items[ stack->startIndex + bs->script.status.stackHead ]; + bs->script.status.currentItem = item; + // + if ( bs->script.flags & BSFL_FIRST_CALL ) { + Bot_ScriptLog_Entry( bs, qtrue, Bot_LineText( bs->script.data->events[bs->script.status.eventIndex].text ), "" ); + } + // + oldScriptId = bs->script.status.id; + // + + // Mad Doc - TDf + if ( G_IsSinglePlayerGame() ) { + if ( bot_debug.integer ) { + trap_SendServerCommand( 0, va( "botdebugprint %i \"Line: %i %s %s\"", bs->client, Bot_Script_GetCurrentLine( bs ), item->action->actionString, item->params ) ); + } + } + + if ( !item->action->actionFunc( bs, item->params ) ) { + bs->script.flags &= ~BSFL_FIRST_CALL; + return qfalse; + } + // if our script changed, stop execution + if ( oldScriptId != bs->script.status.id ) { + return qfalse; + } + // move to the next action in the script + bs->script.status.stackHead++; + // record the time that this new item became active + bs->script.status.stackChangeTime = level.time; + // reset misc stuff + bs->script.flags |= BSFL_FIRST_CALL; + } + + Bot_ScriptLog_Entry( bs, qtrue, Bot_LineText( bs->script.data->events[bs->script.status.eventIndex].text ), "** FINISHED **" ); + + bs->script.status.eventIndex = -1; + + return qtrue; +} + +/* +================= +Bot_Script_GetCurrentLine +================= +*/ +int Bot_Script_GetCurrentLine( bot_state_t *bs ) { + if ( bs->script.status.eventIndex < 0 ) { + return -1; + } + if ( !bs->script.status.currentItem ) { + return -1; + } + return bs->script.status.currentItem->lineNum; +} + +/* +================= +Bot_ScriptLog_Entry +================= +*/ +void Bot_ScriptLog_Entry( bot_state_t *bs, qboolean showDetails, char *preText, char *fmt, ... ) { + va_list ap; + char text[1024], *pStr, *token; + fileHandle_t f; + int i; + // + if ( !( f = bs->script.logFile ) ) { + return; + } + // + // timestamp + // get the time/date + Q_strncpyz( text, va( "(%i) ", level.time ), sizeof( text ) ); + trap_FS_Write( text, strlen( text ), f ); + // + i = 40; // padding for indentation + // pretext + if ( preText ) { + trap_FS_Write( preText, strlen( preText ), f ); + i -= strlen( preText ); + if ( i < 0 ) { + i = 0; + } + } + // indentation + while ( i-- ) trap_FS_Write( " ", 1, f ); + // + if ( showDetails && ( Bot_Script_GetCurrentLine( bs ) > -1 ) ) { + // show the current script line and text + Q_strncpyz( text, va( "(line %i:", Bot_Script_GetCurrentLine( bs ) ), sizeof( text ) ); + trap_FS_Write( text, strlen( text ), f ); + // text + pStr = bs->script.status.currentItem->text; + while ( ( token = COM_ParseExt( &pStr, qfalse ) ) && token[0] ) { + trap_FS_Write( " ", 1, f ); + trap_FS_Write( token, strlen( token ), f ); + } + trap_FS_Write( ") ", 2, f ); + } + // + if ( fmt ) { + va_start( ap, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, ap ); + if ( strlen( text ) >= sizeof( text ) ) { + //G_Error( "Bot_ScriptLog_Entry: text exceeded buffer size" ); + // just cut it short + text[sizeof( text ) - 1] = '\0'; + } + va_end( ap ); + // + trap_FS_Write( text, strlen( text ), f ); + } + trap_FS_Write( "\r\n", 2, f ); +} + +/* +================ +Bot_ScriptThink +================ +*/ +void Bot_ScriptThink( void ) { + int i; + bot_state_t *bs; + + for ( i = 0; i < level.maxclients; i++ ) { + // get the bot for this entity num + bs = &botstates[i]; + + // if there's no bot here, skip it + if ( !bs->inuse ) { + continue; + } + + // if the bot is dead, skip it + if ( BotIsDead( bs ) ) { + continue; + } + + Bot_ScriptRun( bs, qfalse ); + } +} diff --git a/src/botai/ai_script_actions.c b/src/botai/ai_script_actions.c new file mode 100644 index 0000000..ce7c774 --- /dev/null +++ b/src/botai/ai_script_actions.c @@ -0,0 +1,2209 @@ +/* +=========================================================================== + +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: ai_script_actions.c +// Function: Wolfenstein Bot Scripting +// Programmer: Ridah +// Tab Size: 4 (real tabs) +//=========================================================================== + +#include "../game/g_local.h" +#include "../game/q_shared.h" +#include "../game/botlib.h" //bot lib interface +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../botai/botai.h" //bot ai interface + +#include "inv.h" //indexes into the inventory + +#include "ai_main.h" +#include "ai_dmq3.h" + +/* +Contains the code to handle the various commands available with an event script. + +These functions will return true if the action has been performed, and the script +should proceed to the next item on the list. +*/ + +qboolean Bot_ScriptAction_SetSpeed( bot_state_t* bs, char *params ) { + vec3_t speed; + char* pString; + int i; + char* token; + + pString = params; + for ( i = 0; i < 3; i++ ) { + token = COM_Parse( &pString ); + if ( !token || !*token ) { + G_Error( "G_Scripting: syntax: setspeed \n" ); + } + speed[i] = atoi( token ); + } + + VectorAdd( g_entities[bs->entitynum].client->ps.velocity, speed, g_entities[bs->entitynum].client->ps.velocity ); + + return qtrue; +} + +/* +================= +Bot_ScriptError +================= +*/ +void Bot_ScriptError( bot_state_t *bs, char *fmt, ... ) { + va_list ap; + char text[512]; + // + va_start( ap, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, ap ); + if ( strlen( text ) >= sizeof( text ) ) { + text[sizeof( text ) - 1] = '\0'; + } + // + G_Error( "BotScript (line %i): %s", bs->script.status.currentItem->lineNum, text ); + // + va_end( ap ); +} + +/* +================= +Bot_ScriptAction_Print + + syntax: print + + Mostly for debugging purposes +================= +*/ +qboolean Bot_ScriptAction_Print( bot_state_t *bs, char *params ) { + char *pString, *token, *printThis; + int printLevel = 0; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "print requires some text" ); + } + + // Start parsing at the beginning of the string + pString = params; + + // Default to printing whole string + printThis = params; + + // See if the first parameter is a /N, where N is a number + if ( ( token = COM_ParseExt( &pString, qfalse ) ) && token[0] == '/' ) { + // Get the integer version of the print debug level + printLevel = atoi( &( token[1] ) ); + + // Just print what's left + printThis = pString; + + } + + // Only print if our debug level is as high as the print level + if ( g_scriptDebugLevel.integer >= printLevel ) { + // Print the statement + G_Printf( "(BotScript) %s-> %s\n", g_entities[bs->entitynum].client->pers.netname, printThis ); + + } // if (g_scriptDebugLevel.integer >= printLevel)... + + return qtrue; +} + +/* +================= +Bot_ScriptAction_SetAccumToPlayerCount + + syntax: SetAccumToPlayerCount [[ ] [ ] ...] + + condition list: team [axis/allies], class [medic/engineer/etc], weapon [flamethrower/sniperrifle/etc], within_range +================= +*/ +qboolean Bot_ScriptAction_SetAccumToPlayerCount( bot_state_t *bs, char *params ) { + char *pStr, *pStrBackup, *token; + int count, i, val, accum, weapons[2]; + gitem_t *item = NULL; + gentity_t *ent; + byte validPlayers[MAX_CLIENTS]; + + // setup the list of starting validPlayers + memset( validPlayers, 0, sizeof( validPlayers ) ); + count = 0; + for ( i = 0; i < level.maxclients; i++ ) { + if ( !g_entities[i].inuse ) { + continue; + } + if ( !g_entities[i].client ) { + continue; + } + if ( !g_entities[i].client->pers.connected != CON_CONNECTED ) { + continue; + } + validPlayers[i] = 1; + count++; + } + + pStr = params; + + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || ( token[0] < '0' ) || ( token[0] > '9' ) ) { + Bot_ScriptError( bs, "accum buffer index expected, %s found: SetAccumToPlayerCount %s", token, params ); + } + accum = atoi( token ); + if ( accum < 0 || accum >= MAX_SCRIPT_ACCUM_BUFFERS ) { + Bot_ScriptError( bs, "accum buffer index out of range, %s found (range is 0 - %i): SetAccumToPlayerCount %s", token, MAX_SCRIPT_ACCUM_BUFFERS - 1, params ); + } + + // eliminate them with each condition not met + while ( qtrue ) { + val = 0; + // + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] ) { + // we're done + break; + } + // + if ( token[0] != '/' ) { + Bot_ScriptError( bs, "condition identifier expected, %s found: SetAccumToPlayerCount %s", token, params ); + } + // + if ( !Q_stricmp( token, "/team" ) ) { + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + Bot_ScriptError( bs, "unexpected end of command: SetAccumToPlayerCount %s", params ); + } + // + if ( !Q_stricmp( token, "axis" ) ) { + val = TEAM_AXIS; + } else if ( !Q_stricmp( token, "allies" ) ) { + val = TEAM_ALLIES; + } else { + Bot_ScriptError( bs, "unknown team \"%s\": SetAccumToPlayerCount %s", token, params ); + } + // eliminate players + for ( i = 0; i < level.maxclients; i++ ) { + if ( !validPlayers[i] ) { + continue; + } + if ( g_entities[i].client->sess.sessionTeam != val ) { + validPlayers[i] = 0; + count--; + } + } + } else + // + if ( !Q_stricmp( token, "/class" ) ) { + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + Bot_ScriptError( bs, "unexpected end of command: SetAccumToPlayerCount %s", params ); + } + // + val = Team_ClassForString( token ); + if ( val < 0 ) { + Bot_ScriptError( bs, "unknown class \"%s\": SetAccumToPlayerCount %s", token, params ); + } + // eliminate players + for ( i = 0; i < level.maxclients; i++ ) { + if ( !validPlayers[i] ) { + continue; + } + if ( g_entities[i].client->sess.playerType != val ) { + validPlayers[i] = 0; + count--; + } + } + } else + // + if ( !Q_stricmp( token, "/weapon" ) ) { + memset( weapons, 0, sizeof( weapons ) ); + // for each weapon + while ( qtrue ) { + // read the weapon + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + Bot_ScriptError( bs, "unexpected end of command: SetAccumToPlayerCount %s", params ); + } + // + if ( ( item = BG_FindItem( token ) ) ) { + if ( !item->giTag ) { + Bot_ScriptError( bs, "unknown weapon \"%s\": SetAccumToPlayerCount %s", token, params ); + } + COM_BitSet( weapons, item->giTag ); + } else { + Bot_ScriptError( bs, "unknown weapon \"%s\": SetAccumToPlayerCount %s", token, params ); + } + // + pStrBackup = pStr; + token = COM_ParseExt( &pStr, qfalse ); + if ( !token[0] || ( Q_stricmp( token, "or" ) != 0 ) ) { + // not OR, so drop out of here + pStr = pStrBackup; + break; + } + } + // eliminate players + for ( i = 0; i < level.maxclients; i++ ) { + if ( !validPlayers[i] ) { + continue; + } + if ( !( g_entities[i].client->ps.weapons[0] & weapons[0] ) && !( g_entities[i].client->ps.weapons[1] & weapons[1] ) ) { + validPlayers[i] = 0; + count--; + } + } + } else + // + if ( !Q_stricmp( token, "/within_range" ) ) { + // targetname + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + Bot_ScriptError( bs, "unexpected end of command: SetAccumToPlayerCount %s", params ); + } + ent = G_FindByTargetname( NULL, token ); + if ( !ent ) { + Bot_ScriptError( bs, "unknown spawn point \"%s\": SetAccumToPlayerCount %s", token, params ); + } + // range + token = COM_ParseExt( &pStr, qfalse ); + if ( !token || !token[0] || token[0] == '/' ) { + Bot_ScriptError( bs, "range expected, not found: SetAccumToPlayerCount %s", params ); + } + // + // eliminate players + for ( i = 0; i < level.maxclients; i++ ) { + if ( !validPlayers[i] ) { + continue; + } + if ( VectorDistanceSquared( g_entities[i].r.currentOrigin, ent->s.origin ) > SQR( atof( token ) ) ) { + validPlayers[i] = 0; + count--; + } + } + } + } + // + bs->script.accumBuffer[accum] = count; + // + return qtrue; +} + +/* +====================== +Bot_ScriptAction_SpawnBot + + see Svcmd_SpawnBot() +====================== +*/ +qboolean Bot_ScriptAction_SpawnBot( bot_state_t *bs, char *params ) { + //trap_SendConsoleCommand( EXEC_APPEND, va("spawnbot %s\n", params) ); + G_SpawnBot( params ); + return qtrue; +} + +/* +================= +Bot_ScriptAction_Accum + + syntax: accum + + Commands: + + accum inc + accum abort_if_less_than + accum abort_if_greater_than + accum abort_if_not_equal + accum abort_if_equal + accum set_to + accum random + accum bitset + accum bitclear + accum abort_if_bitset + accum abort_if_not_bitset +================= +*/ + +qboolean Bot_ScriptAction_Trigger( bot_state_t *bs, char *params ); + +qboolean Bot_ScriptAction_Accum( bot_state_t *bs, char *params ) { + char *pString, *token, lastToken[MAX_QPATH]; + int bufferIndex; + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: without a buffer index" ); + } + + bufferIndex = atoi( token ); + if ( bufferIndex >= MAX_SCRIPT_ACCUM_BUFFERS ) { + Bot_ScriptError( bs, "accum: buffer is outside range (0 - %i)", MAX_SCRIPT_ACCUM_BUFFERS ); + } + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: without a command" ); + } + + Q_strncpyz( lastToken, token, sizeof( lastToken ) ); + token = COM_ParseExt( &pString, qfalse ); + + if ( !Q_stricmp( lastToken, "inc" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum:: %s requires a parameter", lastToken ); + } + bs->script.accumBuffer[bufferIndex] += atoi( token ); + } else if ( !Q_stricmp( lastToken, "abort_if_less_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] < atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_greater_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] > atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_equal" ) || !Q_stricmp( lastToken, "abort_if_not_equals" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] != atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] == atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + bs->script.accumBuffer[bufferIndex] |= ( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "bitclear" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + bs->script.accumBuffer[bufferIndex] &= ~( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "abort_if_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( !( bs->script.accumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "set_to" ) || !Q_stricmp( lastToken, "set" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + bs->script.accumBuffer[bufferIndex] = atoi( token ); + } else if ( !Q_stricmp( lastToken, "random" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + bs->script.accumBuffer[bufferIndex] = rand() % atoi( token ); + } else if ( !Q_stricmp( lastToken, "trigger_if_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "accum: %s requires a parameter", lastToken ); + } + if ( bs->script.accumBuffer[bufferIndex] == atoi( token ) ) { + return Bot_ScriptAction_Trigger( bs, pString ); + } + } else { + Bot_ScriptError( bs, "accum: %s: unknown command", params ); + } + + return qtrue; +} + +/* +====================== +Bot_ScriptAction_Wait +====================== +*/ +qboolean Bot_ScriptAction_Wait( bot_state_t *bs, char *params ) { + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "Wait requires a duration." ); + } + if ( !atoi( params ) ) { + Bot_ScriptError( bs, "Wait has invalid duration." ); + } + // + // Gordon: check <= 0 instead of == 0? + return ( bs->script.status.stackChangeTime < level.time - atoi( params ) ); +} + +/* +====================== +Bot_ScriptAction_MoveToMarker +====================== +*/ +qboolean Bot_ScriptAction_MoveToMarker( bot_state_t *bs, char *params ) { + char *pString, *token; + g_serverEntity_t *target; + vec3_t vec; + + // Gordon: 24/10/02 + float radius = 24; + + + // TAT 11/14/2002 - This causes a crash if you have guys on patrol routes + // Why did we need it? + // Gordon: 27/11/02: dont want to teleport dead players, can move further down, + // but i see no reason a general early out wouldn't be as good + // why does it crash? + /* + // Gordon: 6/11/02 + // cant move if we're dead... + if(g_entities[bs->entitynum].health <= 0) { + return qtrue; + } + */ + + // TAT 8/20/2002 + // If we have received a player command that overrides our movement scripts, + // we should end the script + if ( bs->overrideMovementScripts ) { + // We're done with this script + return qtrue; +// return qfalse; + } + + // Did this move fail? + if ( bs->movementFailedBadly ) { +// Bot_ScriptError( bs, "MoveToMarker failed." ); + + // Cancel the move + bs->movementFailedBadly = qfalse; + + // We're done here + return qtrue; + + } // if (bs->movementFailedBadly)... + + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "MoveToMarker requires a targetname." ); + } + // + pString = params; + token = COM_ParseExt( &pString, qfalse ); + target = FindServerEntity( NULL, SE_FOFS( name ), token ); + if ( !target ) { + Bot_ScriptError( bs, "MoveToMarker has unknown targetname: \"%s\"", token ); + } + // + bs->script.frameFlags |= BSFFL_MOVETOTARGET; + bs->script.entityNum = target->number; + bs->script.moveType = BSMT_DEFAULT; + // + while ( ( token = COM_ParseExt( &pString, qfalse ) ) && token[0] ) { + if ( !Q_stricmp( token, "/WALKING" ) ) { + bs->script.moveType = BSMT_WALKING; + } else if ( !Q_stricmp( token, "/CROUCHING" ) ) { + bs->script.moveType = BSMT_CROUCHING; + } else if ( !Q_stricmp( token, "/DIRECT" ) ) { + bs->script.frameFlags |= BSFFL_DIRECTMOVE; + // Gordon: 24/10/02 Adding ability to set custom radius for movement goal + } else if ( !Q_stricmp( token, "radius" ) ) { + token = COM_ParseExt( &pString, qfalse ); + if ( !*token ) { + Bot_ScriptError( bs, "MoveToMarker with radius has no value" ); + } else { + radius = atof( token ); + } + // Gordon: 24/10/02 Adding ability to teleport a bot somewhere + } else if ( !Q_stricmp( token, "instant" ) ) { + TeleportPlayer( &g_entities[bs->entitynum], target->origin, g_entities[bs->entitynum].client->ps.viewangles ); + return qtrue; + // Gordon: end + } + } + // START Mad Doctor I changes, 8/19/2002 + // The check was too constrictive and failed too often. I may have made it too loose, but it seems to work. + // if we have passed or are close enough to the marker, then return qtrue + // Gordon: added custom radius ability, default is 24 as Ian had set here + if ( VectorDistanceSquared( bs->origin, target->origin ) < SQR( radius ) ) { +// if (VectorDistanceSquared( bs->origin, target->s.origin ) < SQR(12)) { + // END Mad Doctor I changes, 8/19/2002 + return qtrue; + } else if ( ( bs->script.status.stackChangeTime < level.time - 500 ) && VectorDistanceSquared( bs->origin, target->origin ) < SQR( 48 ) ) { + VectorSubtract( target->origin, bs->origin, vec ); + if ( DotProduct( bs->cur_ps.velocity, vec ) < 0 ) { + return qtrue; + } + } + // + return qfalse; +} + +/* +===================== +Bot_ScriptAction_Trigger +===================== +*/ +qboolean Bot_ScriptAction_Trigger( bot_state_t *bs, char *params ) { + gentity_t *trent, *ent; + char *pString, name[MAX_QPATH], trigger[MAX_QPATH], *token; + int oldId, i; + qboolean terminate, found; + + // get the cast name + pString = params; + token = COM_ParseExt( &pString, qfalse ); + Q_strncpyz( name, token, sizeof( name ) ); + if ( !name[0] ) { + G_Error( "G_Scripting: trigger must have a name and an identifier\n" ); + } + + token = COM_ParseExt( &pString, qfalse ); + Q_strncpyz( trigger, token, sizeof( trigger ) ); + if ( !trigger[0] ) { + G_Error( "G_Scripting: trigger must have a name and an identifier\n" ); + } + + ent = BotGetEntity( bs->client ); + + // START Mad Doctor I changes, 8/14/2002 + // Changes to fix bugs caused if you used "trigger Foo FooAction" within bot Foo's + // scripts instead of using "trigger self FooAction". + if ( ( !Q_stricmp( name, "self" ) ) || ( !Q_stricmp( name, ent->scriptName ) ) ) { + trent = ent; + oldId = bs->script.status.id; + Bot_ScriptEvent( bs->client, "trigger", trigger ); + + // See if we've popped back to the original script + return ( oldId == bs->script.status.id ); +// return (oldId == trent->scriptStatus.scriptId); + // END Mad Doctor I changes, 8/14/2002 + } else if ( !Q_stricmp( name, "global" ) ) { + terminate = qfalse; + found = qfalse; + // for all entities/bots + trent = g_entities; + for ( i = 0; i < level.num_entities; i++, trent++ ) { + if ( !trent->inuse ) { + continue; + } + if ( !trent->scriptName ) { + continue; + } + if ( !trent->scriptName[0] ) { + continue; + } + found = qtrue; + if ( !( trent->r.svFlags & SVF_BOT ) ) { + G_Script_ScriptEvent( trent, "trigger", trigger ); + } else { + oldId = bs->script.status.id; + Bot_ScriptEvent( bs->client, "trigger", trigger ); + // if the script changed, return false so we don't muck with it's variables + if ( ( trent == ent ) && ( oldId != bs->script.status.id ) ) { + terminate = qtrue; + } + } + } + // + if ( terminate ) { + return qfalse; + } + if ( found ) { + return qtrue; + } + } else { + terminate = qfalse; + found = qfalse; + // for all entities/bots with this scriptName + trent = NULL; + while ( ( trent = BotFindEntity( trent, FOFS( scriptName ), name ) ) ) { + found = qtrue; + if ( !( trent->r.svFlags & SVF_BOT ) ) { + oldId = trent->scriptStatus.scriptId; + G_Script_ScriptEvent( trent, "trigger", trigger ); + // if the script changed, return false so we don't muck with it's variables + if ( ( trent == ent ) && ( oldId != trent->scriptStatus.scriptId ) ) { + terminate = qtrue; + } + } else { + Bot_ScriptEvent( trent->s.number, "trigger", trigger ); + } + } + // + if ( terminate ) { + return qfalse; + } + + // Did we find a bot? + if ( found ) { + + // We found one, and triggered the action, so keep going on with our script + return qtrue; + + } // if (found) ... + + + } // else... + +// G_Error( "G_Scripting: trigger has unknown name: %s\n", name ); + G_Printf( "G_Scripting: trigger has unknown name: %s\n", name ); + return qfalse; // shutup the compiler +} + +/* +===================== +Bot_ScriptAction_Logging +===================== +*/ +qboolean Bot_ScriptAction_Logging( bot_state_t *bs, char *params ) { + struct tm *localTime; + time_t long_time; + char filename[MAX_QPATH]; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "Logging requires an ON/OFF" ); + } + if ( !Q_stricmp( params, "ON" ) ) { + if ( bs->script.flags & BSFL_LOGGING ) { + // logging already started + return qtrue; + } + bs->script.flags |= BSFL_LOGGING; + // get the time/date + time( &long_time ); + localTime = localtime( &long_time ); + Q_strncpyz( filename, va( "BotLog_%s_[%i]_[%4i_%2i_%2i]_[%2i_%2i_%2i].txt", g_entities[bs->client].aiName, bs->client, 1900 + localTime->tm_year, 1 + localTime->tm_mon, 1 + localTime->tm_mday, localTime->tm_hour, localTime->tm_min, localTime->tm_sec ), sizeof( filename ) ); + // open the log file + if ( trap_FS_FOpenFile( filename, &bs->script.logFile, FS_APPEND ) < 0 ) { + Bot_ScriptError( bs, "Cannot open file for logging: %s", filename ); + } + } else if ( !Q_stricmp( params, "OFF" ) ) { + if ( !( bs->script.flags & BSFL_LOGGING ) ) { + // logging already started + return qtrue; + } + bs->script.flags &= ~BSFL_LOGGING; + trap_FS_FCloseFile( bs->script.logFile ); + bs->script.logFile = 0; + } else { + Bot_ScriptError( bs, "Logging has unknown parameter (%s), expected ON/OFF", params ); + } + + return qtrue; +} + +/* +===================== +Bot_ScriptAction_AbortIfWarmup +===================== +*/ +qboolean Bot_ScriptAction_AbortIfWarmup( bot_state_t *bs, char *params ) { + if ( level.warmupTime ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + // + return qtrue; +} + +char *botAttributeStrings[] = +{ + + + + "BOT_REACTION_TIME", + "BOT_AIM_ACCURACY", + + + + + + "BOT_WIMP_FACTOR", + NULL +}; + +/* +============= +Bot_ScriptAction_SetAttribute + + returns qfalse if error +============= +*/ +qboolean Bot_ScriptAction_SetAttribute( bot_state_t *bs, char *params ) { + int i; + char *pString, *token; + + // get the attribString + pString = params; + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "attribute string required" ); + } + + for ( i = 0; botAttributeStrings[i]; i++ ) { + if ( !Q_stricmp( botAttributeStrings[i], token ) ) { + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "attribute value required" ); + } + bs->attribs[i] = atof( token ); + return qtrue; + } + } + + // Let's give a big old error right here! + Bot_ScriptError( bs, "SetAttribute: Invalid attribute %s.", token ); + + return qfalse; +} + +/* +=============== +Bot_ScriptAction_MountMG42 +=============== +*/ +qboolean Bot_ScriptAction_MountMG42( bot_state_t *bs, char *params ) { + gentity_t *mg42, *mg42Spot; + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "MountMG42 requires a targetname" ); + } + // find the mg42 + mg42 = NULL; + while ( ( mg42 = BotFindNextStaticEntity( mg42, BOTSTATICENTITY_MG42 ) ) ) { + if ( !Q_stricmp( mg42->targetname, params ) ) { + break; + } + } + // + if ( !mg42 ) { + Bot_ScriptError( bs, "MountMG42: targetname \"%s\" not found", params ); + } + // + mg42Spot = mg42->melee; + // + if ( !mg42Spot ) { + Bot_ScriptError( bs, "MountMG42: (internal error) mg42 (\"%s\") has invalid mg42_spot", params ); + } + // + bs->script.flags |= BSFL_MOUNT_MG42; + bs->script.mg42entnum = mg42Spot->s.number; + // + return qtrue; +} + +// START Mad Doctor I changes, 8/19/2002 + +/* +=============== +Bot_ScriptAction_PlaySoundAtPlayer + + +=============== +*/ +qboolean Bot_ScriptAction_PlaySoundAtPlayer( bot_state_t *bs, char *params ) { + // We need to find out who the player is + gentity_t *player = NULL; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "PlaySound requires a soundname" ); + } + + // Look up the player entity by name + // NOTE: This would need adjusting for COOP play. + player = BotFindEntityForName( "player" ); + + // Bail if no player exists + if ( player == NULL ) { + return qtrue; + } + + // + G_AddEvent( player, EV_GENERAL_SOUND, G_SoundIndex( params ) ); + // + return qtrue; + +} // qboolean Bot_ScriptAction_PlaySoundAtPlayer( bot_state_t *bs, char *params )... + +// END Mad Doctor I changes, 8/19/2002 + + +extern qboolean AddWeaponToPlayer( gclient_t *client, weapon_t weapon, int ammo, int ammoclip, qboolean setcurrent ); + +/* +=============== +Bot_ScriptAction_SetWeapon +=============== +*/ +qboolean Bot_ScriptAction_SetWeapon( bot_state_t *bs, char *params ) { + char userinfo[MAX_INFO_STRING]; + + // Which class are we? + int playerClass = g_entities[bs->client].client->sess.playerType; + + int weapon; + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetWeapon requires a weapon name" ); + } + + + weapon = Bot_GetWeaponForClassAndTeam( playerClass, g_entities[bs->client].client->sess.sessionTeam, params ); + if ( weapon == -1 ) { + // can't use this weapon + Bot_ScriptError( bs, "Bot %s on team %s cannot use weapon %s\n", g_entities[bs->client].aiName, ( g_entities[bs->client].client->sess.sessionTeam == TEAM_AXIS ) ? "Axis" : "Allies", params ); + } + + if ( weapon == WP_NONE ) { + trap_GetUserinfo( bs->client, userinfo, sizeof( userinfo ) ); + Info_SetValueForKey( userinfo, "pWeapon", "NONE" ); + trap_SetUserinfo( bs->client, userinfo ); + ClientUserinfoChanged( bs->client ); + // TAT 12/26/2002 - make sure the bot state knows we have no weapon + bs->weaponnum = WP_NONE; + } else + { + gentity_t *player; + int i; + // make sure we tell all the clients that we have a weapon + for ( i = 0; i < level.numConnectedClients; i++ ) + { + // send the noweapon command with who as the 1st param, and 0 meaning we don't have no weapon + player = g_entities + level.sortedClients[i]; + if ( player->inuse && player->client->sess.sessionTeam == bs->mpTeam ) { + trap_SendServerCommand( player->s.number, va( "nwp %i 0", bs->client ) ); + } + } +/* + trap_GetUserinfo( bs->client, userinfo, sizeof(userinfo) ); + Info_SetValueForKey( userinfo, "pWeapon", va("%i", weapon)); + trap_SetUserinfo( bs->client, userinfo ); + ClientUserinfoChanged(bs->client); +*/ + + } + + // set the weapon + g_entities[bs->client].client->sess.playerWeapon = weapon; + g_entities[bs->client].client->ps.weapon = weapon; + g_entities[bs->client].s.weapon = weapon; + + // use this new func: don't bother with the whole weaponSpawnNumber thing + SetWolfSpawnWeapons( g_entities[bs->client].client ); + + if ( weapon != WP_NONE ) { +// AddWeaponToPlayer( g_entities[bs->client].client, weapon, 1, 0, qtrue ); + + // Make sure this weapon is allowed + COM_BitSet( g_entities[bs->client].client->ps.weapons, weapon ); + + //@TODO set ammo with an extra toekn +// client->ps.ammoclip[BG_FindClipForWeapon(weapon)] = ammoclip; +// client->ps.ammo[BG_FindAmmoForWeapon(weapon)] = ammo; + + // Make this the current weapon + g_entities[bs->client].client->ps.weapon = weapon; + + } // if (weapon != WP_NONE)... + + return qtrue; +} + +/* +=============== +Bot_ScriptAction_SetClass +=============== +*/ +qboolean Bot_ScriptAction_SetClass( bot_state_t *bs, char *params ) { + int val = -1; + char userinfo[MAX_INFO_STRING]; + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetClass requires a class name" ); + } + // + if ( !Q_stricmp( params, "ANY" ) ) { + val = -1; + } else if ( !Q_stricmp( params, "soldier" ) ) { + val = PC_SOLDIER; + } else if ( !Q_stricmp( params, "medic" ) ) { + val = PC_MEDIC; + } else if ( !Q_stricmp( params, "engineer" ) ) { + val = PC_ENGINEER; + } else if ( !Q_stricmp( params, "lieutenant" ) ) { // FIXME: remove this from missionpack? once all scripts have been updated + val = PC_FIELDOPS; + } else if ( !Q_stricmp( params, "fieldops" ) ) { + val = PC_FIELDOPS; + } else if ( !Q_stricmp( params, "covertops" ) ) { + val = PC_COVERTOPS; + } else { + Bot_ScriptError( bs, "unknown class \"%s\"", params ); + } + // + trap_GetUserinfo( bs->client, userinfo, sizeof( userinfo ) ); + Info_SetValueForKey( userinfo, "pClass", va( "%i", val ) ); + trap_SetUserinfo( bs->client, userinfo ); + // + return qtrue; +} + +/* +================= +Bot_ScriptAction_GlobalAccum + + syntax: globalAccum + + Commands: + + globalAccum inc + globalAccum abort_if_less_than + globalAccum abort_if_greater_than + globalAccum abort_if_not_equal + globalAccum abort_if_equal + globalAccum set_to + globalAccum random + globalAccum bitset + globalAccum bitclear + globalAccum abort_if_bitset + globalAccum abort_if_not_bitset +================= +*/ +qboolean Bot_ScriptAction_GlobalAccum( bot_state_t *bs, char *params ) { + char *pString, *token, lastToken[MAX_QPATH]; + int bufferIndex; + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: without a buffer index" ); + } + + bufferIndex = atoi( token ); + if ( bufferIndex >= MAX_SCRIPT_ACCUM_BUFFERS ) { + Bot_ScriptError( bs, "globalAccum: buffer is outside range (0 - %i)", MAX_SCRIPT_ACCUM_BUFFERS ); + } + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: without a command" ); + } + + Q_strncpyz( lastToken, token, sizeof( lastToken ) ); + token = COM_ParseExt( &pString, qfalse ); + + if ( !Q_stricmp( lastToken, "inc" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum:: %s requires a parameter", lastToken ); + } + level.globalAccumBuffer[bufferIndex] += atoi( token ); + } else if ( !Q_stricmp( lastToken, "abort_if_less_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( level.globalAccumBuffer[bufferIndex] < atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_greater_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( level.globalAccumBuffer[bufferIndex] > atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_equal" ) || !Q_stricmp( lastToken, "abort_if_not_equals" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( level.globalAccumBuffer[bufferIndex] != atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( level.globalAccumBuffer[bufferIndex] == atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + level.globalAccumBuffer[bufferIndex] |= ( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "bitclear" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + level.globalAccumBuffer[bufferIndex] &= ~( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "abort_if_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( level.globalAccumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + if ( !( level.globalAccumBuffer[bufferIndex] & ( 1 << atoi( token ) ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "set_to" ) || !Q_stricmp( lastToken, "set" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + level.globalAccumBuffer[bufferIndex] = atoi( token ); + } else if ( !Q_stricmp( lastToken, "random" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "globalAccum: %s requires a parameter", lastToken ); + } + level.globalAccumBuffer[bufferIndex] = rand() % atoi( token ); + } else { + Bot_ScriptError( bs, "globalAccum: %s: unknown command", params ); + } + + return qtrue; +} + +/* +================= +Bot_ScriptAction_FollowLeader +================= +*/ +qboolean Bot_ScriptAction_FollowLeader( bot_state_t *bs, char *params ) { + char *pString, *token; + gentity_t *target; +// vec3_t vec; + int duration; + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "FollowLeader requires a name." ); + } + + // TAT 8/20/2002 + // If we have received a player command that overrides our movement scripts, + // we should end the script + if ( bs->overrideMovementScripts ) { + return qfalse; + } + + // + pString = params; + token = COM_ParseExt( &pString, qfalse ); + target = BotFindEntityForName( token ); + if ( !target ) { + if ( bs->script.status.stackChangeTime != level.time ) { + // Gordon: lets assume they have died... + return qtrue; + } + Bot_ScriptError( bs, "FollowLeader has unknown name: \"%s\"", token ); + } + + if ( target->health <= 0 ) { + // Gordon: dead, dont follow + return qtrue; + } + // read the duration + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "FollowLeader requires a duration" ); + } + if ( !Q_stricmp( token, "forever" ) ) { + duration = (int)0x7fffffff; + } else { + duration = atoi( token ); + } + // + bs->script.frameFlags |= BSFFL_FOLLOW_LEADER; + bs->script.entityNum = target->s.number; + bs->script.moveType = BSMT_DEFAULT; + // + while ( ( token = COM_ParseExt( &pString, qfalse ) ) && token[0] ) { + if ( !Q_stricmp( token, "/WALKING" ) ) { + bs->script.moveType = BSMT_WALKING; + } else if ( !Q_stricmp( token, "/CROUCHING" ) ) { + bs->script.moveType = BSMT_CROUCHING; + } + } + // + return ( bs->script.status.stackChangeTime < level.time - duration ); +} + +/* +=================== +Bot_ScriptAction_Cvar + + syntax: cvar +=================== +*/ +qboolean Bot_ScriptAction_Cvar( bot_state_t *bs, char *params ) { + char *pString, *token, lastToken[MAX_QPATH], name[MAX_QPATH], cvarName[MAX_QPATH]; + int cvarValue; + qboolean terminate, found; + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar without a cvar name\n" ); + } + + cvarValue = trap_Cvar_VariableIntegerValue( cvarName ); + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar without a command\n" ); + } + + Q_strncpyz( lastToken, token, sizeof( lastToken ) ); + token = COM_ParseExt( &pString, qfalse ); + + if ( !Q_stricmp( lastToken, "inc" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + trap_Cvar_Set( cvarName, va( "%i", cvarValue + 1 ) ); + } else if ( !Q_stricmp( lastToken, "abort_if_less_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue < atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_greater_than" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue > atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_equal" ) || !Q_stricmp( lastToken, "abort_if_not_equals" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue != atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue == atoi( token ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + cvarValue |= ( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "bitreset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + cvarValue &= ~( 1 << atoi( token ) ); + } else if ( !Q_stricmp( lastToken, "abort_if_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue & ( 1 << atoi( token ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "abort_if_not_bitset" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( !( cvarValue & ( 1 << atoi( token ) ) ) ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } else if ( !Q_stricmp( lastToken, "set" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + cvarValue = atoi( token ); + } else if ( !Q_stricmp( lastToken, "random" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + cvarValue = rand() % atoi( token ); + } else if ( !Q_stricmp( lastToken, "trigger_if_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue == atoi( token ) ) { + gentity_t* trent; + int oldId; + + token = COM_ParseExt( &pString, qfalse ); + Q_strncpyz( lastToken, token, sizeof( lastToken ) ); + if ( !*lastToken ) { + Bot_ScriptError( bs, "trigger must have a name and an identifier\n" ); + } + + token = COM_ParseExt( &pString, qfalse ); + Q_strncpyz( name, token, sizeof( name ) ); + if ( !*name ) { + Bot_ScriptError( bs, "trigger must have a name and an identifier\n" ); + } + + terminate = qfalse; + found = qfalse; + trent = NULL; + while ( ( trent = BotFindEntity( trent, FOFS( scriptName ), lastToken ) ) ) { + found = qtrue; + oldId = trent->scriptStatus.scriptId; + G_Script_ScriptEvent( trent, "trigger", name ); + // if the script changed, return false so we don't muck with it's variables + if ( ( trent->s.number == bs->client ) && ( oldId != trent->scriptStatus.scriptId ) ) { + terminate = qtrue; + } + } + + if ( terminate ) { + return qfalse; + } + if ( found ) { + return qtrue; + } + +// Bot_ScriptError( bs, "trigger has unknown name: %s\n", name ); + G_Printf( "trigger has unknown name: %s\n", name ); + return qfalse; + } + } else if ( !Q_stricmp( lastToken, "wait_while_equal" ) ) { + if ( !token[0] ) { + Bot_ScriptError( bs, "cvar %s requires a parameter\n", lastToken ); + } + if ( cvarValue == atoi( token ) ) { + return qfalse; + } + } else { + Bot_ScriptError( bs, "cvar %s: unknown command\n", params ); + } + + return qtrue; +} + +/* +================= +Bot_ScriptAction_SetMovementAutonomy +================= +*/ +qboolean Bot_ScriptAction_SetMovementAutonomy( bot_state_t *bs, char *params ) { + int mlevel; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetMovementAutonomy requires a parameter" ); + } + // + mlevel = BotMovementAutonomyForString( params ); + if ( mlevel < 0 ) { + Bot_ScriptError( bs, "SetMovementAutonomy: unknown parameter \"%s\"", params ); + } + bs->script.movementAutonomy = mlevel; + // TAT - why are there 2 of these vars? set both of them + bs->movementAutonomy = mlevel; + + // + if ( bs->leader < 0 ) { + VectorCopy( level.clients[bs->client].ps.origin, bs->script.movementAutonomyPos ); + VectorCopy( level.clients[bs->client].ps.origin, bs->movementAutonomyPos ); + } + // + return qtrue; +} + +/* +====================== +Bot_ScriptAction_MovementAutonomy +====================== +*/ +qboolean Bot_ScriptAction_MovementAutonomy( bot_state_t *bs, char *params ) { + char *pString, *token, *operand; + int maLevel; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "MovementAutonomy requires a parameter" ); + } + // + pString = params; + // + // read the operand + token = COM_ParseExt( &pString, qfalse ); + operand = va( "%s", token ); // RF, this is a cheap and nasty way of saving memory + // + if ( !operand[0] ) { + Bot_ScriptError( bs, "MovementAutonomy requires an operand" ); + } + // + // read the level + token = COM_ParseExt( &pString, qfalse ); + // + if ( !token[0] ) { + Bot_ScriptError( bs, "MovementAutonomy requires a level" ); + } + // + maLevel = BotMovementAutonomyForString( token ); + if ( maLevel < 0 ) { + Bot_ScriptError( bs, "SetMovementAutonomy: unknown movementAutonomy \"%s\"", params ); + } + // + // apply the function + if ( !Q_stricmp( operand, "set" ) ) { + bs->script.movementAutonomy = maLevel; + VectorCopy( level.clients[bs->client].ps.origin, bs->script.movementAutonomyPos ); + } + // + if ( !Q_stricmp( operand, "force" ) ) { + bs->script.movementAutonomy = maLevel; + VectorCopy( level.clients[bs->client].ps.origin, bs->script.movementAutonomyPos ); + bs->leader = -1; // stop following others + // + bs->script.flags |= BSFL_FORCED_MOVEMENT_AUTONOMY; // force this level + } + // + if ( !Q_stricmp( operand, "unforce" ) ) { + bs->script.flags &= ~BSFL_FORCED_MOVEMENT_AUTONOMY; // turn it off + } + // + else if ( !Q_stricmp( operand, "abort_if_less_than" ) ) { + if ( bs->movementAutonomy < maLevel ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } + // + else if ( !Q_stricmp( operand, "abort_if_greater_than" ) ) { + if ( bs->movementAutonomy > maLevel ) { + // abort the current script + bs->script.status.stackHead = bs->script.data->events[bs->script.status.eventIndex].stack.numItems; + } + } + // + return qtrue; +} + +// +// START Mad Doctor I changes, 8/12/2002 +// Adding some basic script functions for setting health, notarget, and accuracy +// + +/* +================= +Bot_ScriptAction_NoTarget + + syntax: notarget ON/OFF +================= +*/ +qboolean Bot_ScriptAction_NoTarget +( + bot_state_t *bs, + char *params +) { + // The user needs to specify on or off + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "notarget requires ON or OFF as parameter" ); + + } // if (!params || !params[0]) ... + + if ( !Q_stricmp( params, "ON" ) ) { + g_entities[bs->client].flags |= FL_NOTARGET; + } else if ( !Q_stricmp( params, "OFF" ) ) { + g_entities[bs->client].flags &= ~FL_NOTARGET; + } else + { + Bot_ScriptError( bs, "notarget requires ON or OFF as parameter" ); + } + + return qtrue; + +} // qboolean Bot_ScriptAction_NoTarget( cast_state_t *cs, char *params) ... + +/* +==================== +Bot_ScriptAction_ResetScript +==================== +*/ +qboolean Bot_ScriptAction_ResetScript( bot_state_t *bs, char *params ) { + return qtrue; +} + + +// START Mad Doctor I changes, 8/14/2002 + +/* +================= +Bot_ScriptAction_SetFieldOfView +================= +*/ +qboolean Bot_ScriptAction_SetFieldOfView +( + bot_state_t *bs, + char *params +) { + // Make sure we have a parameter to set the FOV to. + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetFieldOfView requires a FOV value" ); + + } // if (!params || !params[0]) ... + + // Set the FOV + bs->scriptedFieldOfView = atof( params ); + + return qtrue; + +} // qboolean Bot_ScriptAction_SetFieldOfView( cast_state_t *cs, char *params) ... + + + +/* +================= +Bot_ScriptAction_SetHearingRange +================= +*/ +qboolean Bot_ScriptAction_SetHearingRange +( + bot_state_t *bs, + char *params +) { + // Make sure we have a parameter to set the hearing range to. + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetHearingRange requires a range value" ); + + } // if (!params || !params[0]) ... + + // Set the hearing range + bs->scriptedHearingRange = atof( params ); + + return qtrue; + +} // qboolean Bot_ScriptAction_SetHearingRange( cast_state_t *cs, char *params) ... + +// START xkan, 8/20/2002 +/* +================= +Bot_ScriptAction_SetCrouch + + syntax: SetCrouch + + OnOffFlag - 1 crouch; 0 don't crouch. +================= +*/ +qboolean Bot_ScriptAction_SetCrouch +( + bot_state_t *bs, + char *params +) { + char *pString, *token; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetCrouch: syntax: SetCrouch \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetCrouch: syntax: SetCrouch \n" ); + } + if ( !Q_stricmp( token, "on" ) ) { + bs->script.flags |= BSFL_CROUCH; + } else if ( !Q_stricmp( token, "off" ) ) { + bs->script.flags &= ~BSFL_CROUCH; + } else { + G_Error( "Bot_ScriptAction_SetCrouch: syntax: SetCrouch \n" ); + } + + // We're done here! + return qtrue; + +} // Bot_ScriptAction_SetCrouch + + +qboolean Bot_ScriptAction_SetProne +( + bot_state_t *bs, + char *params +) { + char *pString, *token; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetProne: syntax: SetProne \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetProne: syntax: SetProne \n" ); + } + if ( !Q_stricmp( token, "on" ) ) { + bs->script.flags |= BSFL_PRONE; + } else if ( !Q_stricmp( token, "off" ) ) { + bs->script.flags &= ~BSFL_PRONE; + } else { + G_Error( "Bot_ScriptAction_SetProne: syntax: SetProne \n" ); + } + + // We're done here! + return qtrue; +} // Bot_ScriptAction_SetProne + +// Mad Doc - TDF +/* +=================== +G_ScriptAction_PrintAccum + + syntax: printaccum + + prints out the value of accum 'accumNumber' +=================== +*/ +qboolean Bot_ScriptAction_PrintAccum( bot_state_t *bs, char *params ) { + char *token, *pString; + gentity_t *ent; + int bufferIndex; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_PrintAccum: syntax: PrintAccum \n" ); + } + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + G_Error( "Bot_ScriptAction_PrintAccum: syntax: PrintAccum \n" ); + } + + + bufferIndex = atoi( token ); + if ( ( bufferIndex < 0 ) || ( bufferIndex >= MAX_SCRIPT_ACCUM_BUFFERS ) ) { + G_Error( "Bot_ScriptAction_PrintAccum: buffer is outside range (0 - %i)", MAX_SCRIPT_ACCUM_BUFFERS ); + } + + ent = BotGetEntity( bs->client ); + + G_Printf( "(BotScript) %s: Accum[%i] = %d\n", ent->scriptName, bufferIndex, ent->scriptAccumBuffer[bufferIndex] ); + + return qtrue; +} + + +// Mad Doc - TDF +/* +=================== +Bot_ScriptAction_PrintGlobalAccum + + syntax: printGlobalAccum + + prints out the value of global accum 'globalaccumnumber' +=================== +*/ +qboolean Bot_ScriptAction_PrintGlobalAccum( gentity_t *ent, char *params ) { + char *token, *pString; + int bufferIndex; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_PrintGlobalAccum: syntax: PrintGlobalAccum \n" ); + } + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + G_Error( "Bot_ScriptAction_PrintGlobalAccum: syntax: PrintGlobalAccum \n" ); + } + + + bufferIndex = atoi( token ); + if ( ( bufferIndex < 0 ) || ( bufferIndex >= MAX_SCRIPT_ACCUM_BUFFERS ) ) { + G_Error( "PrintGlobalAccum: buffer is outside range (0 - %i)", MAX_SCRIPT_ACCUM_BUFFERS ); + } + + + G_Printf( "(BotScript) GlobalAccum[%i] = %d\n", bufferIndex, level.globalAccumBuffer[bufferIndex] ); + + return qtrue; +} + + + + + + + +// Mad Doc - TDF +/* +=================== +Bot_ScriptAction_BotDebugging + + syntax: BotDebugging ON/OFF + + toggles bot debugging +=================== +*/ +qboolean Bot_ScriptAction_BotDebugging( gentity_t *ent, char *params ) { + char *token, *pString; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_BotDebugging: syntax: BotDebugging \n" ); + } + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + G_Error( "Bot_ScriptAction_BotDebugging: syntax: BotDebugging \n" ); + } + + if ( !Q_stricmp( token, "ON" ) ) { + trap_Cvar_Set( "bot_debug", "1" ); + } else if ( !Q_stricmp( token, "OFF" ) ) { + trap_Cvar_Set( "bot_debug", "0" ); + } else + { + G_Error( "Bot_ScriptAction_BotDebugging: syntax: BotDebugging \n" ); + } + + return qtrue; +} + + +// xkan, sets the fire rate for the bot +/* +======================= +Bot_ScriptAction_SetFireRate + + Sets the fire rate. fire rate 1 is normal (continuous) firing. fire rate 0 means do not fire + at all. fire rate in between makes the bot fire a few bullets, wait a little bit, and then fire + a few more bullets. +======================= +*/ +qboolean Bot_ScriptAction_SetFireRate( bot_state_t *bs, char *params ) { + char *pString, *token; + float fireRate; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetFireRate: syntax: SetFireRate <0-1>\n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetFireRate: syntax: SetFireRate <0-1>\n" ); + } + fireRate = atof( token ); + if ( fireRate < 0.0 || fireRate > 1.0 ) { + G_Error( "Bot_ScriptAction_SetFireRate: syntax: SetFireRate <0-1>\n" ); + } + + bs->fireRate = fireRate; + // We're done here! + return qtrue; +} + +// xkan, sets the fire cycle time for the bot +/* +======================= +Bot_ScriptAction_SetFireCycleTime + + Sets the minimum/maximum fire cycle time. Actual fire cycle time is a random number between + minimum cycle time and maximum cycle time. + + during a fire cycle, the bot will fire for a duration equal to fireRate * cycleTime, + and hold fire for the rest of the cycle (which is equal to (1-fireRate)*cycleTime.) + +======================= +*/ +qboolean Bot_ScriptAction_SetFireCycleTime( bot_state_t *bs, char *params ) { + char *pString, *token; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetFireCycleTime: syntax: SetFireCycleTime \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] || token[0] < '0' || token[0] > '9' ) { + G_Error( "Bot_ScriptAction_SetFireCycleTime: syntax: SetFireCycleTime \n" ); + } + bs->minFireRateCycleTime = atoi( token ); + + token = COM_Parse( &pString ); + if ( !token || !token[0] || token[0] < '0' || token[0] > '9' ) { + G_Error( "Bot_ScriptAction_SetFireCycleTime: syntax: SetFireCycleTime \n" ); + } + bs->maxFireRateCycleTime = atoi( token ); + + // We're done here! + return qtrue; +} + + +/* +======================= +Bot_ScriptAction_SetVisionRange + +Mad Doctor I, 10/23/2002 +Set a maximum vision range for bots to see you +======================= +*/ +qboolean Bot_ScriptAction_SetVisionRange( bot_state_t *bs, char *params ) { + char *pString, *token; + float visionRange = 0; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetVisionRange: syntax: SetVisionRange \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetVisionRange: syntax: SetVisionRange \n" ); + } + visionRange = atof( token ); + + // Set the range for the bot + bs->visionRange = visionRange; + + // We're done here! + return qtrue; +} + + + +/* +======================= +Bot_ScriptAction_SetFarSeeingRange + +Mad Doctor I, 10/23/2002 +Set a maximum vision range for bots to "spot and report" not attack +======================= +*/ +qboolean Bot_ScriptAction_SetFarSeeingRange( bot_state_t *bs, char *params ) { + char *pString, *token; + float farSeeingRange = 0; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetFarSeeingRange: syntax: SetFarSeeingRange \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetFarSeeingRange: syntax: SetFarSeeingRange \n" ); + } + farSeeingRange = atof( token ); + + // Set the range for the bot + bs->farSeeingRange = farSeeingRange; + + // We're done here! + return qtrue; +} + + + +/* +======================= +Bot_ScriptAction_SetCloseHearingRange + +Mad Doctor I, 10/23/2002 +When an enemy is this close, you can sense them outside FOV +======================= +*/ +qboolean Bot_ScriptAction_SetCloseHearingRange( bot_state_t *bs, char *params ) { + char *pString, *token; + float closeHearingRange = 0; + + if ( !params || !params[0] ) { + G_Error( "Bot_ScriptAction_SetCloseHearingRange: syntax: SetCloseHearingRange \n" ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token || !token[0] ) { + G_Error( "Bot_ScriptAction_SetCloseHearingRange: syntax: SetCloseHearingRange \n" ); + } + closeHearingRange = atof( token ); + + // Set the range for the bot + bs->closeHearingRange = closeHearingRange; + + // We're done here! + return qtrue; +} + + +/* +======================= +Bot_ScriptAction_SetSpeedCoefficient + +Mad Doctor I, 11/26/2002. Set an individual bot's speed +======================= +*/ +qboolean Bot_ScriptAction_SetSpeedCoefficient( bot_state_t *bs, char *params ) { + return qtrue; +} + +// TAT 2/4/2003 - just force an update of current bot selection - used when who is selectable has changed +extern void UpdateSelectedBots( gentity_t *ent ); + +// TAT 11/16/2002 - Set the selected weapon of the bot - does NOT change which weapons the bot is holding +qboolean Bot_ScriptAction_SetActiveWeapon( bot_state_t *bs, char *params ) { + // Which class are we? + int playerClass = g_entities[bs->client].client->sess.playerType; + + int weapon; + // + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetActiveWeapon requires a weapon name" ); + } + + weapon = Bot_GetWeaponForClassAndTeam( playerClass, g_entities[bs->client].client->sess.sessionTeam, params ); + if ( weapon == -1 ) { + Bot_ScriptError( bs, "Bot %s on team %s cannot use weapon %s\n", g_entities[bs->client].aiName, ( g_entities[bs->client].client->sess.sessionTeam == TEAM_AXIS ) ? "Axis" : "Allies", params ); + } + + // if the bot doesn't have the weapon in question, error + if ( !COM_BitCheck( bs->cur_ps.weapons, weapon ) ) { + Bot_ScriptError( bs, "Bot %s on team %s doesn't have weapon %s\n", g_entities[bs->client].aiName, ( g_entities[bs->client].client->sess.sessionTeam == TEAM_AXIS ) ? "Axis" : "Allies", params ); + } + + // otherwise, set the commanded weapon + bs->commandedWeapon = weapon; + bs->weaponnum = weapon; + // and switch to it + trap_EA_SelectWeapon( bs->client, weapon ); + + // done + return qtrue; +} + + + +/* +=================== +Bot_ScriptAction_Announce + + Gordon: same as the equivilant G_ScriptAction_Announce + syntax: wm_announce <"text to send to all clients"> +=================== +*/ +qboolean Bot_ScriptAction_Announce( bot_state_t *bs, char *params ) { + char *pString, *token; + + if ( level.intermissiontime ) { + return qtrue; + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token[0] ) { + G_Error( "Bot_ScriptAction_Announce: statement parameter required\n" ); + } + + trap_SendServerCommand( -1, va( "cp \"%s\" 2", token ) ); + + return qtrue; +} + +/* +================= +Bot_ScriptAction_FireAtTarget + + syntax: fireattarget [duration] +================= +*/ +qboolean Bot_ScriptAction_FireAtTarget( bot_state_t *bs, char *params ) { + gentity_t *ent; + vec3_t vec, org, src; + char *pString, *token; + float diff; + int i; + + pString = params; + + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + Bot_ScriptError( bs, "AI Scripting: fireattarget without a targetname\n" ); + } + + // find this targetname + ent = BotFindEntityForName( token ); + if ( !ent ) { + ent = G_FindByTargetname( NULL, token ); + if ( !ent ) { + Bot_ScriptError( bs, "AI Scripting: fireattarget cannot find targetname/aiName \"%s\"\n", token ); + } + } + + // if this is our first call for this fireattarget, record the ammo count + //if (bs->script.flags & BSFL_FIRST_CALL) + //{ + // bs->last_fire = 0; + //} + + // make sure we don't move or shoot while turning to our target + //if (bs->script.status.scriptNoAttackTime < level.time) { + // bs->script.status.scriptNoAttackTime = level.time + 500; + //} + // dont reload prematurely + //bs->noReloadTime = level.time + 1000; + // don't move while firing at all + //bs->castScriptStatus.scriptNoMoveTime = level.time + 500; + + // stand still + //bs->script.frameFlags |= BSFFL_STAND; + + // let us move our view, whether it looks bad or not + //bs->castScriptStatus.playAnimViewlockTime = 0; + + // set the view angle manually + BG_EvaluateTrajectory( &ent->s.pos, level.time, org, qfalse, -1 ); + VectorCopy( bs->origin, src ); + src[2] += bs->cur_ps.viewheight; + + VectorSubtract( org, src, vec ); + VectorNormalize( vec ); + vectoangles( vec, bs->ideal_viewangles ); + + if ( bs->weaponnum == WP_MORTAR_SET ) { +/* vec_t x, y, u, d, b, g, a; + vec3_t diff; + VectorSubtract( org, src, diff ); + diff[2] = 0; + + g = g_gravity.value; + x = VectorLength( diff ); + y = org[2] - src[2]; + u = MORTAR_SP_BOTSPEED; + + d = (g * SQR(x)) / (2 * SQR(u)); + + b = (SQR(x) - (4 * d * (y + d))); + + if(b < 0) { + return qfalse; + } + + a = (vec_t)atan((-x - b) / (-2 * d)); + bs->ideal_viewangles[PITCH] = (AngleMod(RAD2DEG(a)- 180) + 60);*/ + + float g = -g_gravity.value; + + float uz = sqrt( -2 * 3072 * g ); + float t = ( ( -uz ) / g ) * 2; + float ux = ( org[0] - src[0] ) / t; + float uy = ( org[1] - src[1] ) / t; + + VectorSet( g_entities[bs->entitynum].gDelta, ux, uy, uz ); + } else //if (bs->weaponnum != WP_MORTAR_SET) + { + for ( i = 0; i < 2; i++ ) { + diff = abs( AngleDifference( bs->cur_ps.viewangles[i], bs->ideal_viewangles[i] ) ); + if ( VectorCompare( vec3_origin, ent->s.pos.trDelta ) ) { + if ( diff ) { + return qfalse; // not facing yet + } + } else { + if ( diff > 25 ) { // allow some slack when target is moving + return qfalse; + } + } + } + } + + // force fire + trap_EA_Attack( bs->client ); + // + bs->flags |= BFL_ATTACKED; + + // if we haven't fired yet + //if (!bs->last_fire) { + // return qfalse; + //} + + // do we need to stay and fire for a duration? + token = COM_ParseExt( &pString, qfalse ); + if ( !token[0] ) { + return qtrue; // no need to wait around + + } + if ( !Q_stricmp( token, "forever" ) ) { + return qfalse; + } + + // only return true if we've been firing for long enough + // TAT 1/29/2003 - the token in question was parsed right above this + // so can't just move this up to the top + // plus, if we don't pass in a duration, that means fire once, which that check above for no token will handle, + // but we WILL fire once at least + return ( ( bs->script.status.stackChangeTime + atoi( token ) ) < level.time ); +} + +/* +======================= +Bot_ScriptAction_SetScriptAutonomy +======================= +*/ +qboolean Bot_ScriptAction_SetScriptAutonomy( bot_state_t *bs, char *params ) { + scriptAutonomy_t level; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "SetScriptAutonomy requires a parameter" ); + } + // + level = BotScriptAutonomyForString( params ); + if ( level < 0 ) { + Bot_ScriptError( bs, "SetScriptAutonomy: unknown parameter \"%s\"", params ); + } + + bs->scriptAutonomy = level; + + // + return qtrue; +} + +// TAT 12/14/2002 - Set how much ammo we have for a particular weapon +// Doesn't change the current weapon loadout (which weapons we carry) +qboolean Bot_ScriptAction_SetAmmoAmount( bot_state_t *bs, char *params ) { + char *pString, *token; + int weapon, amount; + qboolean clipOnly = qfalse; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "Bot_ScriptAction_SetAmmoAmmount: syntax: SetAmmoAmount " ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( !token[0] ) { + Bot_ScriptError( bs, "Bot_ScriptAction_SetAmmoAmmount: syntax: SetAmmoAmount " ); + } + + weapon = Bot_GetWeaponForClassAndTeam( g_entities[bs->client].client->sess.playerType, g_entities[bs->client].client->sess.sessionTeam, token ); + if ( weapon == -1 ) { + // can't use this weapon + Bot_ScriptError( bs, "Bot %s on team %s cannot use weapon %s\n", g_entities[bs->client].aiName, ( g_entities[bs->client].client->sess.sessionTeam == TEAM_AXIS ) ? "Axis" : "Allies", token ); + } + + // Do we have this weapon? + if ( !COM_BitCheck( bs->cur_ps.weapons, weapon ) ) { + Bot_ScriptError( bs, "Bot_ScriptAction_SetAmmoAmount: Bot %s does not have weapon %s", g_entities[bs->client].aiName, token ); + } + + // what are we setting the ammo amount to? + token = COM_Parse( &pString ); + if ( !token[0] ) { + Bot_ScriptError( bs, "Bot_ScriptAction_SetAmmoAmmount: syntax: SetAmmoAmount " ); + } + + amount = atoi( token ); + + // Some specials use the clip only + switch ( weapon ) + { + case WP_AMMO: + case WP_MEDKIT: + case WP_LANDMINE: + case WP_MEDIC_SYRINGE: + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + case WP_FLAMETHROWER: + case WP_MORTAR: + case WP_MORTAR_SET: + case WP_DYNAMITE: + clipOnly = qtrue; + break; + } + + if ( clipOnly ) { + g_entities[bs->client].client->ps.ammoclip[BG_FindAmmoForWeapon( weapon )] = amount; + } else + { + g_entities[bs->client].client->ps.ammo[BG_FindAmmoForWeapon( weapon )] = amount; + } + + // done + return qtrue; +} + +qboolean Bot_ScriptAction_SetCivilian( bot_state_t *bs, char *params ) { + char *pString, *token; + + if ( !params || !params[0] ) { + Bot_ScriptError( bs, "Bot_ScriptAction_SetCivilian: syntax: SetCivilian " ); + } + + pString = params; + token = COM_Parse( &pString ); + if ( token[0] && !Q_stricmp( token, "yes" ) ) { + g_entities[bs->client].client->isCivilian = qtrue; + } else if ( token[0] && !Q_stricmp( token, "no" ) ) { + g_entities[bs->client].client->isCivilian = qfalse; + } else { + Bot_ScriptError( bs, "Bot_ScriptAction_SetCivilian: syntax: SetCivilian " ); + } + + // done + return qtrue; +} diff --git a/src/botai/ai_team.c b/src/botai/ai_team.c new file mode 100644 index 0000000..4b83b49 --- /dev/null +++ b/src/botai/ai_team.c @@ -0,0 +1,3183 @@ +/* +=========================================================================== + +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: ai_team.c + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +#include "../game/g_local.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../game/be_ea.h" +#include "../game/be_ai_char.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_gen.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../botai/botai.h" +#include "../botai/inv.h" +// +#include "ai_main.h" +#include "ai_dmq3.h" +#include "ai_cmd.h" +#include "ai_team.h" +// +#include "ai_dmnet_mp.h" + +// Key AI constants +#include "ai_distances.h" + +// Array contains all the info about the team VO and AI coordination +AI_Team_t g_aiTeam[TEAM_NUM_TEAMS]; + + +/* +================== +BotValidTeamLeader +================== +*/ +int BotValidTeamLeader( bot_state_t *bs ) { + if ( !strlen( bs->teamleader ) ) { + return qfalse; + } + if ( ClientFromName( bs->teamleader ) == -1 ) { + return qfalse; + } + return qtrue; +} + +/* +================== +BotNumTeamMates +================== +*/ +int BotNumTeamMates( bot_state_t *bs, int *list, int maxList ) { + int i, j, numplayers; + + numplayers = 0; + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( bs->client == j ) { + continue; + } + + if ( !g_entities[j].inuse ) { + continue; + } + + if ( !BotSameTeam( bs, j ) ) { + continue; + } + + if ( list ) { + if ( numplayers < maxList ) { + list[numplayers++] = j; + } + } else { + // calling without a list gives the full count no matter what maxList is set to + numplayers++; + } + } + return numplayers; +} + +/* +================== +BotNumTeamMatesWithTarget +================== +*/ +int BotNumTeamMatesWithTarget( bot_state_t *bs, int targetEntity, int *list, int maxList ) { + int i, j, numplayers; + + numplayers = 0; + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + + if ( bs->client == j ) { + continue; + } + + if ( !g_entities[j].inuse ) { + continue; + } + + if ( !BotSameTeam( bs, j ) ) { + continue; + } + + if ( g_entities[j].health <= 0 ) { + continue; + } + + if ( !botstates[j].inuse ) { + continue; + } + + if ( botstates[j].target_goal.entitynum != targetEntity ) { + continue; + } + + if ( list ) { + if ( numplayers < maxList ) { + list[numplayers++] = j; + } + } else { + // calling without a list gives the full count no matter what maxList is set to + numplayers++; + } + } + return numplayers; +} + +/* +================== +BotNumTeamMatesWithTargetByClass +================== +*/ +int BotNumTeamMatesWithTargetByClass( bot_state_t *bs, int targetEntity, int *list, int maxList, int playerType ) { + int i, j, numplayers; + + numplayers = 0; + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( bs->client == j ) { + continue; + } + + if ( !g_entities[j].inuse ) { + continue; + } + + if ( !BotSameTeam( bs, j ) ) { + continue; + } + + if ( g_entities[j].health <= 0 ) { + continue; + } + + if ( g_entities[j].client->sess.playerType != playerType ) { + continue; + } + + if ( !botstates[j].inuse ) { + continue; + } + + if ( botstates[j].target_goal.entitynum != targetEntity ) { + continue; + } + + if ( list ) { + if ( numplayers < maxList ) { + list[numplayers++] = j; + } + } else { + // calling without a list gives the full count no matter what maxList is set to + numplayers++; + } + } + return numplayers; +} + +/* +================== +BotNumTeamMatesWithTargetAndCloser +================== +*/ +int BotNumTeamMatesWithTargetAndCloser( bot_state_t *bs, int targetEntity, int targetArea, int *list, int maxList, int playerType ) { + int i, j, numplayers; + int ourTime, t; + + numplayers = 0; + + ourTime = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, targetArea, bs->tfl ); + + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + if ( bs->client == j ) { + continue; + } + + if ( !g_entities[j].inuse ) { + continue; + } + + if ( !botstates[j].inuse ) { + continue; + } + + if ( !BotSameTeam( bs, j ) ) { + continue; + } + + if ( g_entities[j].health <= 0 ) { + continue; + } + + if ( playerType >= 0 && g_entities[j].client->sess.playerType != playerType ) { + continue; + } + + if ( botstates[j].target_goal.entitynum != targetEntity ) { + continue; + } + + t = botstates[j].inventory[GOAL_TRAVELTIME]; + // trap_AAS_AreaTravelTimeToGoalArea( botstates[j].target_goal.areanum, botstates[j].origin, targetArea, bs->tfl ); + if ( !t ) { + continue; + } + if ( t > ourTime ) { + continue; + } + + if ( list ) { + if ( numplayers < maxList ) { + list[numplayers++] = j; + } + } else { + // calling without a list gives the full count no matter what maxList is set to + numplayers++; + } + } + return numplayers; +} + +/* +================== +BotSortPlayersByDistance + + returns the distances list +================== +*/ +float* BotSortPlayersByDistance( vec3_t target, int *list, int numList ) { + static float outDistances[MAX_CLIENTS]; + + float distances[MAX_CLIENTS], bestDist; + int i, j, outList[MAX_CLIENTS], best = 0; + + for ( i = 0; i < numList; i++ ) { + distances[i] = VectorDistanceSquared( g_entities[list[i]].r.currentOrigin, target ); + } + + for ( j = 0; j < numList; j++ ) { + // find the closest player + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( bestDist < 0 || distances[i] <= bestDist ) { + best = i; + bestDist = distances[i]; + } + } + + outDistances[j] = distances[best]; + distances[best] = -1; + outList[j] = list[best]; + } + + return outDistances; +} + +/* +================== +BotSortPlayersByTraveltime + + returns the distances list +================== +*/ +float *BotSortPlayersByTraveltime( int areanum, int *list, int numList ) { + static float outDistances[MAX_CLIENTS]; + + float distances[MAX_CLIENTS], bestDist; + int i, j, outList[MAX_CLIENTS], best = 0; + bot_state_t *lbs; + + for ( i = 0; i < numList; i++ ) { + lbs = &botstates[list[i]]; + if ( lbs && lbs->inuse ) { + distances[i] = (float)trap_AAS_AreaTravelTimeToGoalArea( BotGetArea( list[i] ), BotGetOrigin( list[i] ), areanum, lbs->tfl ); + } else { + distances[i] = 0; + } + } + + for ( j = 0; j < numList; j++ ) { + // find the closest player + bestDist = -1; + for ( i = 0; i < numList; i++ ) { + if ( distances[i] < 0 ) { + continue; + } + if ( bestDist < 0 || distances[i] <= bestDist ) { + best = i; + bestDist = distances[i]; + } + } + + outDistances[j] = distances[best]; + distances[best] = -1; + outList[j] = list[best]; + } + + return outDistances; +} + +/* +================== +BotGetEnemyFlagCarrier +================== +*/ +gentity_t *BotGetEnemyFlagCarrier( bot_state_t *bs ) { + int i; + char buf[MAX_INFO_STRING]; + static int maxclients; + + if ( !maxclients ) { + maxclients = level.maxclients; + } + + for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { + if ( !g_entities[i].inuse ) { + continue; + } + if ( g_entities[i].health <= 0 ) { + continue; + } + trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); + //if no config string or no name + if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { + continue; + } + //skip spectators + if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { + continue; + } + // + if ( !BotSameTeam( bs, i ) ) { + if ( g_entities[i].client->ps.powerups[PW_BLUEFLAG] || g_entities[i].client->ps.powerups[PW_REDFLAG] ) { + return BotGetEntity( i ); + } + } + } + return NULL; +} + +/* +================== +BotGetTeamFlagCarrier +================== +*/ +int BotGetTeamFlagCarrier( bot_state_t *bs ) { + int i; + char buf[MAX_INFO_STRING]; + static int maxclients; + + if ( !maxclients ) { + maxclients = trap_Cvar_VariableIntegerValue( "sv_maxclients" ); + } + + for ( i = 0; i < maxclients && i < MAX_CLIENTS; i++ ) { + if ( !g_entities[i].inuse ) { + continue; + } + if ( g_entities[i].health <= 0 ) { + continue; + } + trap_GetConfigstring( CS_PLAYERS + i, buf, sizeof( buf ) ); + //if no config string or no name + if ( !strlen( buf ) || !strlen( Info_ValueForKey( buf, "n" ) ) ) { + continue; + } + //skip spectators + if ( atoi( Info_ValueForKey( buf, "t" ) ) == TEAM_SPECTATOR ) { + continue; + } + // + if ( BotSameTeam( bs, i ) ) { + if ( g_entities[i].client->ps.powerups[PW_BLUEFLAG] || g_entities[i].client->ps.powerups[PW_REDFLAG] ) { + return g_entities[i].s.number; + } + } + } + return -1; +} + +/* +================== +BotFlagFlagAtBase + + returns -1 if there is no such flag, qfalse or qtrue otherwise. +================== +*/ +int BotFlagAtBase( int team, gentity_t **returnEnt ) { + gentity_t *ent; + botStaticEntityEnum_t flags[2] = {BOTSTATICENTITY_CTF_REDFLAG, BOTSTATICENTITY_CTF_BLUEFLAG}; + // + if ( team > TEAM_SPECTATOR ) { + return qfalse; + } + // + ent = NULL; + if ( returnEnt ) { + *returnEnt = NULL; + } + while ( ( ent = BotFindNextStaticEntity( ent, flags[team - 1] ) ) ) { + if ( ent->flags & FL_DROPPED_ITEM ) { + continue; + } + if ( returnEnt ) { + *returnEnt = ent; + } + // this is the enemy flag, is it at base? + if ( ent->r.svFlags & SVF_NOCLIENT ) { + // not at base + return qfalse; + } else { + // at base + return qtrue; + } + } + // + return -1; +} + +/* +================== +BotClientTravelTimeToGoal +================== +*/ +int BotClientTravelTimeToGoal( int client, bot_goal_t *goal ) { + playerState_t ps; + int areanum; + + BotAI_GetClientState( client, &ps ); + areanum = BotPointAreaNum( client, ps.origin ); + if ( !areanum ) { + return 1; + } + if ( !BotTravelFlagsForClient( client ) ) { + return 1; + } + return trap_AAS_AreaTravelTimeToGoalArea( areanum, ps.origin, goal->areanum, BotTravelFlagsForClient( client ) ); +} + +/* +================== +BotSortTeamMatesByBaseTravelTime +================== +*/ +int BotSortTeamMatesByBaseTravelTime( bot_state_t *bs, int *teammates, int maxteammates ) { + return 0; +} + +/* +================== +BotSayTeamOrders +================== +*/ +void BotSayTeamOrder( bot_state_t *bs, int toclient ) { + char teamchat[MAX_MESSAGE_SIZE]; + char buf[MAX_MESSAGE_SIZE]; + char name[MAX_NETNAME]; + + //if the bot is talking to itself + if ( bs->client == toclient ) { + //don't show the message just put it in the console message queue + trap_BotGetChatMessage( bs->cs, buf, sizeof( buf ) ); + ClientName( bs->client, name, sizeof( name ) ); + Com_sprintf( teamchat, sizeof( teamchat ), "(%s): %s", name, buf ); + trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, teamchat ); + } else { + trap_BotEnterChat( bs->cs, bs->client, CHAT_TEAM ); + } +} + +/* +================== +BotFindDroppedFlag +================== +*/ +qboolean BotFindDroppedFlag( gentity_t **returnEnt ) { + gentity_t *ent; + char *flagStr[2] = {"team_CTF_redflag", "team_CTF_blueflag"}; + int i, j; + // + ent = BotGetEntity( level.maxclients ); + for ( j = 0; j < level.num_entities - level.maxclients; j++, ent++ ) { + if ( !ent->inuse ) { + continue; + } + if ( !( ent->flags & FL_DROPPED_ITEM ) ) { + continue; + } + if ( ent->classname[0] != 't' || ent->classname[1] != 'e' ) { + continue; + } + for ( i = 0; i < 2; i++ ) { + if ( Q_stricmp( ent->classname, flagStr[i] ) ) { + continue; + } + // this is a dropped flag + if ( returnEnt ) { + *returnEnt = ent; + } + return qtrue; + } + } + // + return qfalse; +} + +/* +================== +BotFindSparseDefendArea +================== +*/ +int BotFindSparseDefendArea( bot_state_t *bs, bot_goal_t *goal, qboolean force ) { + vec3_t dSpot, targetPos; + int numTeam, i, j, t, t2, teammates[64]; + int numareas, numTPos, area, bestArea = 0, bestTime = 0; + vec3_t tPos[64], waypoints[64]; + bot_state_t *tbs; + float dist, bestDist, closest, d, maxrange; + int avoidEnts[] = {BOTSTATICENTITY_FUNC_DOOR, BOTSTATICENTITY_FUNC_DOOR_ROTATING, -1}; + vec3_t avoidPos[128]; + int numAvoidPos; + gentity_t *trav, *flagEnt; + qboolean hasflag, getFurthestFromFlag; + bot_goal_t flagGoal, flag; + int flagDestTime = 0; + bot_state_t *obs; + vec3_t brushPos, center, vec; + int list[20], numList; + int oldestTime = -1, oldest = 0; + // + if ( !force && bs->last_SparseDefense > level.time - 400 - rand() % 200 ) { + return -1; + } + bs->last_SparseDefense = level.time; + // + // find our team mates that are defending the same goal + numTeam = BotNumTeamMates( bs, teammates, 64 ); + numTPos = 0; + for ( i = 0; i < numTeam; i++ ) { + if ( !g_entities[teammates[i]].inuse || !( g_entities[teammates[i]].r.svFlags & SVF_BOT ) || g_entities[teammates[i]].health <= 0 ) { + continue; + } + if ( teammates[i] == bs->client ) { + continue; + } + tbs = &botstates[teammates[i]]; + if ( tbs->target_goal.entitynum == goal->entitynum ) { + // same goal + VectorCopy( tbs->target_goal.origin, tPos[numTPos] ); + numTPos++; + } + } + // get the location of the goal + if ( goal->entitynum >= 0 ) { + VectorAdd( g_entities[goal->entitynum].r.absmin, g_entities[goal->entitynum].r.absmax, targetPos ); + VectorScale( targetPos, 0.5, targetPos ); + //if (!trap_AAS_PointAreaNum( targetPos )) { + // VectorCopy( goal->origin, targetPos ); + //} + } else { + VectorCopy( goal->origin, targetPos ); + } + // + hasflag = qfalse; + getFurthestFromFlag = qfalse; + if ( !BotSinglePlayer() && !BotCoop() && goal->entitynum >= 0 && goal->entitynum < MAX_CLIENTS && BotCarryingFlag( goal->entitynum ) ) { + if ( !( g_entities[goal->entitynum].r.svFlags & SVF_BOT ) ) { + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY ); + if ( !trav ) { + trav = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_FLAGONLY_MULTIPLE ); + } + if ( trav ) { + BotGoalForEntity( NULL, trav->s.number, &flagGoal, 0 ); + // find the flag itself, if we are closer the that, then pick a spot further away from the flag + flagEnt = NULL; + if ( bs->sess.sessionTeam == TEAM_AXIS ) { + flagEnt = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_CTF_BLUEFLAG ); + } else { + flagEnt = BotFindNextStaticEntity( NULL, BOTSTATICENTITY_CTF_REDFLAG ); + } + if ( flagEnt ) { + BotGoalForEntity( NULL, flagEnt->s.number, &flag, 0 ); + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, flag.areanum, bs->tfl ); + if ( t ) { + t2 = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, flagGoal.areanum, bs->tfl ); + if ( t < t2 ) { + // we should find a spot further away from the flag itself, rather than closer to the destination. + // this takes into account the player taking an alternate route, so that the bots lead the player + // still, rather than trailing behind while they think they are leading the player. + getFurthestFromFlag = qtrue; + BotGoalForEntity( NULL, flagEnt->s.number, &flagGoal, 0 ); + hasflag = qtrue; + flagDestTime = t; + } + } + } else { + VectorAdd( trav->r.absmin, trav->r.absmax, brushPos ); + VectorScale( brushPos, 0.5, brushPos ); + // find the best goal area + numList = trap_AAS_BBoxAreas( trav->r.absmin, trav->r.absmax, list, 20 ); + bestDist = -1; + if ( numList ) { + oldestTime = -1; + for ( i = 0; i < numList; i++ ) { + if ( !trap_AAS_AreaReachability( list[i] ) ) { + continue; + } + t = trap_AAS_AreaTravelTimeToGoalArea( goal->areanum, goal->origin, list[i], bs->tfl ); + if ( t > 0 ) { + // choose the reachable area closest to the center of the info_objective brush + trap_AAS_AreaCenter( list[i], center ); + VectorSubtract( brushPos, center, vec ); + vec[2] = 0; + dist = VectorLength( vec ); + if ( bestDist < 0 || dist < bestDist ) { + oldestTime = t; + oldest = list[i]; + bestDist = dist; + } + } + } + } + if ( bestDist > -1 ) { + hasflag = qtrue; + flagDestTime = oldestTime; + flagGoal.areanum = oldest; + } + } + } + } else { // use the bot's current goal + obs = &botstates[goal->entitynum]; + flagGoal = obs->target_goal; + // do they have an alt_goal + if ( obs->alt_goal.number && obs->alt_goal.entitynum == obs->target_goal.entitynum && obs->alt_goal.number == obs->target_goal.number ) { + flagGoal = obs->target_goal; // use the ultimate destination, not the altgoal + t = trap_AAS_AreaTravelTimeToGoalArea( goal->areanum, goal->origin, flagGoal.areanum, bs->tfl ); + if ( t ) { + hasflag = qtrue; + flagDestTime = t; + } + } + } + } + // + if ( !BotSinglePlayer() && !BotCoop() ) { + maxrange = 700; + if ( !hasflag && numTPos < 4 ) { + maxrange = 300.0 + ( ( maxrange - 300.0 ) * numTPos / 4.0 ); + } else if ( hasflag ) { + maxrange = 800; + } + } else { + // use autonomy range + maxrange = BotGetMovementAutonomyRange( bs, goal ); + if ( maxrange > 700 ) { + maxrange = 700; + } +/* // if we are following a leader, we need to work out where to position ourselves from them + if (goal->entitynum >= 0 && goal->entitynum < MAX_CLIENTS) { + useoffset = qtrue; + // + trav = BotGetEntity( goal->entitynum ); + // are they moving? + if (VectorLengthSquared( trav->client->ps.velocity ) > SQR(10)) { + VectorNormalize2( trav->client->ps.velocity, offsetVec ); + // low aggression, stay behind + + } + } +*/ + } + // + numareas = trap_AAS_ListAreasInRange( targetPos, goal->areanum, maxrange, bs->tfl & ~( TFL_BARRIERJUMP | TFL_LADDER | TFL_JUMP ), (float **)waypoints, 64 ); + // + if ( !numareas ) { + return 0; + } + // + // build a list of avoidEnts + numAvoidPos = 0; + for ( i = 0; avoidEnts[i] >= 0; i++ ) { + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, avoidEnts[i] ) ) ) { + VectorAdd( trav->r.absmin, trav->r.absmax, dSpot ); + VectorScale( dSpot, 0.5, dSpot ); + VectorCopy( dSpot, avoidPos[numAvoidPos] ); + numAvoidPos++; + if ( numAvoidPos == 128 ) { + break; + } + } + if ( numAvoidPos == 128 ) { + break; + } + } + // + // find the area with the greatest distance from all other teammates, and the goal + bestDist = -999999; + closest = 99999; + for ( i = 0; i < numareas; i++ ) { + // if this point is outside movement autonomy, ignore it + if ( !BotPointWithinMovementAutonomy( bs, goal, waypoints[i] ) ) { + continue; + } + // + dist = 0.5 * VectorDistance( targetPos, waypoints[i] ); + for ( j = 0; j < numTPos; j++ ) { + d = VectorDistance( waypoints[i], tPos[j] ); + if ( !j || d < closest ) { + closest = d; + } + } + // add avoidEnts + for ( j = 0; j < numAvoidPos; j++ ) { + d = VectorDistance( avoidPos[j], bs->origin ); + if ( d < closest ) { + closest = d; + } + } + dist += closest * 6; // weight this higher than the goal distance, since we really want to space each other out + // + // if they have the flag, select areas that are closer to their goal + if ( hasflag ) { + // get closer or further from goal depending on leader's status + area = trap_AAS_PointAreaNum( waypoints[i] ); + t = trap_AAS_AreaTravelTimeToGoalArea( area, waypoints[i], flagGoal.areanum, bs->tfl ); + // + if ( !t ) { + continue; + } + // + if ( BotCarryingFlag( goal->entitynum ) && !getFurthestFromFlag ) { + dist += 14 * ( flagDestTime - t ); // closer + } else { + dist -= 12 * ( flagDestTime - t ); // further + } + } + // + if ( dist > bestDist ) { + area = trap_AAS_PointAreaNum( waypoints[i] ); + if ( area ) { + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); + if ( t ) { + bestDist = dist; + VectorCopy( waypoints[i], dSpot ); + bestArea = area; + bestTime = t; + } + } + } + } + // + if ( bestArea ) { + // we have our spot, defend this area now + goal->areanum = bestArea; + VectorCopy( dSpot, goal->origin ); + goal->flags |= GFL_DEFEND_CLOSE; + } + // + return bestTime; +} + +#define GETTARGETEXPLOSIVE_CACHE_SIZE 32 + +typedef struct botExplosiveCache_s { + int list[GETTARGETEXPLOSIVE_CACHE_SIZE]; + int count; + int listSize; + qboolean ignore; + int time; +} botExplosiveCache_t; + +botExplosiveCache_t g_botExplosiveCache[2]; +botExplosiveCache_t g_botSatchelCache[2]; + +gentity_t* G_FindMissile( gentity_t* start, weapon_t weap ) { + int i = start ? ( start - g_entities ) + 1 : 0; + gentity_t* ent = &g_entities[i]; + + for ( ; i < level.num_entities; i++, ent++ ) { + if ( ent->s.eType != ET_MISSILE ) { + continue; + } + + if ( ent->s.weapon != weap ) { + continue; + } + + return ent; + } + + return NULL; +} + +gentity_t* G_FindDynamite( gentity_t* start ) { + return G_FindMissile( start, WP_DYNAMITE ); +} + +gentity_t* G_FindSmokeBomb( gentity_t* start ) { + return G_FindMissile( start, WP_SMOKE_BOMB ); +} + +gentity_t* G_FindLandmine( gentity_t* start ) { + return G_FindMissile( start, WP_LANDMINE ); +} + +gentity_t* G_FindSatchels( gentity_t* start ) { + return G_FindMissile( start, WP_SATCHEL ); +} + +// Gordon: adding some support functions +// returns qtrue if a construction is under way on this ent, even before it hits any stages +qboolean G_ConstructionBegun( gentity_t* ent ) { + if ( G_ConstructionIsPartlyBuilt( ent ) ) { + return qtrue; + } + + if ( ent->s.angles2[0] ) { + return qtrue; + } + + return qfalse; +} + +// returns qtrue if all stage are built +qboolean G_ConstructionIsFullyBuilt( gentity_t* ent ) { + if ( ent->s.angles2[1] != 1 ) { + return qfalse; + } + return qtrue; +} + +// returns qtrue if 1 stage or more is built +qboolean G_ConstructionIsPartlyBuilt( gentity_t* ent ) { + if ( G_ConstructionIsFullyBuilt( ent ) ) { + return qtrue; + } + + if ( ent->count2 ) { + if ( !ent->grenadeFired ) { + return qfalse; + } else { + return qtrue; + } + } + + return qfalse; +} + +qboolean G_ConstructionIsDestroyable( gentity_t* ent ) { + if ( !G_ConstructionIsPartlyBuilt( ent ) ) { + return qfalse; + } + + if ( ent->s.angles2[0] ) { + return qfalse; + } + + return qtrue; +} + +// returns the constructible for this team that is attached to this toi +gentity_t* G_ConstructionForTeam( gentity_t* toi, team_t team ) { + gentity_t* targ = toi->target_ent; + if ( !targ || targ->s.eType != ET_CONSTRUCTIBLE ) { + return NULL; + } + + if ( targ->spawnflags & 4 ) { + if ( team == TEAM_ALLIES ) { + return targ->chain; + } + } else if ( targ->spawnflags & 8 ) { + if ( team == TEAM_AXIS ) { + return targ->chain; + } + } + + return targ; +} + +gentity_t* G_IsConstructible( team_t team, gentity_t* toi ) { + gentity_t* ent; + + if ( !toi || toi->s.eType != ET_OID_TRIGGER ) { + return NULL; + } + + if ( !( ent = G_ConstructionForTeam( toi, team ) ) ) { + return NULL; + } + + if ( G_ConstructionIsFullyBuilt( ent ) ) { + return NULL; + } + + if ( ent->chain && G_ConstructionBegun( ent->chain ) ) { + return NULL; + } + + return ent; +} + +gentity_t* G_FindDynamiteTargetForTeam( gentity_t* trav, team_t team ) { + gentity_t* targ; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + if ( !trav->r.linked ) { + continue; + } + + if ( ( targ = trav->target_ent ) ) { + if ( targ->s.eType == ET_EXPLOSIVE ) { + if ( !( targ->spawnflags & 64 ) ) { // DY-NO-MITE + continue; + } + + if ( !targ->parent ) { + continue; + } + + if ( targ->aiInactive & ( 1 << team ) ) { + continue; + } + + // Gordon: dont wanna dynamite our own things + if ( ( targ->parent->spawnflags & AXIS_OBJECTIVE ) && ( team == TEAM_AXIS ) ) { + continue; + } else if ( ( targ->parent->spawnflags & ALLIED_OBJECTIVE ) && ( team == TEAM_ALLIES ) ) { + continue; + } + + return targ; + } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { + targ = G_ConstructionForTeam( trav, team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS ); + // no constructible for the other team attached to this + if ( !targ ) { + continue; + } + + // dynamite only + if ( !( targ->spawnflags & 32 ) ) { + continue; + } + + // if it isn't built yet, there's nothing to blow up + if ( !G_ConstructionIsDestroyable( targ ) ) { + continue; + } + + // not active from the script + if ( targ->aiInactive & ( 1 << team ) ) { + continue; + } + + return targ; + } + } + } + + return NULL; +} + +gentity_t* G_FindSatchelChargeTargetForTeam( gentity_t* trav, team_t team ) { + gentity_t* targ; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + if ( !trav->r.linked ) { + continue; + } + + if ( ( targ = trav->target_ent ) ) { + if ( targ->s.eType == ET_EXPLOSIVE ) { + continue; + } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { + targ = G_ConstructionForTeam( trav, team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS ); + // no constructible for the other team attached to this + if ( !targ ) { + continue; + } + + // can satchel charge it + if ( !( targ->spawnflags & 256 ) ) { + continue; + } + + // if it isn't built yet, there's nothing to blow up + if ( !G_ConstructionIsDestroyable( targ ) ) { + continue; + } + + // not active from the script + if ( targ->aiInactive & ( 1 << team ) ) { + continue; + } + + return targ; + } + } + } + + return NULL; +} + + + +/* +================== +BotGetTargetsForSatchelCharge +================== +*/ +int BotGetTargetsForSatchelCharge( team_t team, int *list, int listSize, qboolean ignoreSatchelCharge ) { + int count; + gentity_t *trav, *dyn; + vec3_t pos, vec; + botExplosiveCache_t* pCache = &g_botSatchelCache[team - TEAM_AXIS]; + + if ( ( pCache->time == level.time ) && ( pCache->listSize == listSize ) && ( pCache->ignore == ignoreSatchelCharge ) ) { + // cache hit + if ( listSize <= GETTARGETEXPLOSIVE_CACHE_SIZE ) { + memcpy( list, pCache->list, sizeof( int ) * listSize ); + return pCache->count; + } + } + + count = 0; + trav = NULL; + for ( trav = G_FindSatchelChargeTargetForTeam( NULL, team ); trav; trav = G_FindSatchelChargeTargetForTeam( trav->parent, team ) ) { + if ( !ignoreSatchelCharge ) { + // is there already some dynamite planted here? + VectorAdd( trav->r.absmax, trav->r.absmin, pos ); + VectorScale( pos, 0.5, pos ); + + // Gordon: could really do with just having some ref counting for this... + for ( dyn = G_FindSatchels( NULL ); dyn; dyn = G_FindSatchels( dyn ) ) { + G_AdjustedDamageVec( trav, dyn->r.currentOrigin, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( dyn->splashRadius ) ) && CanDamage( trav, dyn->r.currentOrigin ) ) { + if ( listSize ) { + if ( list && count >= listSize ) { + continue; + } + continue; // planted satchel charge was found + } + } + } + } + + if ( list ) { + list[count] = trav->s.number; + } + + count++; + if ( list && count >= listSize ) { + break; + } + } + + // set the cache items + if ( list && listSize <= GETTARGETEXPLOSIVE_CACHE_SIZE ) { + memcpy( pCache->list, list, sizeof( int ) * listSize ); + pCache->count = count; + pCache->ignore = ignoreSatchelCharge; + pCache->listSize = listSize; + pCache->time = level.time; + } + + return count; +} + + +/* +================== +BotGetTargetExplosives +================== +*/ +int BotGetTargetExplosives( team_t team, int *list, int listSize, qboolean ignoreDynamite ) { + int count; + gentity_t *trav, *dyn; + vec3_t pos, vec; + botExplosiveCache_t* pCache = &g_botExplosiveCache[team - TEAM_AXIS]; + + if ( ( pCache->time == level.time ) && ( pCache->listSize == listSize ) && ( pCache->ignore == ignoreDynamite ) ) { + // cache hit + if ( listSize <= GETTARGETEXPLOSIVE_CACHE_SIZE ) { + memcpy( list, pCache->list, sizeof( int ) * listSize ); + return pCache->count; + } + } + + count = 0; + trav = NULL; + for ( trav = G_FindDynamiteTargetForTeam( NULL, team ); trav; trav = G_FindDynamiteTargetForTeam( trav->parent, team ) ) { + if ( !ignoreDynamite ) { + // is there already some dynamite planted here? + VectorAdd( trav->r.absmax, trav->r.absmin, pos ); + VectorScale( pos, 0.5, pos ); + + for ( dyn = G_FindDynamite( NULL ); dyn; dyn = G_FindDynamite( dyn ) ) { + G_AdjustedDamageVec( trav, dyn->r.currentOrigin, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( dyn->splashRadius ) ) && CanDamage( trav, dyn->r.currentOrigin ) ) { + // Gordon: checking damage makes no sense in this case, as these are one hit wonders + if ( listSize ) { + if ( list && count >= listSize ) { + continue; + } + continue; // planted dynamite was found + } + } + } + } + + if ( list ) { + if ( trav->s.eType == ET_EXPLOSIVE ) { + list[count] = trav->parent->s.number; + } else { + list[count] = trav->s.number; + } + } + + count++; + if ( list && count >= listSize ) { + break; + } + } + + // set the cache items + if ( list && listSize <= GETTARGETEXPLOSIVE_CACHE_SIZE ) { + memcpy( pCache->list, list, sizeof( int ) * listSize ); + pCache->count = count; + pCache->ignore = ignoreDynamite; + pCache->listSize = listSize; + pCache->time = level.time; + } + + return count; +} + +int GetTargetExplosives( team_t team, qboolean ignoreDynamite ) { + return BotGetTargetExplosives( team, NULL, 0, ignoreDynamite ); +} + +/* +================== +BotGetTargetDynamite +================== +*/ +int BotGetTargetDynamite( int *list, int listSize, gentity_t* target ) { + gentity_t *dyn, *trav; + vec3_t vec; + int count = 0; + team_t team; + + for ( dyn = G_FindDynamite( NULL ); dyn; dyn = G_FindDynamite( dyn ) ) { + // RF, if the dynamite is unarmed, ignore + if ( dyn->s.teamNum >= 4 ) { + continue; + } + for ( team = TEAM_AXIS; team <= TEAM_ALLIES; team++ ) { + vec3_t mins, maxs; + VectorAdd( dyn->r.currentOrigin, dyn->r.mins, mins ); + VectorAdd( dyn->r.currentOrigin, dyn->r.maxs, maxs ); + + if ( target ) { + if ( target->s.eType == ET_EXPLOSIVE ) { + if ( !target->parent ) { + continue; + } + + if ( BG_BBoxCollision( dyn->r.absmin, dyn->r.absmax, target->parent->r.absmin, target->parent->r.absmax ) ) { + if ( list ) { + list[count] = dyn->s.number; + } + count++; + break; + } + } else { + G_AdjustedDamageVec( target, dyn->r.currentOrigin, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( dyn->splashRadius ) ) && CanDamage( target, dyn->r.currentOrigin ) ) { + if ( list ) { + list[count] = dyn->s.number; + } + count++; + break; + } + } + } else { + for ( trav = G_FindDynamiteTargetForTeam( NULL, team ); trav; trav = G_FindDynamiteTargetForTeam( trav->parent, team ) ) { + if ( trav->s.eType == ET_EXPLOSIVE ) { + if ( !trav->parent ) { + continue; + } + + if ( BG_BBoxCollision( dyn->r.absmin, dyn->r.absmax, trav->parent->r.absmin, trav->parent->r.absmax ) ) { + if ( list ) { + list[count] = dyn->s.number; + } + count++; + break; + } + } else { + G_AdjustedDamageVec( trav, dyn->r.currentOrigin, vec ); + if ( ( VectorLengthSquared( vec ) <= SQR( dyn->splashRadius ) ) && CanDamage( trav, dyn->r.currentOrigin ) ) { + if ( list ) { + list[count] = dyn->s.number; + } + count++; + break; + } + } + } + } + + if ( list && count >= listSize ) { + break; + } + } + } + + return count; +} + +/* +================== +BotGetConstructibles +================== +*/ +int BotGetConstructibles( team_t team, int *list, int listSize, qboolean ignoreBuilt ) { + gentity_t *trav, *targ; + int count = 0; + + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_OBJECTIVE_INFO ) ) ) { + if ( !trav->r.linked ) { + continue; + } + + targ = G_ConstructionForTeam( trav, team ); + if ( !targ ) { + continue; + } + + if ( ignoreBuilt && G_ConstructionIsFullyBuilt( targ ) ) { + continue; + } + + + if ( listSize >= 0 ) { + if ( list ) { + list[count] = targ->s.number; + } + count++; + } + + if ( listSize > 0 && list && count >= listSize ) { + break; + } + } + + return count; +} + +/* +================== +BotNumTeamMembers +================== +*/ +int BotNumTeamMembers( int team ) { + gclient_t *cl; + int i, cnt; + + cl = &level.clients[0]; + cnt = 0; + for ( i = 0; i < level.maxclients; i++, cl++ ) { + if ( !( cl->pers.connected == CON_CONNECTED ) ) { + continue; + } + if ( cl->sess.sessionTeam == team ) { + cnt++; + } + } + // + return cnt; +} + +/* +================== +BotNumTeamClasses +================== +*/ +int BotNumTeamClasses( team_t team, int mpClass, int ignore ) { + gclient_t *cl; + int i, cnt, j; + + cnt = 0; + for ( i = 0; i < level.numConnectedClients; i++ ) { + j = level.sortedClients[i]; + + if ( j == ignore ) { + continue; + } + + if ( !g_entities[j].inuse ) { + continue; + } + + cl = &level.clients[j]; + if ( cl->sess.sessionTeam != team ) { + continue; + } + + if ( g_entities[j].r.svFlags & SVF_BOT ) { + if ( BotIsDead( &botstates[j] ) ) { + if ( botstates[j].mpClass != mpClass ) { + continue; + } + } else { + if ( cl->sess.playerType != mpClass ) { + continue; + } + } + } else { + if ( cl->ps.pm_flags & PMF_LIMBO ) { + if ( cl->sess.latchPlayerType != mpClass ) { + continue; + } + } else { + if ( cl->sess.playerType != mpClass ) { + continue; + } + } + } + + cnt++; + } + + return cnt; +} + +/* +================== +BotCheckNeedEngineer +================== +*/ +qboolean BotCheckNeedEngineer( bot_state_t *bs, team_t team ) { + static int teamLastTime[2] = { 0, 0 }; + int* lastTime = &teamLastTime[ team == TEAM_AXIS ? 0 : 1 ]; + + // Gordon: want a couple at the start + if ( level.time - level.startTime < 20000 ) { + if ( BotNumTeamClasses( team, PC_ENGINEER, bs->client ) < 2 ) { + return qtrue; + } else { + return qfalse; + } + } + + if ( *lastTime && *lastTime <= level.time && *lastTime > level.time - 10000 ) { + return qfalse; // only check every 10 seconds, to prevent everyone from changing class at once + } + *lastTime = level.time; + + if ( bs->last_fire > level.time - 10000 ) { + return qfalse; + } + + // flag checks + if ( BotCarryingFlag( bs->client ) || ( bs->leader > -1 && BotCarryingFlag( bs->leader ) ) ) { // dont abandon a leader that needs us + return qfalse; + } + + if ( BotNumTeamClasses( team, PC_ENGINEER, bs->client ) > 0 ) { + return qfalse; + } + + if ( !BotGetTargetExplosives( team, NULL, 0, qfalse ) && !BotGetConstructibles( team, NULL, 0, qtrue ) ) { + return qfalse; + } + + // + // we need engineer's + return qtrue; +} + +/* +================== +BotSuggestClass +================== +*/ +int BotSuggestClass( bot_state_t *bs, team_t team ) { + gclient_t *cl; + int i; + int numRequired[NUM_PLAYER_CLASSES]; +// int list[10], numList; + int numTeamMembers; + + int classPriority[NUM_PLAYER_CLASSES] = {PC_ENGINEER, PC_SOLDIER, PC_MEDIC, PC_FIELDOPS, PC_COVERTOPS}; +// char userinfo[MAX_INFO_STRING], *str; + float bestDiff, diff; + int bestClass; + qboolean needEngineers = qfalse; + int lastMg42Death = ( team == TEAM_ALLIES ? level.alliesMG42Counter : level.axisMG42Counter ); + + cl = &level.clients[bs->entitynum]; + + // if we have a specified class, then use that +/* trap_GetUserinfo( bs->client, userinfo, sizeof(userinfo) ); + if ((str = Info_ValueForKey( userinfo, "pClass" )) && strlen(str)) { + i = atoi(str); + if (i > 0) { + return i-1; + } + }*/ + + // quick test for engineers + if ( BotCheckNeedEngineer( bs, team ) ) { + return PC_ENGINEER; + } + + memset( numRequired, 0, sizeof( numRequired ) ); + numTeamMembers = BotNumTeamMembers( team ); + + // do we need to destroy barriers? + if ( BotGetTargetExplosives( team, NULL, 0, qtrue ) || BotGetConstructibles( team, NULL, 0, qtrue ) ) { + needEngineers = qtrue; + } else { + numRequired[PC_ENGINEER] = 0; + } + + if ( needEngineers ) { + if ( numTeamMembers <= 3 ) { + numRequired[PC_ENGINEER] = 1; + } else { + numRequired[PC_ENGINEER] = (int)ceil( numTeamMembers / 3.f ); + } + } + + // we should have at least one of each other class + numTeamMembers -= numRequired[PC_ENGINEER]; + + numRequired[PC_SOLDIER] = numTeamMembers / 2.f > 1 ? numTeamMembers / 2.f : 1; + numRequired[PC_COVERTOPS] = numRequired[PC_FIELDOPS] = numRequired[PC_MEDIC] = ( numTeamMembers / 6.f ) > 1 ? ( numTeamMembers / 6.f ) : 1; + + // + // special cases + if ( lastMg42Death && ( ( level.time - lastMg42Death ) < ( 30 * 1000 ) ) ) { + // use panzers to clear mg42 nests + numRequired[PC_SOLDIER] = ( numTeamMembers - 3 > numRequired[PC_SOLDIER] ? numTeamMembers - 3 : numRequired[PC_SOLDIER] ); + numRequired[PC_COVERTOPS] = numRequired[PC_FIELDOPS] = numRequired[PC_MEDIC] = 1; + } + + // allocate classes in order of priority + bestDiff = 1.0; + bestClass = -1; + for ( i = 0; i < NUM_PLAYER_CLASSES; i++ ) { + if ( !numRequired[classPriority[i]] ) { + continue; + } + diff = BotNumTeamClasses( team, classPriority[i], bs->client ) / (float)numRequired[classPriority[i]]; + if ( bestDiff > diff ) { + // we need one of these + bestDiff = diff; + bestClass = classPriority[i]; + } + } + + if ( bestClass >= 0 ) { + return bestClass; + } + + if ( level.time < level.startTime + 20000 ) { + return PC_SOLDIER; + } + + // Gordon: FIXME: balanace it? and why not engineer, they ARE useful outside of constructing stuff etc... + // not important, so just pick at random + while ( ( i = rand() % NUM_PLAYER_CLASSES ) == PC_ENGINEER ) ; + + return i; +} + +/* +================== +BotSuggestWeapon +================== +*/ +int BotSuggestWeapon( bot_state_t *bs, team_t team ) { + int i, r; + gentity_t *trav; + qboolean noSniper = qfalse; + int lastMg42Death = ( team == TEAM_ALLIES ? level.alliesMG42Counter : level.axisMG42Counter ); + +// char userinfo[MAX_INFO_STRING], *str; + // + // if we have a specified weapon, then use that +/* trap_GetUserinfo( bs->client, userinfo, sizeof(userinfo) ); + if ((str = Info_ValueForKey( userinfo, "pWeapon" )) && Q_stricmp(str, "ANY") && (i = atoi(str))) { + return i; + }*/ + + // + // special cases + if ( level.captureFlagMode ) { + // if defending team, try to get some snipers at the start + if ( ( bs->mpClass == PC_COVERTOPS ) && ( rand() % 3 ) && ( level.time < level.startTime + 120000 ) && ( bs->mpTeam != level.attackingTeam ) ) { + r = rand() % 2; + switch ( r ) { + default: return WP_FG42; + case 1: + switch ( bs->mpTeam ) { + case TEAM_AXIS: + return WP_K43; + default: + return WP_GARAND; + } + } + } + // if attacking, use panzers to clear defenses + if ( ( bs->mpClass == PC_SOLDIER ) && ( rand() % 3 ) && ( level.time < level.startTime + 30000 ) && ( bs->mpTeam == level.attackingTeam ) ) { + //return 4; // panzer + return WP_PANZERFAUST; + } + // if GT_WOLF, and checkpoint is owned by opposition, then no point in having snipers + trav = NULL; + while ( ( trav = BotFindNextStaticEntity( trav, BOTSTATICENTITY_CHECKPOINT ) ) ) { + if ( trav->count == level.attackingTeam ) { + break; + } + } + if ( trav ) { + // checkpoints are not defended + noSniper = qtrue; + } + } + + if ( !noSniper && !( rand() % 2 ) && ( bs->mpClass == PC_COVERTOPS ) ) { + // are there sniper spots available? + i = BotBestSniperSpot( bs ); + if ( i >= 0 ) { + // sniper spots available! + g_entities[i].missionLevel = 0; // dont avoid this spot + + switch ( bs->mpTeam ) { + case TEAM_AXIS: + return WP_K43; + default: + return WP_GARAND; + } + } + } + // else choose at random + + switch ( bs->mpClass ) { + case PC_ENGINEER: + r = rand() % 2; + switch ( bs->mpTeam ) { + case TEAM_AXIS: + switch ( r ) { + default: return WP_KAR98; + case 1: return WP_MP40; + } + default: + switch ( r ) { + default: return WP_CARBINE; + case 1: return WP_THOMPSON; + } + } + case PC_SOLDIER: + if ( lastMg42Death && ( level.time - lastMg42Death ) < ( 60 * 1000 ) ) { + // Gordon: much greater chance of panzerfaust + r = rand() % 12; + } else { + r = rand() % 3; + } + switch ( r ) { + default: return WP_PANZERFAUST; + case 1: return WP_FLAMETHROWER; + case 2: return WP_MOBILE_MG42; + //case 3: return WP_MORTAR; // they dont understand this yet + case 0: + switch ( bs->mpTeam ) { + case TEAM_AXIS: + return WP_MP40; + default: + return WP_THOMPSON; + } + } + break; + case PC_COVERTOPS: + r = rand() % 3; + switch ( r ) { + default: return WP_FG42; + case 1: + switch ( bs->mpTeam ) { + case TEAM_AXIS: + return WP_K43; + default: + return WP_GARAND; + } + case 2: return WP_STEN; + } + break; + } + + switch ( bs->mpTeam ) { + case TEAM_AXIS: + return WP_MP40; + default: + return WP_THOMPSON; + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_BothFlagsNotAtBase( bot_state_t *bs ) { + int numteammates, defenders, attackers, i, other; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME], carriername[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); + //different orders based on the number of team mates + switch ( bs->numteammates ) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to attack the enemy base + if ( teammates[0] != bs->flagcarrier ) { + other = teammates[0]; + } else { other = teammates[1];} + ClientName( other, name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, other ); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to accompany the flag carrier + if ( teammates[0] != bs->flagcarrier ) { + other = teammates[0]; + } else { other = teammates[1];} + ClientName( other, name, sizeof( name ) ); + ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); + if ( bs->flagcarrier == bs->client ) { + BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); + } else { + BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); + } + BotSayTeamOrder( bs, other ); + //tell the one furthest from the the base not carrying the flag to get the enemy flag + if ( teammates[2] != bs->flagcarrier ) { + other = teammates[2]; + } else { other = teammates[1];} + ClientName( other, name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, other ); + break; + } + default: + { + defenders = (int) ( float ) numteammates * 0.4 + 0.5; + attackers = (int) ( float ) numteammates * 0.5 + 0.5; + ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); + for ( i = 0; i < defenders; i++ ) { + // + if ( teammates[i] == bs->flagcarrier ) { + continue; + } + // + ClientName( teammates[i], name, sizeof( name ) ); + if ( bs->flagcarrier == bs->client ) { + BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); + } else { + BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); + } + BotSayTeamOrder( bs, teammates[i] ); + } + for ( i = 0; i < attackers; i++ ) { + // + if ( teammates[numteammates - i - 1] == bs->flagcarrier ) { + continue; + } + // + ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); + } + // + break; + } + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_FlagNotAtBase( bot_state_t *bs ) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); + //different orders based on the number of team mates + switch ( bs->numteammates ) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName( teammates[0], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[0] ); + //the other will get the flag + ClientName( teammates[1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[1] ); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName( teammates[0], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[0] ); + //the other two get the flag + ClientName( teammates[1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[1] ); + // + ClientName( teammates[2], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[2] ); + break; + } + default: + { + defenders = (int) ( float ) numteammates * 0.3 + 0.5; + attackers = (int) ( float ) numteammates * 0.5 + 0.5; + for ( i = 0; i < defenders; i++ ) { + // + ClientName( teammates[i], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[i] ); + } + for ( i = 0; i < attackers; i++ ) { + // + ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); + } + // + break; + } + } +} + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_EnemyFlagNotAtBase( bot_state_t *bs ) { + int numteammates, defenders, attackers, i, other; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME], carriername[MAX_NETNAME]; + + numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); + //different orders based on the number of team mates + switch ( numteammates ) { + case 1: break; + case 2: + { + //tell the one not carrying the flag to defend the base + if ( teammates[0] == bs->flagcarrier ) { + other = teammates[1]; + } else { other = teammates[0];} + ClientName( other, name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, other ); + break; + } + case 3: + { + //tell the one closest to the base not carrying the flag to defend the base + if ( teammates[0] != bs->flagcarrier ) { + other = teammates[0]; + } else { other = teammates[1];} + ClientName( other, name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, other ); + //tell the one furthest from the base not carrying the flag to accompany the flag carrier + if ( teammates[2] != bs->flagcarrier ) { + other = teammates[2]; + } else { other = teammates[1];} + ClientName( other, name, sizeof( name ) ); + ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); + if ( bs->flagcarrier == bs->client ) { + BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); + } else { + BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); + } + BotSayTeamOrder( bs, other ); + break; + } + default: + { + //40% will defend the base + defenders = (int) ( float ) numteammates * 0.4 + 0.5; + //50% accompanies the flag carrier + attackers = (int) ( float ) numteammates * 0.5 + 0.5; + for ( i = 0; i < defenders; i++ ) { + // + if ( teammates[i] == bs->flagcarrier ) { + continue; + } + ClientName( teammates[i], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[i] ); + } + ClientName( bs->flagcarrier, carriername, sizeof( carriername ) ); + for ( i = 0; i < attackers; i++ ) { + // + if ( teammates[numteammates - i - 1] == bs->flagcarrier ) { + continue; + } + // + ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); + if ( bs->flagcarrier == bs->client ) { + BotAI_BotInitialChat( bs, "cmd_accompanyme", name, NULL ); + } else { + BotAI_BotInitialChat( bs, "cmd_accompany", name, carriername, NULL ); + } + BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); + } + // + break; + } + } +} + + +/* +================== +BotCTFOrders +================== +*/ +void BotCTFOrders_BothFlagsAtBase( bot_state_t *bs ) { + int numteammates, defenders, attackers, i; + int teammates[MAX_CLIENTS]; + char name[MAX_NETNAME]; +// char buf[MAX_MESSAGE_SIZE]; + + numteammates = BotSortTeamMatesByBaseTravelTime( bs, teammates, sizeof( teammates ) ); + //different orders based on the number of team mates + switch ( numteammates ) { + case 1: break; + case 2: + { + //the one closest to the base will defend the base + ClientName( teammates[0], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[0] ); + //the other will get the flag + ClientName( teammates[1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[1] ); + break; + } + case 3: + { + //the one closest to the base will defend the base + ClientName( teammates[0], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[0] ); + //the second one closest to the base will defend the base + ClientName( teammates[1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[1] ); + //the other will get the flag + ClientName( teammates[2], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[2] ); + break; + } + default: + { + defenders = (int) ( float ) numteammates * 0.5 + 0.5; + attackers = (int) ( float ) numteammates * 0.3 + 0.5; + for ( i = 0; i < defenders; i++ ) { + // + ClientName( teammates[i], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_defendbase", name, NULL ); + BotSayTeamOrder( bs, teammates[i] ); + } + for ( i = 0; i < attackers; i++ ) { + // + ClientName( teammates[numteammates - i - 1], name, sizeof( name ) ); + BotAI_BotInitialChat( bs, "cmd_getflag", name, NULL ); + BotSayTeamOrder( bs, teammates[numteammates - i - 1] ); + } + // + break; + } + } +} + + +/* +================== +BotTeamOrders +================== +*/ +void BotTeamOrders( bot_state_t *bs ) { + //no teamplay orders at this time +} + + +// Start - TAT 9/20/2002 +// Covert Ops bot searches for a body to steal the uniform from +qboolean BotClass_CovertOpsCheckDisguises( bot_state_t *bs, int maxTravel, bot_goal_t *goal ) { + gentity_t *trav; + int t, area, best = -1, bestTravel, bestArea = -1; // Arnout: bestArea was not initialized + bot_goal_t target; + int list[32], numList; + vec3_t loc; + + //if we are not covert ops + if ( bs->sess.playerType != PC_COVERTOPS ) { + return qfalse; + } + + bestTravel = maxTravel; + + trav = NULL; + // loop through all the corpses in the world + while ( ( trav = G_Find( trav, FOFS( classname ), "corpse" ) ) ) + { + // if on the same team + if ( OnSameTeam( BotGetEntity( bs->client ), trav ) ) { + continue; + } + + // make sure there isn't already a covertop snagging their getup + numList = BotNumTeamMatesWithTargetByClass( bs, trav->s.number, list, 32, PC_COVERTOPS ); + if ( numList ) { + numList = BotReduceListByTravelTime( list, numList, BotGetOrigin( trav->s.number ), BotGetArea( trav->s.number ), BotTravelTimeToEntity( bs, trav->s.number ) ); + } + if ( numList ) { + continue; + } + // + t = 0; + + VectorCopy( trav->r.currentOrigin, loc ); + loc[2] += 30; + + // check the route to them + area = trap_AAS_PointAreaNum( loc ); + if ( area ) { + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); + } + + if ( t && t < bestTravel ) { + BotClearGoal( &target ); + target.entitynum = trav->s.number; + target.areanum = area; + VectorCopy( trav->r.mins, target.mins ); + VectorCopy( trav->r.maxs, target.maxs ); + VectorCopy( loc, target.origin ); + // + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + best = trav->s.number; + bestTravel = t; + bestArea = area; + } + } + } + + // did we find someone + if ( best >= 0 ) { + // yes, so copy the goal over + *goal = target; + + // and return success + return qtrue; + } + // + return qfalse; +} +// End - TAT 9/20/2002 + +/* +============== +BotClass_MedicCheckRevives +============== +*/ +qboolean BotClass_MedicCheckRevives( bot_state_t *bs, int maxtravel, bot_goal_t *goal, qboolean lookForBots ) { + gentity_t *trav; + int i, t, area, best = -1, bestTravel, bestArea = -1; // Arnout: bestArea was not initialized + int teammates[64], numTeammates; + bot_goal_t target; + int list[32], numList; + + //if we are not medic + if ( bs->sess.playerType != PC_MEDIC ) { + return qfalse; + } + + if ( !BotGotEnoughAmmoForWeapon( bs, WP_MEDIC_SYRINGE ) ) { + return qfalse; + } + + bestTravel = maxtravel; + numTeammates = BotNumTeamMates( bs, teammates, 64 ); + for ( i = 0; i < numTeammates; i++ ) { + trav = &g_entities[teammates[i]]; + if ( trav->botIgnoreHealthTime > level.time ) { + continue; + } + + if ( trav->client->ps.pm_type != PM_DEAD ) { + continue; + } + + if ( trav->client->ps.pm_flags & PMF_LIMBO ) { + continue; + } + + // make sure there isn't already another medic helping them + if ( ( numList = BotNumTeamMatesWithTargetByClass( bs, teammates[i], list, 32, PC_MEDIC ) ) ) { + if ( BotReduceListByTravelTime( list, numList, BotGetOrigin( teammates[i] ), BotGetArea( teammates[i] ), BotTravelTimeToEntity( bs, teammates[i] ) ) ) { + continue; + } + } + + // check the route to them + area = BotGetArea( trav->s.number ); //trap_AAS_PointAreaNum(trav->r.currentOrigin); + if ( !area ) { + continue; + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); + if ( !t ) { + continue; + } + + if ( t < bestTravel ) { + BotClearGoal( &target ); + target.entitynum = i; + target.areanum = area; + VectorCopy( g_entities[teammates[i]].r.mins, target.mins ); + VectorCopy( g_entities[teammates[i]].r.maxs, target.maxs ); + VectorCopy( g_entities[teammates[i]].r.currentOrigin, target.origin ); + + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_HIGH ) ) { + best = teammates[i]; + bestTravel = t; + bestArea = area; + } + } + } + + if ( best >= 0 ) { + // make this our goal? + BotClearGoal( &target ); + target.entitynum = best; + target.areanum = bestArea; + VectorCopy( g_entities[best].r.mins, target.mins ); + VectorCopy( g_entities[best].r.maxs, target.maxs ); + VectorCopy( g_entities[best].r.currentOrigin, target.origin ); + + *goal = target; + return qtrue; + } + + return qfalse; +} + +/* +============== +G_RequestedHealth +============== +*/ +qboolean G_RequestedHealth( bot_state_t *bs, int client, qboolean clearRequest ) { + bot_chat_t *trav; + int i; + + trav = bs->vchats; + for ( i = 0; i < MAX_VCHATS; i++, trav++ ) { + if ( !trav->time ) { + continue; + } + + if ( trav->time < level.time - 8000 ) { + trav->time = 0; + continue; + } + + if ( trav->client != client ) { + continue; + } + + if ( !OnSameTeam( BotGetEntity( trav->client ), BotGetEntity( client ) ) ) { + continue; + } + + if ( trav->id != VCHAT_MEDIC ) { + continue; + } + + // they want health! + if ( clearRequest ) { + trav->time = 0; // chat has been processed + } + + return qtrue; + } + return qfalse; +} + +/* +============== +BotClass_MedicCheckGiveHealth +============== +*/ +qboolean BotClass_MedicCheckGiveHealth( bot_state_t *bs, int maxTravelTime, bot_goal_t *goal ) { + gentity_t *trav; + int i, area, best = -1, bestArea = -1; // Arnout: bestArea was not initialized + int time; + int bestTravelTime = maxTravelTime; + int teammates[64], numTeammates; + bot_goal_t target; + int list[32], numList; + + //if we are not medic + if ( bs->sess.playerType != PC_MEDIC ) { + return qfalse; + } + + if ( !BotWeaponCharged( bs, WP_MEDKIT ) ) { + return qfalse; + } + + numTeammates = BotNumTeamMates( bs, teammates, 64 ); + for ( i = 0; i < numTeammates; i++ ) { + float scale; + trav = &g_entities[teammates[i]]; + + scale = BotHealthScale( trav - g_entities ); + if ( scale <= 0 || scale >= 1.f ) { + continue; + } + + if ( trav->botIgnoreHealthTime > level.time ) { + continue; + } + + if ( trav->s.number == bs->target_goal.entitynum ) { + continue; + } + + if ( trav->client->sess.playerType == PC_MEDIC ) { + continue; + } + + if ( trav->client->ps.pm_type != PM_NORMAL ) { + continue; + } + + // make sure there isn't already another medic helping them + if ( ( numList = BotNumTeamMatesWithTargetByClass( bs, teammates[i], list, 32, PC_MEDIC ) ) ) { + if ( BotReduceListByTravelTime( list, numList, BotGetOrigin( teammates[i] ), BotGetArea( teammates[i] ), BotTravelTimeToEntity( bs, teammates[i] ) ) ) { + continue; + } + } + + // check the route to them + area = BotGetArea( trav->s.number ); //trap_AAS_PointAreaNum(trav->r.currentOrigin); + if ( !area ) { + continue; + } + + time = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); + if ( !time ) { + continue; + } + + if ( !G_RequestedHealth( bs, trav->s.number, qfalse ) ) { + if ( !trap_InPVS( bs->origin, trav->r.currentOrigin ) ) { + continue; + } + if ( BotHealthScale( trav->s.number ) > 0.5 ) { + time += ( BotHealthScale( trav->s.number ) - 0.5 ) * 100 * 5; + } + } else { + time -= 1500; + if ( time <= 0 ) { + time = 1; + } + } + + if ( time && time < bestTravelTime ) { + BotClearGoal( &target ); + target.entitynum = i; + target.areanum = area; + VectorCopy( g_entities[teammates[i]].r.mins, target.mins ); + VectorCopy( g_entities[teammates[i]].r.maxs, target.maxs ); + VectorCopy( g_entities[teammates[i]].r.currentOrigin, target.origin ); + + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + best = teammates[i]; + bestTravelTime = time; + bestArea = area; + } + } + } + + if ( best >= 0 ) { + G_RequestedHealth( bs, best, qtrue ); + + // make this our goal? + BotClearGoal( &target ); + target.entitynum = best; + target.areanum = bestArea; + VectorCopy( g_entities[best].r.mins, target.mins ); + VectorCopy( g_entities[best].r.maxs, target.maxs ); + VectorCopy( g_entities[best].r.currentOrigin, target.origin ); + + *goal = target; + return qtrue; + } + + return qfalse; +} + +/* +============== +G_RequestedAmmo +============== +*/ +qboolean G_RequestedAmmo( bot_state_t *bs, int client, qboolean clear ) { + bot_chat_t *trav; + int i; + qboolean clearRequest = qfalse; + // + if ( client < 0 ) { + clearRequest = qtrue; + client = -client - 1; + } + // + trav = bs->vchats; + for ( i = 0; i < MAX_VCHATS; i++, trav++ ) { + if ( !trav->time ) { + continue; + } + if ( trav->time < level.time - 8000 ) { + trav->time = 0; + continue; + } + if ( trav->client != client ) { + continue; + } + if ( !OnSameTeam( BotGetEntity( trav->client ), BotGetEntity( client ) ) ) { + continue; + } + if ( trav->id != VCHAT_NEEDAMMO ) { + continue; + } + // they want ammo! + if ( clearRequest ) { + trav->time = 0; // chat has been processed + } + return qtrue; + } + // + return qfalse; +} + +/* +============== +BotClass_LtCheckGiveAmmo +============== +*/ +qboolean BotClass_LtCheckGiveAmmo( bot_state_t *bs, int maxTravelTime, bot_goal_t *goal ) { + gentity_t *trav; + int i, area = 0, best = -1, bestArea = -1; // Arnout: bestArea was not initialized + int time; + int bestTravelTime = maxTravelTime; + int teammates[64], numTeammates; + bot_goal_t target; + int list[32], numList; + + //if we are not fieldops + if ( bs->sess.playerType != PC_FIELDOPS ) { + return qfalse; + } + + if ( !BotWeaponCharged( bs, WP_AMMO ) ) { + return qfalse; + } + + numTeammates = BotNumTeamMates( bs, teammates, 64 ); + for ( i = 0; i < numTeammates; i++ ) { + trav = &g_entities[teammates[i]]; + if ( trav->health <= 0 ) { + continue; + } + + if ( trav->client->sess.playerType == PC_FIELDOPS ) { + continue; // fieldops's dont need ammo from us + } + + if ( trav->botIgnoreAmmoTime > level.time ) { + continue; + } + + if ( trav->s.number == bs->target_goal.entitynum ) { + continue; + } + + // make sure there isn't already another field op helping them + if ( ( numList = BotNumTeamMatesWithTargetByClass( bs, teammates[i], list, 32, PC_FIELDOPS ) ) ) { + if ( BotReduceListByTravelTime( list, numList, BotGetOrigin( teammates[i] ), BotGetArea( teammates[i] ), BotTravelTimeToEntity( bs, teammates[i] ) ) ) { + continue; + } + } + + time = 0; + if ( trav->client->ps.pm_type == PM_NORMAL ) { + + // if they requested ammo, ignore check for PVS + if ( !G_RequestedAmmo( bs, teammates[i], qfalse ) ) { + // do they need ammo? + if ( !ClientNeedsAmmo( teammates[i] ) ) { + continue; + } + if ( !trap_InPVS( bs->origin, trav->r.currentOrigin ) ) { + continue; + } + } else { + if ( !ClientNeedsAmmo( teammates[i] ) ) { + continue; + } + } + + // check the route to them + if ( !( area = BotGetArea( trav->s.number ) ) ) { + continue; + } + + time = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, area, bs->tfl ); + if ( !time ) { + continue; + } + + if ( G_RequestedAmmo( bs, teammates[i], qfalse ) ) { + time -= 1500; + if ( time < 0 ) { + time = 1; + } + } + } + + if ( time && time < bestTravelTime ) { + BotClearGoal( &target ); + target.entitynum = i; + target.areanum = area; + VectorCopy( g_entities[teammates[i]].r.mins, target.mins ); + VectorCopy( g_entities[teammates[i]].r.maxs, target.maxs ); + VectorCopy( g_entities[teammates[i]].r.currentOrigin, target.origin ); + + if ( BotGoalWithinMovementAutonomy( bs, &target, BGU_LOW ) ) { + best = teammates[i]; + bestTravelTime = time; + bestArea = area; + } + } + } + + if ( best >= 0 ) { + G_RequestedAmmo( bs, best, qtrue ); // clear the request + + // make this our goal + BotClearGoal( &target ); + target.entitynum = best; + target.areanum = bestArea; + VectorCopy( g_entities[best].r.mins, target.mins ); + VectorCopy( g_entities[best].r.maxs, target.maxs ); + VectorCopy( g_entities[best].r.currentOrigin, target.origin ); + + *goal = target; + return qtrue; + } + + return qfalse; +} + +extern void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ); + +// NOTE!!! must be in synch with enum table in ai_team.h +char *vchat_idstr[] = +{ + "HealMe", + "NeedAmmo", + "NeedBackup", + "DynamitePlanted", + "GreatShot", + "Hi", + "Thanks", + "FollowMe", + "DropFlag", + "AggressionLow", + "AggressionMed", + "AggressionHigh", + "AggressionCycle", + "AutonomyLow", + "AutonomyMed", + "AutonomyHigh", + "AutonomyCycle", + + // Start - TAT 8/21/2002 + // corresponding strings from the .menu file that trigger the vchat events below + "HoldPosition", + // End - TAT 8/21/2002 + + // START xkan, 9/13/2002 + "Prone", + // END xkan, 9/13/2002 + "Crouch", + "Stand", + "PositionCycle", + + "FireteamA", + "FireteamB", + "FireteamC", + + "ReviveMe", + "HealTeam", + "TeamAmmo", + // End - TAT 9/23/2002 + + "CycleWeapon", + "BestWeapon", + + "HelpMe", + "HelpTeam", + + NULL +}; + +/* +=============== +BotRecordVoiceChat +=============== +*/ +void BotRecordVoiceChat( int client, int destclient, const char *id, int mode, qboolean noResponse ) { + int i,j, vchat_id; + bot_chat_t *trav, *oldest; + bot_state_t *bs; + + if ( destclient == client ) { + return; + } + + if ( noResponse ) { + return; + } + + vchat_id = -1; + for ( j = 0; vchat_idstr[j]; j++ ) { + if ( !Q_stricmp( id, vchat_idstr[j] ) ) { + vchat_id = j; + break; + } + } + + if ( vchat_id == -1 ) { + return; // not a known vchat + } + + bs = &botstates[destclient]; + if ( !bs->inuse ) { + return; + } + + // find a free chat slot + trav = bs->vchats; + oldest = NULL; + for ( i = 0; i < MAX_VCHATS; i++, trav++ ) { + if ( trav->time ) { + if ( !oldest || trav->time < oldest->time ) { + oldest = trav; + } + continue; + } + + trav->id = vchat_id; + + // RF, if this is a request to drop the flag from a teammate, then drop it + if ( trav->id == VCHAT_DROPFLAG ) { + if ( bs && BotSameTeam( bs, client ) && BotCarryingFlag( destclient ) ) { + BotDropFlag( bs ); + } + } + + // record it + oldest = NULL; + break; + } + + if ( i == MAX_VCHATS ) { + trav = NULL; + } + + if ( oldest ) { + trav = oldest; + trav->id = vchat_id; + } + + if ( trav ) { + trav->client = client; + trav->mode = mode; + trav->time = level.time + 1200 + rand() % 2000; + + BotCheckVoiceChatResponse( bs ); + } +} + +/* +=============== +BotCheckVoiceChatResponse +=============== +*/ +void BotCheckVoiceChatResponse( bot_state_t *bs ) { + int i; + bot_chat_t *trav; + gentity_t *ent, *other; + qboolean clear; + + ent = &g_entities[bs->client]; + trav = bs->vchats; + for ( i = 0; i < MAX_VCHATS; i++, trav++ ) { + if ( !trav->time ) { + continue; + } + if ( trav->time < level.time ) { + continue; + } + if ( trav->time > level.time + 5000 ) { + memset( trav, 0, sizeof( *trav ) ); + continue; + } + + other = &g_entities[trav->client]; + clear = qfalse; + + switch ( trav->id ) { + case VCHAT_THANKS: + clear = qtrue; + if ( !BotSameTeam( bs, trav->client ) ) { + break; + } + + // did we just help them? + if ( bs->last_helped_client != trav->client ) { + break; + } + if ( bs->last_helped_time < level.time - 5000 ) { + break; + } + + // your welcome! + BotVoiceChatAfterIdleTime( bs->client, "Welcome", SAY_TEAM, 1000 + rand() % 1000, BOT_SHOWTEXT, 3000, qfalse ); + break; + case VCHAT_GREATSHOT: + clear = qtrue; + // did we just kill them? + if ( other->health > 0 ) { + break; + } + if ( other->client->lasthurt_client != bs->client ) { + break; + } + if ( ent->client->lastKillTime > level.time - 10000 ) { + break; + } + + // thanks! + BotSendVoiceChat( bs, "Thanks", SAY_ALL, 1000 + rand() % 1000, BOT_SHOWTEXT, qfalse ); + break; + case VCHAT_HI: + clear = qtrue; + if ( other->client->sess.sessionTeam && !BotSameTeam( bs, trav->client ) ) { + break; + } + + // if we dont feel like talking + if ( rand() % 100 > 50 ) { + break; + } + + if ( other->client->sess.sessionTeam ) { + BotVoiceChatAfterIdleTime( bs->client, "Hi", SAY_TEAM, 1000 + rand() % 6000, BOT_SHOWTEXT, 7000, qfalse ); + } else { + BotVoiceChatAfterIdleTime( bs->client, "Hi", SAY_ALL, 1000 + rand() % 6000, BOT_SHOWTEXT, 7000, qfalse ); + } + break; + + // Make these explicit requests + case VCHAT_NEEDAMMO: + case VCHAT_MEDIC: + break; + } + + if ( clear ) { + memset( trav, 0, sizeof( *trav ) ); + } + } +} + +/* +=============== +BotDelayedVoiceChat +=============== +*/ +void BotDelayedVoiceChat( gentity_t *ent ) { + bot_state_t *bs; + + // Should we force the voice to play even if the bot is dead? + qboolean forceIsDead = ent->spawnflags & 1; + + // + if ( level.intermissiontime ) { + G_FreeEntity( ent ); + return; + } + // + bs = &botstates[ent->r.ownerNum]; + if ( !bs->inuse ) { + G_FreeEntity( ent ); + return; + } + // + if ( !forceIsDead && BotIsDead( bs ) ) { + G_FreeEntity( ent ); + return; + } + // + if ( g_entities[ent->r.ownerNum].inuse && ent->count2 == g_entities[ent->r.ownerNum].client->pers.connectTime ) { + bs->last_voice_chat = level.time; + G_Voice( BotGetEntity( ent->r.ownerNum ), NULL, ent->missionLevel, ent->spawnitem, ent->count ); + } + + G_FreeEntity( ent ); +} + +/* +=============== +BotSendVoiceChat +=============== +*/ +void BotSendVoiceChat( bot_state_t *bs, const char *id, int mode, int delay, qboolean voiceonly, qboolean forceIfDead ) { + gentity_t *thinker; + // + if ( level.intermissiontime ) { + return; + } + // + if ( !forceIfDead && BotIsDead( bs ) ) { + return; + } + // + bs->last_voice_chat = level.time; + // + if ( delay ) { + thinker = G_Spawn(); + if ( !thinker ) { + return; + } + thinker->nextthink = level.time + delay; + thinker->think = BotDelayedVoiceChat; + + thinker->spawnitem = (char *)id; + thinker->r.ownerNum = bs->client; + thinker->missionLevel = mode; + thinker->count = voiceonly; + thinker->count2 = g_entities[bs->client].client->pers.connectTime; + + // We need to signal if this VO can play if the bot is dead + if ( forceIfDead ) { + thinker->spawnflags |= 1; + } + + return; + } + G_Voice( BotGetEntity( bs->client ), NULL, mode, id, voiceonly ); +} + +/* +=============== +BotVoiceChatAfterIdleTime +=============== +*/ +void BotVoiceChatAfterIdleTime( int client, const char *id, int mode, int delay, qboolean voiceonly, int idleTime, qboolean forceIfDead ) { + bot_state_t* bs = &botstates[client]; + if ( !bs->inuse ) { + return; + } + + if ( !forceIfDead && BotIsDead( bs ) ) { + return; + } + + if ( bs->last_voice_chat && bs->last_voice_chat > level.time - idleTime ) { + return; // ignore + + } + BotSendVoiceChat( bs, id, mode, delay, voiceonly, forceIfDead ); +} + + +// +// AI_Team_Init +// +// Description: Blank out the AI Team information +// Written: 10/31/2002 +// +void AI_Team_Init +( + AI_Team_t *thisOne +) { + // Reset the last Team VO time + thisOne->last_voice_chat = 0; + +} +// +// AI_Team_Init +// + + + +// +// AI_Team_Init_All_Teams +// +// Description: Blank out the AI Team information for All teams +// Written: 10/31/2002 +// +void AI_Team_Init_All_Teams +( +) { + // Local Variables //////////////////////////////////////////////////////// + int i; + /////////////////////////////////////////////////////////////////////////// + + // Loop through the AI teams and blank each one + for ( i = 0; i < TEAM_NUM_TEAMS; i++ ) + { + // Blank the team + AI_Team_Init( &( g_aiTeam[i] ) ); + + } // for (i = 0; i < TEAM_NUM_TEAMS; i++)... +} +// +// AI_Team_Init_All_Teams +// + + + +/* +=============== +BotVoiceChatAfterTeamIdleTime +=============== + +Says the specified line if the TEAM has been idle for the specified time. + +*/ +void BotVoiceChatAfterTeamIdleTime( int client, const char *id, int mode, int delay, qboolean voiceonly, int idleTime, qboolean forceIfDead ) { + bot_state_t* bs = &botstates[client]; + + if ( !bs->inuse ) { + return; + } + + // + if ( !forceIfDead && BotIsDead( bs ) ) { + return; + } + + + // If we had a team voice chat too recently + if ( g_aiTeam[bs->sess.sessionTeam].last_voice_chat && ( g_aiTeam[bs->sess.sessionTeam].last_voice_chat > level.time - idleTime ) ) { + // Ignore this request + return; + + } + + // We'll have chatted now + g_aiTeam[bs->sess.sessionTeam].last_voice_chat = level.time; + + // Say the line after the specified delay + BotSendVoiceChat( bs, id, mode, delay, voiceonly, forceIfDead ); +} + +void G_SpawnGEntityFromSpawnVars( void ); +char *G_AddSpawnVarToken( const char *string ); + +/* +=============== +BotSpawnSpecialEntities +=============== +*/ +void BotSpawnSpecialEntities( void ) { + vmCvar_t cvar_mapname; + char keyname[MAX_QPATH]; + char *com_token; + char string[8192], *pStr; + char filename[MAX_QPATH]; + int len; + fileHandle_t f; + + + // HACK, spawn special entities that would usually be compiled into maps + + trap_Cvar_Register( &cvar_mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); + + Com_sprintf( filename, sizeof( filename ), "maps/%s.botents", cvar_mapname.string ); + if ( ( len = trap_FS_FOpenFile( filename, &f, FS_READ ) ) < 0 ) { + return; + } + + if ( len >= sizeof( string ) ) { + G_Error( "BotSpawnSpecialEntities: (%s) file is too big", filename ); + } + + memset( string, 0, sizeof( string ) ); + trap_FS_Read( string, len, f ); + + trap_FS_FCloseFile( f ); + + pStr = string; + + G_Printf( "Enable spawning!\n" ); + level.spawning = qtrue; + + while ( 1 ) { + level.numSpawnVars = 0; + level.numSpawnVarChars = 0; + + // parse the opening brace + com_token = COM_Parse( &pStr ); + if ( !com_token || !com_token[0] ) { + // end of spawn string + return; + } + if ( com_token[0] != '{' ) { + G_Error( "BotSpawnSpecialEntities: (%s) found %s when expecting {", filename, com_token ); + } + + // go through all the key / value pairs + while ( 1 ) { + // parse key + com_token = COM_Parse( &pStr ); + if ( !com_token || !com_token[0] ) { + G_Error( "BotSpawnSpecialEntities: (%s) EOF without closing brace", filename ); + } + Q_strncpyz( keyname, com_token, sizeof( keyname ) ); + if ( keyname[0] == '}' ) { + break; + } + + // parse value + com_token = COM_Parse( &pStr ); + if ( !com_token || !com_token[0] ) { + G_Error( "BotSpawnSpecialEntities: (%s) EOF without closing brace", filename ); + } + + if ( com_token[0] == '}' ) { + G_Error( "BotSpawnSpecialEntities: (%s) closing brace without data", filename ); + } + if ( level.numSpawnVars == MAX_SPAWN_VARS ) { + G_Error( "BotSpawnSpecialEntities: (%s) MAX_SPAWN_VARS", filename ); + } + level.spawnVars[ level.numSpawnVars ][0] = G_AddSpawnVarToken( keyname ); + level.spawnVars[ level.numSpawnVars ][1] = G_AddSpawnVarToken( com_token ); + level.numSpawnVars++; + } + + // spawn the entity + G_SpawnGEntityFromSpawnVars(); + } + + G_Printf( "Disable spawning!\n" ); + level.spawning = qfalse; + + return; +} + +/* +============== +G_RequestedFollow +============== +*/ +qboolean G_RequestedFollow( bot_state_t *bs, int client ) { + bot_chat_t *trav; + int i; + qboolean clearRequest = qfalse; + // + if ( client < 0 ) { + clearRequest = qtrue; + client = -client - 1; + } + // + trav = bs->vchats; + for ( i = 0; i < MAX_VCHATS; i++, trav++ ) { + if ( !trav->time ) { + continue; + } + if ( trav->time < level.time - 30000 ) { + trav->time = 0; + continue; + } + if ( trav->client != client ) { + continue; + } + if ( !OnSameTeam( BotGetEntity( trav->client ), BotGetEntity( client ) ) ) { + continue; + } + if ( trav->id != VCHAT_FOLLOWME && trav->id != VCHAT_NEEDBACKUP ) { + continue; + } + // they want us to follow! + //if (clearRequest) + trav->time = 0; // chat has been processed + return qtrue; + } + // + return qfalse; +} + +/* +=============== +BotGetLeader +=============== +*/ +int BotGetLeader( bot_state_t *bs, qboolean onlyRequested ) { + int i, j, t, best = -1, bestTime = 99999; + gentity_t *trav; + bot_goal_t goal; + + int heavyWeapons[] = {WP_PANZERFAUST, WP_MOBILE_MG42, -1}; + + qboolean requested, bestRequested = qfalse; + int list[MAX_CLIENTS], numList; + float ourDist, *distances; + bot_state_t *obs; + + // + // if we have a scripted leader + if ( bs->leader > -1 && ( bs->script.frameFlags & BSFFL_FOLLOW_LEADER ) && ( bs->leader == bs->script.entityNum ) ) { + return bs->leader; + } + // if our current leader is carrying the flag, stay with them + if ( bs->leader > -1 && BotCarryingFlag( bs->leader ) ) { + return bs->leader; + } + // if we have a scripting enforced movement autonomy, stay put, unless we are given a new scripted leader + if ( bs->leader == -1 && ( bs->script.flags & BSFL_FORCED_MOVEMENT_AUTONOMY ) ) { + return -1; + } + best = bs->leader; + + // + // find a teammate that is close, and is worth protecting (heavy weapons) + for ( i = 0; i < level.numConnectedClients; i++, trav++ ) { + trav = &g_entities[level.sortedClients[i]]; + + if ( i == bs->client ) { + continue; + } + + if ( bs->leader == i ) { + continue; + } + + if ( trav->r.svFlags & SVF_BOT ) { + obs = &botstates[i]; + } else { + obs = NULL; + } + + if ( trav->health <= 0 ) { + continue; + } + + if ( !BotSameTeam( bs, i ) ) { + continue; + } + + if ( obs && obs->leader > -1 ) { + continue; + } + + requested = qfalse; + if ( G_RequestedFollow( bs, i ) ) { + requested = qtrue; + } + + if ( BotCarryingFlag( i ) ) { + requested = qtrue; + } + + if ( !requested && obs ) { + // let us follow engineers only + if ( trav->client->sess.playerType != PC_ENGINEER ) { + continue; // if not a bot, only follow if requested + } + } + + if ( !requested && bestRequested ) { + continue; + } + + if ( !requested && bs->leader > -1 ) { + continue; + } + +/* if (!requested && (trav->client->sess.playerType != PC_SOLDIER)) { + // follow engineers only if they have a critical task + if (trav->client->sess.playerType == PC_ENGINEER && obs) { + if (!(obs->ainode == AINode_MP_DynamiteTarget || obs->ainode == AINode_MP_ConstructibleTarget)) { + continue; + } else { + requested = qtrue; + } + } else { + continue; + } + }*/ + + if ( ( ourDist = VectorDistanceSquared( trav->r.currentOrigin, bs->origin ) ) > ( MAX_BOTLEADER_DIST * MAX_BOTLEADER_DIST ) ) { + continue; + } + + if ( requested && ( !trap_InPVS( trav->r.currentOrigin, bs->origin ) || ( ourDist > ( 1024 * 1024 ) ) ) ) { + continue; + } + + if ( ( numList = BotNumTeamMatesWithTarget( bs, i, list, MAX_CLIENTS ) ) >= 2 + ( requested ? 2 : 0 ) ) { + // if there are too many defenders, and we are the furthest away, dont defend + distances = BotSortPlayersByDistance( trav->r.currentOrigin, list, numList ); + if ( distances[numList] < ourDist ) { + // we are the furthest + continue; + } + } + + if ( !requested ) { + for ( j = 0; heavyWeapons[j] != -1; j++ ) { + if ( COM_BitCheck( trav->client->ps.weapons, heavyWeapons[j] ) ) { + break; + } + } + + if ( heavyWeapons[j] == -1 ) { + continue; + } + } + + // check travel time + if ( !BotGoalForEntity( bs, i, &goal, BGU_MEDIUM ) && !requested ) { + continue; // NOTE: BotGoalForEntity() must be called first, so that if it fails, but we requested it, it still goes through with valid goal information + } + + t = trap_AAS_AreaTravelTimeToGoalArea( bs->areanum, bs->origin, goal.areanum, bs->tfl ); + if ( !t || t > MAX_BOTLEADER_TRAVEL ) { + continue; + } + + if ( ( bestRequested == requested ) && ( t > bestTime ) ) { + continue; + } + + // we have a valid leader + if ( requested ) { + j = rand() % 2; + if ( j == 0 ) { + BotVoiceChatAfterIdleTime( bs->client, "WhereTo", SAY_TEAM, 1000 + rand() % 3000, BOT_SHOWTEXT, ( requested ? 4000 : 20000 ), qfalse ); + } else if ( j == 1 ) { + BotVoiceChatAfterIdleTime( bs->client, "LetsGo", SAY_TEAM, 1000 + rand() % 3000, BOT_SHOWTEXT, ( requested ? 4000 : 20000 ), qfalse ); + } + + bs->lead_time = trap_AAS_Time() + 99999.0; + bestRequested = qtrue; + } + best = i; + } + // + return best; +} + +int EntGetNumBotFollowers( int entNum ) { + return 0; +} + + +/* +================== +BotSetLeaderTagEnt +================== +*/ +void BotSetLeaderTagEnt( bot_state_t *bs ) { +} + +int BotEngagementFunc( bot_state_t *bs ) { + return 0; +} + +int BotBehaviourFunc( bot_state_t *bs ) { + return 0; +} + +int BotObjectiveFunc( bot_state_t *bs ) { + return 0; +} diff --git a/src/botai/ai_team.h b/src/botai/ai_team.h new file mode 100644 index 0000000..0b94746 --- /dev/null +++ b/src/botai/ai_team.h @@ -0,0 +1,134 @@ +/* +=========================================================================== + +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: ai_team.h + * + * desc: Wolf bot AI + * + * + *****************************************************************************/ + +#ifndef BOT_SHOWTEXT +#define BOT_SHOWTEXT 2 + +typedef enum { + VCHAT_MEDIC, + VCHAT_NEEDAMMO, + VCHAT_NEEDBACKUP, + VCHAT_DYNAMITEPLANTED, + VCHAT_GREATSHOT, + VCHAT_HI, + VCHAT_THANKS, + VCHAT_FOLLOWME, + VCHAT_DROPFLAG, +} vchat_id_t; + + +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// STRUCT AI_Team +// +typedef struct AI_Team_s +{ + // Last time we had a vo from this team + int last_voice_chat; + +} AI_Team_t; +// +// STRUCT AI_Team +// +/////////////////////////////////////////////////////////////////////////////// + + + +qboolean BotCheckNeedEngineer( bot_state_t *bs, team_t team ); +int BotSuggestClass( bot_state_t *bs, team_t team ); +int BotFlagAtBase( int team, gentity_t **returnEnt ); +qboolean BotCheckEmergencyTargets( bot_state_t *bs ); +int BotFindSparseDefendArea( bot_state_t *bs, bot_goal_t *goal, qboolean force ); +int BotNumTeamMatesWithTarget( bot_state_t *bs, int targetEntity, int *list, int maxList ); +int BotNumTeamMatesWithTargetAndCloser( bot_state_t *bs, int targetEntity, int targetArea, int *list, int maxList, int playerType ); +int BotNumTeamMatesWithTargetByClass( bot_state_t *bs, int targetEntity, int *list, int maxList, int playerType ); +int BotNumTeamMates( bot_state_t *bs, int *list, int maxList ); +gentity_t *BotGetEnemyFlagCarrier( bot_state_t *bs ); +// Start - TAT 9/20/2002 +// covert ops bot looks for nearby corpses +qboolean BotClass_CovertOpsCheckDisguises( bot_state_t *bs, int maxTravel, bot_goal_t *goal ); +// End - TAT 9/20/2002 +qboolean BotClass_MedicCheckRevives( bot_state_t *bs, int maxtravel, bot_goal_t *goal, qboolean lookForBots ); +qboolean BotClass_MedicCheckGiveHealth( bot_state_t *bs, int maxTravelTime, bot_goal_t *goal ); +qboolean BotClass_LtCheckGiveAmmo( bot_state_t *bs, int maxTravelTime, bot_goal_t *goal ); +qboolean BotFindDroppedFlag( gentity_t **returnEnt ); +void BotSendVoiceChat( bot_state_t *bs, const char *id, int mode, int delay, qboolean voiceonly, qboolean forceIfDead ); +void BotCheckVoiceChatResponse( bot_state_t *bs ); +void BotSpawnSpecialEntities( void ); +int BotSuggestWeapon( bot_state_t *bs, team_t team ); +int BotGetTeamFlagCarrier( bot_state_t *bs ); +float *BotSortPlayersByDistance( vec3_t target, int *list, int numList ); +float *BotSortPlayersByTraveltime( int areanum, int *list, int numList ); +int BotGetLeader( bot_state_t *bs, qboolean onlyRequested ); +void BotVoiceChatAfterIdleTime( int client, const char *id, int mode, int delay, qboolean voiceonly, int idleTime, qboolean forceIfDead ); +void BotVoiceChatAfterTeamIdleTime( int client, const char *id, int mode, int delay, qboolean voiceonly, int idleTime, qboolean forceIfDead ); +void BotSetLeaderTagEnt( bot_state_t *bs ); +int BotEngagementFunc( bot_state_t *bs ); +int BotBehaviourFunc( bot_state_t *bs ); +int BotObjectiveFunc( bot_state_t *bs ); +qboolean G_RequestedAmmo( bot_state_t *bs, int client, qboolean clear ); +int BotGetConstructibles( team_t team, int *list, int listSize, qboolean ignoreBuilt ); +int BotClosestSeekCoverSpot( bot_state_t *bs ); + +// OK, this one looks in the specified direction (retreat/advance) and +// find the next available spot. +// Returns the entity number of the spot, -1 if N/A. +int BotSquadGetNextAvailableSeekCoverSpot +( + // The info for the bot + bot_state_t *bs, + // Moving forwards or backwards? + qboolean advance, + // entity id of the player we should check being in a seek_cover_sequence + int entityId +); +// TAT 12/19/2002 - When a bot sees an enemy, he tells his squadmates +void BotSquadEnemySight( bot_state_t *bs, int enemy ); + + +// TAT 12/10/2002 +// If possible, find a seek cover spot marked as exposed that is a parent or child of the current seek cover spot +int BotGetAdjacentExposedCoverSpot( bot_state_t *bs ); + +// TAT 1/8/2003 +// Get the next cover spot towards the retreatTo entity - used when retreating +int BotGetRetreatingCoverSpot( bot_state_t *bs, int retreatTo ); + +void AI_Team_Init_All_Teams(); diff --git a/src/botai/botai.h b/src/botai/botai.h new file mode 100644 index 0000000..f848047 --- /dev/null +++ b/src/botai/botai.h @@ -0,0 +1,110 @@ +/* +=========================================================================== + +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: 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/botai/chars.h b/src/botai/chars.h new file mode 100644 index 0000000..9d1477a --- /dev/null +++ b/src/botai/chars.h @@ -0,0 +1,159 @@ +/* +=========================================================================== + +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: 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/botai/inv.h b/src/botai/inv.h new file mode 100644 index 0000000..0a6d441 --- /dev/null +++ b/src/botai/inv.h @@ -0,0 +1,138 @@ +/* +=========================================================================== + +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: 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/botai/match.h b/src/botai/match.h new file mode 100644 index 0000000..ecf1a69 --- /dev/null +++ b/src/botai/match.h @@ -0,0 +1,134 @@ +/* +=========================================================================== + +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: 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/botai/syn.h b/src/botai/syn.h new file mode 100644 index 0000000..965ed32 --- /dev/null +++ b/src/botai/syn.h @@ -0,0 +1,46 @@ +/* +=========================================================================== + +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: 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/botlib/aasfile.h b/src/botlib/aasfile.h new file mode 100644 index 0000000..8bff538 --- /dev/null +++ b/src/botlib/aasfile.h @@ -0,0 +1,285 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +//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/botlib/be_aas_bsp.h b/src/botlib/be_aas_bsp.h new file mode 100644 index 0000000..42849b5 --- /dev/null +++ b/src/botlib/be_aas_bsp.h @@ -0,0 +1,95 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_bspq3.c b/src/botlib/be_aas_bspq3.c new file mode 100644 index 0000000..eb56584 --- /dev/null +++ b/src/botlib/be_aas_bspq3.c @@ -0,0 +1,529 @@ +/* +=========================================================================== + +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: be_aas_bspq3.c + * + * desc: BSP, Environment Sampling + * + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/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/botlib/be_aas_cluster.c b/src/botlib/be_aas_cluster.c new file mode 100644 index 0000000..9066c66 --- /dev/null +++ b/src/botlib/be_aas_cluster.c @@ -0,0 +1,1611 @@ +/* +=========================================================================== + +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: be_aas_cluster.c + * + * desc: area clustering + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_cluster.h b/src/botlib/be_aas_cluster.h new file mode 100644 index 0000000..8106d10 --- /dev/null +++ b/src/botlib/be_aas_cluster.h @@ -0,0 +1,42 @@ +/* +=========================================================================== + +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: be_aas_cluster.h + * + * desc: AAS + * + * + *****************************************************************************/ + +#ifdef AASINTERN +//initialize the AAS clustering +void AAS_InitClustering( void ); +#endif //AASINTERN + diff --git a/src/botlib/be_aas_debug.c b/src/botlib/be_aas_debug.c new file mode 100644 index 0000000..3b0814e --- /dev/null +++ b/src/botlib/be_aas_debug.c @@ -0,0 +1,688 @@ +/* +=========================================================================== + +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: be_aas_debug.c + * + * desc: AAS debug code + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_debug.h b/src/botlib/be_aas_debug.h new file mode 100644 index 0000000..b03294a --- /dev/null +++ b/src/botlib/be_aas_debug.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_def.h b/src/botlib/be_aas_def.h new file mode 100644 index 0000000..9e44151 --- /dev/null +++ b/src/botlib/be_aas_def.h @@ -0,0 +1,303 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_entity.c b/src/botlib/be_aas_entity.c new file mode 100644 index 0000000..ae8a79a --- /dev/null +++ b/src/botlib/be_aas_entity.c @@ -0,0 +1,530 @@ +/* +=========================================================================== + +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: be_aas_entity.c + * + * desc: AAS entities + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_entity.h b/src/botlib/be_aas_entity.h new file mode 100644 index 0000000..1f9281a --- /dev/null +++ b/src/botlib/be_aas_entity.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_file.c b/src/botlib/be_aas_file.c new file mode 100644 index 0000000..c64de95 --- /dev/null +++ b/src/botlib/be_aas_file.c @@ -0,0 +1,681 @@ +/* +=========================================================================== + +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: be_aas_file.c + * + * desc: AAS file loading/writing + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_file.h b/src/botlib/be_aas_file.h new file mode 100644 index 0000000..1e1e7b0 --- /dev/null +++ b/src/botlib/be_aas_file.h @@ -0,0 +1,48 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_funcs.h b/src/botlib/be_aas_funcs.h new file mode 100644 index 0000000..73696ec --- /dev/null +++ b/src/botlib/be_aas_funcs.h @@ -0,0 +1,56 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_main.c b/src/botlib/be_aas_main.c new file mode 100644 index 0000000..04e049e --- /dev/null +++ b/src/botlib/be_aas_main.c @@ -0,0 +1,491 @@ +/* +=========================================================================== + +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: be_aas_main.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_main.h b/src/botlib/be_aas_main.h new file mode 100644 index 0000000..94ff3eb --- /dev/null +++ b/src/botlib/be_aas_main.h @@ -0,0 +1,69 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_move.c b/src/botlib/be_aas_move.c new file mode 100644 index 0000000..89d1102 --- /dev/null +++ b/src/botlib/be_aas_move.c @@ -0,0 +1,1026 @@ +/* +=========================================================================== + +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: be_aas_move.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/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/botlib/be_aas_move.h b/src/botlib/be_aas_move.h new file mode 100644 index 0000000..cd9466c --- /dev/null +++ b/src/botlib/be_aas_move.h @@ -0,0 +1,69 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_optimize.c b/src/botlib/be_aas_optimize.c new file mode 100644 index 0000000..76565b2 --- /dev/null +++ b/src/botlib/be_aas_optimize.c @@ -0,0 +1,657 @@ +/* +=========================================================================== + +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: 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 "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_optimize.h b/src/botlib/be_aas_optimize.h new file mode 100644 index 0000000..afad84f --- /dev/null +++ b/src/botlib/be_aas_optimize.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: be_aas_optimize.h + * + * desc: AAS + * + * + *****************************************************************************/ + +void AAS_Optimize( void ); +void AAS_RemoveNonReachability( void ); +void AAS_RemoveNonGrounded( void ); diff --git a/src/botlib/be_aas_reach.c b/src/botlib/be_aas_reach.c new file mode 100644 index 0000000..31ebeb3 --- /dev/null +++ b/src/botlib/be_aas_reach.c @@ -0,0 +1,4634 @@ +/* +=========================================================================== + +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: be_aas_reach.c + * + * desc: reachability calculations + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_reach.h b/src/botlib/be_aas_reach.h new file mode 100644 index 0000000..d4e0bf7 --- /dev/null +++ b/src/botlib/be_aas_reach.h @@ -0,0 +1,80 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_route.c b/src/botlib/be_aas_route.c new file mode 100644 index 0000000..ea14864 --- /dev/null +++ b/src/botlib/be_aas_route.c @@ -0,0 +1,3704 @@ +/* +=========================================================================== + +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: be_aas_route.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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: - +//=========================================================================== +__inline int AAS_ClusterAreaNum( int cluster, int areanum ) { + int side, areacluster; + + areacluster = aasworld->areasettings[areanum].cluster; + if ( areacluster > 0 ) { + return aasworld->areasettings[areanum].clusterareanum; + } else + { +/*#ifdef ROUTING_DEBUG + if (aasworld->portals[-areacluster].frontcluster != cluster && + aasworld->portals[-areacluster].backcluster != cluster) + { + botimport.Print(PRT_ERROR, "portal %d: does not belong to cluster %d\n" + , -areacluster, cluster); + } //end if +#endif //ROUTING_DEBUG*/ + side = aasworld->portals[-areacluster].frontcluster != cluster; + return aasworld->portals[-areacluster].clusterareanum[side]; + } //end else +} //end of the function AAS_ClusterAreaNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitTravelFlagFromType( void ) { + int i; + + for ( i = 0; i < MAX_TRAVELTYPES; i++ ) + { + aasworld->travelflagfortype[i] = TFL_INVALID; + } //end for + aasworld->travelflagfortype[TRAVEL_INVALID] = TFL_INVALID; + aasworld->travelflagfortype[TRAVEL_WALK] = TFL_WALK; + aasworld->travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH; + aasworld->travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP; + aasworld->travelflagfortype[TRAVEL_JUMP] = TFL_JUMP; + aasworld->travelflagfortype[TRAVEL_LADDER] = TFL_LADDER; + aasworld->travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE; + aasworld->travelflagfortype[TRAVEL_SWIM] = TFL_SWIM; + aasworld->travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP; + aasworld->travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT; + aasworld->travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR; + aasworld->travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP; + aasworld->travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP; + aasworld->travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK; + aasworld->travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP; + aasworld->travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP; + aasworld->travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP; + aasworld->travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD; + aasworld->travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB; +} //end of the function AAS_InitTravelFlagFromType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +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: - +//=========================================================================== +__inline 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 ); + 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: - +//=========================================================================== +__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 * )( (int)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/botlib/be_aas_route.h b/src/botlib/be_aas_route.h new file mode 100644 index 0000000..eed6959 --- /dev/null +++ b/src/botlib/be_aas_route.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_routealt.c b/src/botlib/be_aas_routealt.c new file mode 100644 index 0000000..41c44e0 --- /dev/null +++ b/src/botlib/be_aas_routealt.c @@ -0,0 +1,294 @@ +/* +=========================================================================== + +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: be_aas_routealt.c + * + * desc: AAS + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_aas_routealt.h b/src/botlib/be_aas_routealt.h new file mode 100644 index 0000000..db5fc2b --- /dev/null +++ b/src/botlib/be_aas_routealt.h @@ -0,0 +1,46 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_routetable.c b/src/botlib/be_aas_routetable.c new file mode 100644 index 0000000..0eef989 --- /dev/null +++ b/src/botlib/be_aas_routetable.c @@ -0,0 +1,1166 @@ +/* +=========================================================================== + +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: be_aas_routetable.c +// Function: Area Awareness System, Route-table defines +// Programmer: Ridah +// Tab Size: 3 +//=========================================================================== + +#include "../game/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 "../game/botlib.h" +#include "../game/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 ); + + 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 ); + + 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 ); + + // 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 "../game/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/botlib/be_aas_routetable.h b/src/botlib/be_aas_routetable.h new file mode 100644 index 0000000..c2bb1c7 --- /dev/null +++ b/src/botlib/be_aas_routetable.h @@ -0,0 +1,171 @@ +/* +=========================================================================== + +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: 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/botlib/be_aas_sample.c b/src/botlib/be_aas_sample.c new file mode 100644 index 0000000..4109602 --- /dev/null +++ b/src/botlib/be_aas_sample.c @@ -0,0 +1,1482 @@ +/* +=========================================================================== + +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: be_aas_sample.c + * + * desc: AAS environment sampling + * + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "aasfile.h" +#include "../game/botlib.h" +#include "../game/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/botlib/be_aas_sample.h b/src/botlib/be_aas_sample.h new file mode 100644 index 0000000..ae7f7fd --- /dev/null +++ b/src/botlib/be_aas_sample.h @@ -0,0 +1,72 @@ +/* +=========================================================================== + +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: 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/botlib/be_ai_char.c b/src/botlib/be_ai_char.c new file mode 100644 index 0000000..97b63d1 --- /dev/null +++ b/src/botlib/be_ai_char.c @@ -0,0 +1,767 @@ +/* +=========================================================================== + +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: be_ai_char.c + * + * desc: bot characters + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/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/botlib/be_ai_chat.c b/src/botlib/be_ai_chat.c new file mode 100644 index 0000000..0ffae26 --- /dev/null +++ b/src/botlib/be_ai_chat.c @@ -0,0 +1,2890 @@ +/* +=========================================================================== + +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: be_ai_chat.c + * + * desc: bot chat AI + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/be_ea.h" +#include "../game/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++ ) + { + // + 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 + // + context = 0; + contextlevel = 0; + synlist = NULL; //list synonyms + lastsyn = NULL; //last synonym in the list + // + while ( PC_ReadToken( source, &token ) ) + { + if ( token.type == TT_NUMBER ) { + context |= token.intvalue; + contextstack[contextlevel] = token.intvalue; + contextlevel++; + if ( contextlevel >= 32 ) { + SourceError( source, "more than 32 context levels" ); + FreeSource( source ); + return NULL; + } //end if + if ( !PC_ExpectTokenString( source, "{" ) ) { + FreeSource( source ); + return NULL; + } //end if + } //end if + else if ( token.type == TT_PUNCTUATION ) { + if ( !strcmp( token.string, "}" ) ) { + contextlevel--; + if ( contextlevel < 0 ) { + SourceError( source, "too many }" ); + FreeSource( source ); + return NULL; + } //end if + context &= ~contextstack[contextlevel]; + } //end if + else if ( !strcmp( token.string, "[" ) ) { + size += sizeof( bot_synonymlist_t ); + if ( pass ) { + syn = (bot_synonymlist_t *) ptr; + ptr += sizeof( bot_synonymlist_t ); + syn->context = context; + syn->firstsynonym = NULL; + syn->next = NULL; + if ( lastsyn ) { + lastsyn->next = syn; + } else { synlist = syn;} + lastsyn = syn; + } //end if + numsynonyms = 0; + lastsynonym = NULL; + while ( 1 ) + { + 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 < 0 || token.intvalue >= MAX_MATCHVARIABLES ) { + SourceError( source, "can't have more than %d match variables\n", MAX_MATCHVARIABLES ); + FreeSource( source ); + BotFreeMatchPieces( firstpiece ); + return NULL; + } //end if + if ( lastwasvariable ) { + SourceError( source, "not allowed to have adjacent variables\n" ); + FreeSource( source ); + BotFreeMatchPieces( firstpiece ); + return NULL; + } //end if + lastwasvariable = qtrue; + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory( sizeof( bot_matchpiece_t ) ); + matchpiece->type = MT_VARIABLE; + matchpiece->variable = token.intvalue; + matchpiece->next = NULL; + if ( lastpiece ) { + lastpiece->next = matchpiece; + } else { firstpiece = matchpiece;} + lastpiece = matchpiece; + } //end if + else if ( token.type == TT_STRING ) { + // + matchpiece = (bot_matchpiece_t *) GetClearedHunkMemory( sizeof( bot_matchpiece_t ) ); + matchpiece->firststring = NULL; + matchpiece->type = MT_STRING; + matchpiece->variable = 0; + matchpiece->next = NULL; + if ( lastpiece ) { + lastpiece->next = matchpiece; + } else { firstpiece = matchpiece;} + lastpiece = matchpiece; + // + lastmatchstring = NULL; + emptystring = qfalse; + // + do + { + if ( matchpiece->firststring ) { + if ( !PC_ExpectTokenType( source, TT_STRING, 0, &token ) ) { + FreeSource( source ); + BotFreeMatchPieces( firstpiece ); + return NULL; + } //end if + } //end if + StripDoubleQuotes( token.string ); + matchstring = (bot_matchstring_t *) GetClearedHunkMemory( sizeof( bot_matchstring_t ) + strlen( token.string ) + 1 ); + matchstring->string = (char *) matchstring + sizeof( bot_matchstring_t ); + strcpy( matchstring->string, token.string ); + if ( !strlen( token.string ) ) { + emptystring = qtrue; + } + matchstring->next = NULL; + if ( lastmatchstring ) { + lastmatchstring->next = matchstring; + } else { matchpiece->firststring = matchstring;} + lastmatchstring = matchstring; + } while ( PC_CheckTokenString( source, "|" ) ); + //if there was no empty string found + if ( !emptystring ) { + lastwasvariable = qfalse; + } + } //end if + else + { + SourceError( source, "invalid token %s\n", token.string ); + FreeSource( source ); + BotFreeMatchPieces( firstpiece ); + return NULL; + } //end else + if ( PC_CheckTokenString( source, endtoken ) ) { + break; + } + if ( !PC_ExpectTokenString( source, "," ) ) { + FreeSource( source ); + BotFreeMatchPieces( firstpiece ); + return NULL; + } //end if + } //end while + return firstpiece; +} //end of the function BotLoadMatchPieces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotFreeMatchTemplates( bot_matchtemplate_t *mt ) { + bot_matchtemplate_t *nextmt; + + for (; mt; mt = nextmt ) + { + nextmt = mt->next; + BotFreeMatchPieces( mt->first ); + FreeMemory( mt ); + } //end for +} //end of the function BotFreeMatchTemplates +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bot_matchtemplate_t *BotLoadMatchTemplates( char *matchfile ) { + source_t *source; + token_t token; + bot_matchtemplate_t *matchtemplate, *matches, *lastmatch; + unsigned long int context; + + 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/botlib/be_ai_gen.c b/src/botlib/be_ai_gen.c new file mode 100644 index 0000000..d56fdd5 --- /dev/null +++ b/src/botlib/be_ai_gen.c @@ -0,0 +1,151 @@ +/* +=========================================================================== + +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: be_ai_gen.c + * + * desc: genetic selection + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "../game/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/botlib/be_ai_goal.c b/src/botlib/be_ai_goal.c new file mode 100644 index 0000000..00bdf94 --- /dev/null +++ b/src/botlib/be_ai_goal.c @@ -0,0 +1,1634 @@ +/* +=========================================================================== + +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: be_ai_goal.c + * + * desc: goal AI + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" +#include "../game/be_ai_goal.h" +#include "../game/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 ) (int)&( ( (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/botlib/be_ai_move.c b/src/botlib/be_ai_move.c new file mode 100644 index 0000000..c92678f --- /dev/null +++ b/src/botlib/be_ai_move.c @@ -0,0 +1,3962 @@ +/* +=========================================================================== + +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: be_ai_move.c + * + * desc: bot movement AI + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" + +#include "../game/be_ea.h" +#include "../game/be_ai_goal.h" +#include "../game/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: - +//=========================================================================== +//__inline int AAS_AreaContentsTravelFlag(int areanum); + +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/botlib/be_ai_weap.c b/src/botlib/be_ai_weap.c new file mode 100644 index 0000000..0c5511a --- /dev/null +++ b/src/botlib/be_ai_weap.c @@ -0,0 +1,555 @@ +/* +=========================================================================== + +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: be_ai_weap.c + * + * desc: weapon AI + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_interface.h" +#include "be_ai_weight.h" //fuzzy weights +#include "../game/be_ai_weap.h" + +//#define DEBUG_AI_WEAP + +//structure field offsets +#define WEAPON_OFS( x ) (int)&( ( (weaponinfo_t *)0 )->x ) +#define PROJECTILE_OFS( x ) (int)&( ( (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/botlib/be_ai_weight.c b/src/botlib/be_ai_weight.c new file mode 100644 index 0000000..dabd96f --- /dev/null +++ b/src/botlib/be_ai_weight.c @@ -0,0 +1,972 @@ +/* +=========================================================================== + +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: be_ai_weight.c + * + * desc: fuzzy logic + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/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/botlib/be_ai_weight.h b/src/botlib/be_ai_weight.h new file mode 100644 index 0000000..3d9559c --- /dev/null +++ b/src/botlib/be_ai_weight.h @@ -0,0 +1,89 @@ +/* +=========================================================================== + +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: 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/botlib/be_ea.c b/src/botlib/be_ea.c new file mode 100644 index 0000000..1a735d4 --- /dev/null +++ b/src/botlib/be_ea.c @@ -0,0 +1,493 @@ +/* +=========================================================================== + +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: be_ea.c + * + * desc: elementary actions + * + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_struct.h" +#include "../game/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/botlib/be_interface.c b/src/botlib/be_interface.c new file mode 100644 index 0000000..5a7a3d6 --- /dev/null +++ b/src/botlib/be_interface.c @@ -0,0 +1,878 @@ +/* +=========================================================================== + +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: be_interface.c + * + * desc: bot library interface + * + * + *****************************************************************************/ + +#include "../game/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 "../game/botlib.h" +#include "../game/be_aas.h" +#include "be_aas_funcs.h" +#include "be_aas_def.h" +#include "be_interface.h" + +#include "../game/be_ea.h" +#include "be_ai_weight.h" +#include "../game/be_ai_goal.h" +#include "../game/be_ai_move.h" +#include "../game/be_ai_weap.h" +#include "../game/be_ai_chat.h" +#include "../game/be_ai_char.h" +#include "../game/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 +#ifdef _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.) + Swap_Init(); + 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 ) { + botimport = *import; + + 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; +} diff --git a/src/botlib/be_interface.h b/src/botlib/be_interface.h new file mode 100644 index 0000000..053293b --- /dev/null +++ b/src/botlib/be_interface.h @@ -0,0 +1,94 @@ +/* +=========================================================================== + +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: 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/botlib/botlib.vcproj b/src/botlib/botlib.vcproj new file mode 100644 index 0000000..e3b96e2 --- /dev/null +++ b/src/botlib/botlib.vcproj @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/botlib/botlib_stub.c b/src/botlib/botlib_stub.c new file mode 100644 index 0000000..2023ab5 --- /dev/null +++ b/src/botlib/botlib_stub.c @@ -0,0 +1,271 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +/* + * $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/botlib/l_common.h b/src/botlib/l_common.h new file mode 100644 index 0000000..c9806a3 --- /dev/null +++ b/src/botlib/l_common.h @@ -0,0 +1,34 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#ifndef _L_COMMON_ +#define _L_COMMON_ + +#error CVS REMOVE ME + +#endif diff --git a/src/botlib/l_crc.c b/src/botlib/l_crc.c new file mode 100644 index 0000000..00dd9fc --- /dev/null +++ b/src/botlib/l_crc.c @@ -0,0 +1,155 @@ +/* +=========================================================================== + +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_crc.c + * + * desc: CRC calculation + * + * + *****************************************************************************/ + +#include +#include +#include + +#include "../game/q_shared.h" +#include "../game/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/botlib/l_crc.h b/src/botlib/l_crc.h new file mode 100644 index 0000000..29ed81c --- /dev/null +++ b/src/botlib/l_crc.h @@ -0,0 +1,44 @@ +/* +=========================================================================== + +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_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/botlib/l_libvar.c b/src/botlib/l_libvar.c new file mode 100644 index 0000000..9149505 --- /dev/null +++ b/src/botlib/l_libvar.c @@ -0,0 +1,282 @@ +/* +=========================================================================== + +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 "../game/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/botlib/l_libvar.h b/src/botlib/l_libvar.h new file mode 100644 index 0000000..72f1f3f --- /dev/null +++ b/src/botlib/l_libvar.h @@ -0,0 +1,69 @@ +/* +=========================================================================== + +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.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/botlib/l_log.c b/src/botlib/l_log.c new file mode 100644 index 0000000..e1ec64c --- /dev/null +++ b/src/botlib/l_log.c @@ -0,0 +1,186 @@ +/* +=========================================================================== + +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_log.c + * + * desc: log file + * + * + *****************************************************************************/ + +#include +#include +#include + +#include "../game/q_shared.h" +#include "../game/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/botlib/l_log.h b/src/botlib/l_log.h new file mode 100644 index 0000000..5122569 --- /dev/null +++ b/src/botlib/l_log.h @@ -0,0 +1,54 @@ +/* +=========================================================================== + +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_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/botlib/l_memory.c b/src/botlib/l_memory.c new file mode 100644 index 0000000..8b2bf6f --- /dev/null +++ b/src/botlib/l_memory.c @@ -0,0 +1,446 @@ +/* +=========================================================================== + +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_memory.c + * + * desc: memory allocation + * + * + *****************************************************************************/ + +#include "../game/q_shared.h" +#include "../game/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/botlib/l_memory.h b/src/botlib/l_memory.h new file mode 100644 index 0000000..be708b7 --- /dev/null +++ b/src/botlib/l_memory.h @@ -0,0 +1,82 @@ +/* +=========================================================================== + +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_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/botlib/l_precomp.c b/src/botlib/l_precomp.c new file mode 100644 index 0000000..ff5410b --- /dev/null +++ b/src/botlib/l_precomp.c @@ -0,0 +1,3262 @@ +/* +=========================================================================== + +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_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 "../game/q_shared.h" +#include "../game/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 ) { + int register hash, i; + + hash = 0; + for ( i = 0; name[i] != '\0'; i++ ) + { + hash += name[i] * ( 119 + i ); + //hash += (name[i] << 7) + i; + //hash += (name[i] << (i&15)); + } //end while + hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) ) & ( DEFINEHASHSIZE - 1 ); + return hash; +} //end of the function PC_NameHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddDefineToHash( define_t *define, define_t **definehash ) { + int hash; + + hash = PC_NameHash( define->name ); + define->hashnext = definehash[hash]; + definehash[hash] = define; +} //end of the function PC_AddDefineToHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindHashedDefine( define_t **definehash, char *name ) { + define_t *d; + int hash; + + hash = PC_NameHash( name ); + for ( d = definehash[hash]; d; d = d->hashnext ) + { + if ( !strcmp( d->name, name ) ) { + return d; + } + } //end for + return NULL; +} //end of the function PC_FindHashedDefine +#endif //DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindDefine( define_t *defines, char *name ) { + define_t *d; + + for ( d = defines; d; d = d->next ) + { + if ( !strcmp( d->name, name ) ) { + return d; + } + } //end for + return NULL; +} //end of the function PC_FindDefine +//============================================================================ +// +// Parameter: - +// Returns: number of the parm +// if no parm found with the given name -1 is returned +// Changes Globals: - +//============================================================================ +int PC_FindDefineParm( define_t *define, char *name ) { + token_t *p; + int i; + + i = 0; + for ( p = define->parms; p; p = p->next ) + { + if ( !strcmp( p->string, name ) ) { + return i; + } + i++; + } //end for + return -1; +} //end of the function PC_FindDefineParm +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeDefine( define_t *define ) { + token_t *t, *next; + + //free the define parameters + for ( t = define->parms; t; t = next ) + { + next = t->next; + PC_FreeToken( t ); + } //end for + //free the define tokens + for ( t = define->tokens; t; t = next ) + { + next = t->next; + PC_FreeToken( t ); + } //end for + //free the define + FreeMemory( define ); +} //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; + unsigned long t; // time_t t; //to prevent LCC warning + 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/botlib/l_precomp.h b/src/botlib/l_precomp.h new file mode 100644 index 0000000..aac7e9e --- /dev/null +++ b/src/botlib/l_precomp.h @@ -0,0 +1,167 @@ +/* +=========================================================================== + +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_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/botlib/l_script.c b/src/botlib/l_script.c new file mode 100644 index 0000000..7c931f9 --- /dev/null +++ b/src/botlib/l_script.c @@ -0,0 +1,1444 @@ +/* +=========================================================================== + +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_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 "../game/q_shared.h" +#include "../game/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 + Com_sprintf( basefolder, sizeof( basefolder ), path ); +#endif +} //end of the function PS_SetBaseFolder diff --git a/src/botlib/l_script.h b/src/botlib/l_script.h new file mode 100644 index 0000000..8a374bb --- /dev/null +++ b/src/botlib/l_script.h @@ -0,0 +1,268 @@ +/* +=========================================================================== + +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_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/botlib/l_struct.c b/src/botlib/l_struct.c new file mode 100644 index 0000000..0dcb7b4 --- /dev/null +++ b/src/botlib/l_struct.c @@ -0,0 +1,507 @@ +/* +=========================================================================== + +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_struct.c + * + * desc: structure reading / writing + * + * + *****************************************************************************/ + +#ifdef BOTLIB +#include "../game/q_shared.h" +#include "../game/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/botlib/l_struct.h b/src/botlib/l_struct.h new file mode 100644 index 0000000..8516e7f --- /dev/null +++ b/src/botlib/l_struct.h @@ -0,0 +1,81 @@ +/* +=========================================================================== + +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_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/botlib/l_utils.h b/src/botlib/l_utils.h new file mode 100644 index 0000000..371e04d --- /dev/null +++ b/src/botlib/l_utils.h @@ -0,0 +1,43 @@ +/* +=========================================================================== + +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_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/bspc/_files.c b/src/bspc/_files.c new file mode 100644 index 0000000..1878044 --- /dev/null +++ b/src/bspc/_files.c @@ -0,0 +1,91 @@ +/* +=========================================================================== + +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: _files.c +// Function: +// Programmer: Mr Elusive +// Last update: 1999-12-02 +// Tab Size: 4 +//=========================================================================== + +/* + +aas_areamerging.c //AAS area merging +aas_cfg.c //AAS configuration for different games +aas_create.c //AAS creating +aas_edgemelting.c //AAS edge melting +aas_facemerging.c //AAS face merging +aas_file.c //AAS file writing +aas_gsubdiv.c //AAS gravitational and ladder subdivision +aas_map.c //AAS map brush creation +aas_prunenodes.c //AAS node pruning +aas_store.c //AAS file storing + +map.c //map file loading and writing +map_hl.c //Half-Life map loading +map_q1.c //Quake1 map loading +map_q2.c //Quake2 map loading +map_q3.c //Quake3 map loading +map_sin.c //Sin map loading +tree.c //BSP tree management + node pruning (*) +brushbsp.c //brush bsp creation (*) +portals.c //BSP portal creation and leaf filling (*) +csg.c //Constructive Solid Geometry brush chopping (*) +leakfile.c //leak file writing (*) +textures.c //Quake2 BSP textures (*) + +l_bsp_ent.c //BSP entity parsing +l_bsp_hl.c //Half-Life BSP loading and writing +l_bsp_q1.c //Quake1 BSP loading and writing +l_bsp_q2.c //Quake2 BSP loading and writing +l_bsp_q3.c //Quake2 BSP loading and writing +l_bsp_sin.c //Sin BSP loading and writing +l_cmd.c //cmd library +l_log.c //log file library +l_math.c //math library +l_mem.c //memory management library +l_poly.c //polygon (winding) library +l_script.c //script file parsing library +l_threads.c //multi-threading library +l_utils.c //utility library +l_qfiles.c //loading of quake files + +gldraw.c //GL drawing (*) +glfile.c //GL file writing (*) +nodraw.c //no draw module (*) + +bspc.c //BSPC Win32 console version +winbspc.c //WinBSPC Win32 GUI version +win32_terminal.c //Win32 terminal output +win32_qfiles.c //Win32 game file management (also .pak .sin) +win32_font.c //Win32 fonts +win32_folder.c //Win32 folder dialogs + +*/ diff --git a/src/bspc/aas_areamerging.c b/src/bspc/aas_areamerging.c new file mode 100644 index 0000000..4956d08 --- /dev/null +++ b/src/bspc/aas_areamerging.c @@ -0,0 +1,447 @@ +/* +=========================================================================== + +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: aas_areamerging.c +// Function: Merging of Areas +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "../botlib/aasfile.h" +#include "aas_create.h" +#include "aas_store.h" + +#define CONVEX_EPSILON 0.3 + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_RefreshMergedTree_r( tmp_node_t *tmpnode ) { + tmp_area_t *tmparea; + + //if this is a solid leaf + if ( !tmpnode ) { + return NULL; + } + //if this is an area leaf + if ( tmpnode->tmparea ) { + tmparea = tmpnode->tmparea; + while ( tmparea->mergedarea ) tmparea = tmparea->mergedarea; + tmpnode->tmparea = tmparea; + return tmpnode; + } //end if + //do the children recursively + tmpnode->children[0] = AAS_RefreshMergedTree_r( tmpnode->children[0] ); + tmpnode->children[1] = AAS_RefreshMergedTree_r( tmpnode->children[1] ); + return tmpnode; +} //end of the function AAS_RefreshMergedTree_r +//=========================================================================== +// returns true if the two given faces would create a non-convex area at +// the given sides, otherwise false is returned +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int NonConvex( tmp_face_t *face1, tmp_face_t *face2, int side1, int side2 ) { + int i; + winding_t *w1, *w2; + plane_t *plane1, *plane2; + + w1 = face1->winding; + w2 = face2->winding; + + plane1 = &mapplanes[face1->planenum ^ side1]; + plane2 = &mapplanes[face2->planenum ^ side2]; + + //check if one of the points of face1 is at the back of the plane of face2 + for ( i = 0; i < w1->numpoints; i++ ) + { + if ( DotProduct( plane2->normal, w1->p[i] ) - plane2->dist < -CONVEX_EPSILON ) { + return true; + } + } //end for + //check if one of the points of face2 is at the back of the plane of face1 + for ( i = 0; i < w2->numpoints; i++ ) + { + if ( DotProduct( plane1->normal, w2->p[i] ) - plane1->dist < -CONVEX_EPSILON ) { + return true; + } + } //end for + + return false; +} //end of the function NonConvex +//=========================================================================== +// try to merge the areas at both sides of the given face +// +// Parameter: seperatingface : face that seperates two areas +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TryMergeFaceAreas( tmp_face_t *seperatingface ) { + int side1, side2, area1faceflags, area2faceflags; + tmp_area_t *tmparea1, *tmparea2, *newarea; + tmp_face_t *face1, *face2, *nextface1, *nextface2; + + tmparea1 = seperatingface->frontarea; + tmparea2 = seperatingface->backarea; + + //areas must have the same presence type + if ( tmparea1->presencetype != tmparea2->presencetype ) { + return false; + } + //areas must have the same area contents + if ( tmparea1->contents != tmparea2->contents ) { + return false; + } + //areas must have the same bsp model inside (or both none) + if ( tmparea1->modelnum != tmparea2->modelnum ) { + return false; + } + + area1faceflags = 0; + area2faceflags = 0; + for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = ( face1->frontarea != tmparea1 ); + //debug: check if the area belongs to the area + if ( face1->frontarea != tmparea1 && + face1->backarea != tmparea1 ) { + Error( "face does not belong to area1" ); + } + //just continue if the face is seperating the two areas + //NOTE: a result of this is that ground and gap areas can + // be merged if the seperating face is the gap + if ( ( face1->frontarea == tmparea1 && + face1->backarea == tmparea2 ) || + ( face1->frontarea == tmparea2 && + face1->backarea == tmparea1 ) ) { + continue; + } + //get area1 face flags + area1faceflags |= face1->faceflags; + if ( AAS_GapFace( face1, side1 ) ) { + area1faceflags |= FACE_GAP; + } + // + for ( face2 = tmparea2->tmpfaces; face2; face2 = face2->next[side2] ) + { + side2 = ( face2->frontarea != tmparea2 ); + //debug: check if the area belongs to the area + if ( face2->frontarea != tmparea2 && + face2->backarea != tmparea2 ) { + Error( "face does not belong to area2" ); + } + //just continue if the face is seperating the two areas + //NOTE: a result of this is that ground and gap areas can + // be merged if the seperating face is the gap + if ( ( face2->frontarea == tmparea1 && + face2->backarea == tmparea2 ) || + ( face2->frontarea == tmparea2 && + face2->backarea == tmparea1 ) ) { + continue; + } + //get area2 face flags + area2faceflags |= face2->faceflags; + if ( AAS_GapFace( face2, side2 ) ) { + area2faceflags |= FACE_GAP; + } + //if the two faces would create a non-convex area + if ( NonConvex( face1, face2, side1, side2 ) ) { + return false; + } + } //end for + } //end for + //if one area has gap faces (that aren't seperating the two areas) + //and the other has ground faces (that aren't seperating the two areas), + //the areas can't be merged + if ( ( ( area1faceflags & FACE_GROUND ) && ( area2faceflags & FACE_GAP ) ) || + ( ( area2faceflags & FACE_GROUND ) && ( area1faceflags & FACE_GAP ) ) ) { +// Log_Print(" can't merge: ground/gap\n"); + return false; + } //end if + +// Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum, numfaces); +// return false; + // + //AAS_CheckArea(tmparea1); + //AAS_CheckArea(tmparea2); + //create the new area + newarea = AAS_AllocTmpArea(); + newarea->presencetype = tmparea1->presencetype; + newarea->contents = tmparea1->contents; + newarea->modelnum = tmparea1->modelnum; + newarea->tmpfaces = NULL; + + //add all the faces (except the seperating ones) from the first area + //to the new area + for ( face1 = tmparea1->tmpfaces; face1; face1 = nextface1 ) + { + side1 = ( face1->frontarea != tmparea1 ); + nextface1 = face1->next[side1]; + //don't add seperating faces + if ( ( face1->frontarea == tmparea1 && + face1->backarea == tmparea2 ) || + ( face1->frontarea == tmparea2 && + face1->backarea == tmparea1 ) ) { + continue; + } //end if + // + AAS_RemoveFaceFromArea( face1, tmparea1 ); + AAS_AddFaceSideToArea( face1, side1, newarea ); + } //end for + //add all the faces (except the seperating ones) from the second area + //to the new area + for ( face2 = tmparea2->tmpfaces; face2; face2 = nextface2 ) + { + side2 = ( face2->frontarea != tmparea2 ); + nextface2 = face2->next[side2]; + //don't add seperating faces + if ( ( face2->frontarea == tmparea1 && + face2->backarea == tmparea2 ) || + ( face2->frontarea == tmparea2 && + face2->backarea == tmparea1 ) ) { + continue; + } //end if + // + AAS_RemoveFaceFromArea( face2, tmparea2 ); + AAS_AddFaceSideToArea( face2, side2, newarea ); + } //end for + //free all shared faces + for ( face1 = tmparea1->tmpfaces; face1; face1 = nextface1 ) + { + side1 = ( face1->frontarea != tmparea1 ); + nextface1 = face1->next[side1]; + // + AAS_RemoveFaceFromArea( face1, face1->frontarea ); + AAS_RemoveFaceFromArea( face1, face1->backarea ); + AAS_FreeTmpFace( face1 ); + } //end for + // + tmparea1->mergedarea = newarea; + tmparea1->invalid = true; + tmparea2->mergedarea = newarea; + tmparea2->invalid = true; + // + AAS_CheckArea( newarea ); + AAS_FlipAreaFaces( newarea ); +// Log_Print("merged area %d & %d to %d with %d faces\n", tmparea1->areanum, tmparea2->areanum, newarea->areanum); + return true; +} //end of the function AAS_TryMergeFaceAreas +//=========================================================================== +// try to merge areas +// merged areas are added to the end of the convex area list so merging +// will be tried for those areas as well +// +// Parameter: - +// Returns: - +// Changes Globals: tmpaasworld +//=========================================================================== +/* +void AAS_MergeAreas(void) +{ + int side, nummerges; + tmp_area_t *tmparea, *othertmparea; + tmp_face_t *face; + + nummerges = 0; + Log_Write("AAS_MergeAreas\r\n"); + qprintf("%6d areas merged", 1); + //first merge grounded areas only + //NOTE: this is useless because the area settings aren't available yet + for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) + { +// Log_Print("checking area %d\n", i); + //if the area is invalid + if (tmparea->invalid) + { +// Log_Print(" area invalid\n"); + continue; + } //end if + // +// if (!(tmparea->settings->areaflags & AREA_GROUNDED)) continue; + // + for (face = tmparea->tmpfaces; face; face = face->next[side]) + { + side = (face->frontarea != tmparea); + //if the face has both a front and back area + if (face->frontarea && face->backarea) + { + // + if (face->frontarea == tmparea) othertmparea = face->backarea; + else othertmparea = face->frontarea; +// if (!(othertmparea->settings->areaflags & AREA_GROUNDED)) continue; +// Log_Print(" checking area %d with %d\n", face->frontarea, face->backarea); + if (AAS_TryMergeFaceAreas(face)) + { + qprintf("\r%6d", ++nummerges); + break; + } //end if + } //end if + } //end for + } //end for + //merge all areas + for (tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next) + { +// Log_Print("checking area %d\n", i); + //if the area is invalid + if (tmparea->invalid) + { +// Log_Print(" area invalid\n"); + continue; + } //end if + // + for (face = tmparea->tmpfaces; face; face = face->next[side]) + { + side = (face->frontarea != tmparea); + //if the face has both a front and back area + if (face->frontarea && face->backarea) + { +// Log_Print(" checking area %d with %d\n", face->frontarea, face->backarea); + if (AAS_TryMergeFaceAreas(face)) + { + qprintf("\r%6d", ++nummerges); + break; + } //end if + } //end if + } //end for + } //end for + Log_Print("\r%6d areas merged\n", nummerges); + //refresh the merged tree + AAS_RefreshMergedTree_r(tmpaasworld.nodes); +} //end of the function AAS_MergeAreas*/ + +int AAS_GroundArea( tmp_area_t *tmparea ) { + tmp_face_t *face; + int side; + + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = ( face->frontarea != tmparea ); + if ( face->faceflags & FACE_GROUND ) { + return true; + } + } //end for + return false; +} //end of the function AAS_GroundArea + +int AAS_LadderArea( tmp_area_t *tmparea ) { + tmp_face_t *face; + int side; + + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = ( face->frontarea != tmparea ); + if ( face->faceflags & FACE_LADDER ) { + return true; + } + } //end for + return false; +} //end of the function AAS_GroundArea + +void AAS_MergeAreas( void ) { + int side, nummerges, merges, groundfirst; + tmp_area_t *tmparea, *othertmparea; + tmp_face_t *face; + + nummerges = 0; + Log_Write( "AAS_MergeAreas\r\n" ); + qprintf( "%6d areas merged", 1 ); + // + groundfirst = true; + //for (i = 0; i < 4 || merges; i++) + while ( 1 ) + { + //if (i < 2) groundfirst = true; + //else groundfirst = false; + // + merges = 0; + //first merge grounded areas only + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + //if the area is invalid + if ( tmparea->invalid ) { + continue; + } //end if + // + if ( groundfirst ) { + if ( !AAS_GroundArea( tmparea ) ) { + continue; + } + } //end if + // + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = ( face->frontarea != tmparea ); + //if the face has both a front and back area + if ( face->frontarea && face->backarea ) { + // + if ( face->frontarea == tmparea ) { + othertmparea = face->backarea; + } else { othertmparea = face->frontarea;} + // + if ( groundfirst ) { + if ( !AAS_GroundArea( othertmparea ) ) { + continue; + } + } //end if + + // if the critical types are different, dont merge + if ( ( AAS_GroundArea( tmparea ) != AAS_GroundArea( othertmparea ) ) + || ( AAS_LadderArea( tmparea ) != AAS_LadderArea( othertmparea ) ) + || ( ( tmparea->contents & AREACONTENTS_MOVER ) != ( othertmparea->contents & AREACONTENTS_MOVER ) ) + || ( ( tmparea->contents & AREACONTENTS_WATER ) != ( othertmparea->contents & AREACONTENTS_WATER ) ) ) { + continue; + } + + if ( AAS_TryMergeFaceAreas( face ) ) { + qprintf( "\r%6d", ++nummerges ); + merges++; + break; + } //end if + } //end if + } //end for + } //end for + if ( !merges ) { + if ( groundfirst ) { + groundfirst = false; + } else { break;} + } //end if + } //end for + qprintf( "\n" ); + Log_Write( "%6d areas merged\r\n", nummerges ); + //refresh the merged tree + AAS_RefreshMergedTree_r( tmpaasworld.nodes ); +} //end of the function AAS_MergeAreas diff --git a/src/bspc/aas_areamerging.h b/src/bspc/aas_areamerging.h new file mode 100644 index 0000000..d2294d2 --- /dev/null +++ b/src/bspc/aas_areamerging.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: aas_areamerging.h +// Function: Merging of Areas +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + + +void AAS_MergeAreas( void ); + diff --git a/src/bspc/aas_cfg.c b/src/bspc/aas_cfg.c new file mode 100644 index 0000000..e0292b0 --- /dev/null +++ b/src/bspc/aas_cfg.c @@ -0,0 +1,310 @@ +/* +=========================================================================== + +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: cfg.c +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "float.h" +#include "..\botlib\aasfile.h" +#include "aas_store.h" +#include "aas_cfg.h" +#include "../botlib/l_precomp.h" +#include "../botlib/l_struct.h" +#include "../botlib/l_libvar.h" + +/////////////////////////////////// +extern void LibVarSet( char *var_name, char *value ); +/////////////////////////////////// + +//structure field offsets +#define BBOX_OFS( x ) (int)&( ( (aas_bbox_t *)0 )->x ) +#define CFG_OFS( x ) (int)&( ( (cfg_t *)0 )->x ) + +//bounding box definition +fielddef_t bbox_fields[] = +{ + {"presencetype", BBOX_OFS( presencetype ), FT_INT}, + {"flags", BBOX_OFS( flags ), FT_INT}, + {"mins", BBOX_OFS( mins ), FT_FLOAT | FT_ARRAY, 3}, + {"maxs", BBOX_OFS( maxs ), FT_FLOAT | FT_ARRAY, 3}, + {NULL, 0, 0, 0} +}; + +fielddef_t cfg_fields[] = +{ + {"phys_gravitydirection", CFG_OFS( phys_gravitydirection ), FT_FLOAT | FT_ARRAY, 3}, + {"phys_friction", CFG_OFS( phys_friction ), FT_FLOAT}, + {"phys_stopspeed", CFG_OFS( phys_stopspeed ), FT_FLOAT}, + {"phys_gravity", CFG_OFS( phys_gravity ), FT_FLOAT}, + {"phys_waterfriction", CFG_OFS( phys_waterfriction ), FT_FLOAT}, + {"phys_watergravity", CFG_OFS( phys_watergravity ), FT_FLOAT}, + {"phys_maxvelocity", CFG_OFS( phys_maxvelocity ), FT_FLOAT}, + {"phys_maxwalkvelocity", CFG_OFS( phys_maxwalkvelocity ), FT_FLOAT}, + {"phys_maxcrouchvelocity", CFG_OFS( phys_maxcrouchvelocity ), FT_FLOAT}, + {"phys_maxswimvelocity", CFG_OFS( phys_maxswimvelocity ), FT_FLOAT}, + {"phys_walkaccelerate", CFG_OFS( phys_walkaccelerate ), FT_FLOAT}, + {"phys_airaccelerate", CFG_OFS( phys_airaccelerate ), FT_FLOAT}, + {"phys_swimaccelerate", CFG_OFS( phys_swimaccelerate ), FT_FLOAT}, + {"phys_maxstep", CFG_OFS( phys_maxstep ), FT_FLOAT}, + {"phys_maxsteepness", CFG_OFS( phys_maxsteepness ), FT_FLOAT}, + {"phys_maxwaterjump", CFG_OFS( phys_maxwaterjump ), FT_FLOAT}, + {"phys_maxbarrier", CFG_OFS( phys_maxbarrier ), FT_FLOAT}, + {"phys_jumpvel", CFG_OFS( phys_jumpvel ), FT_FLOAT}, + {"phys_falldelta5", CFG_OFS( phys_falldelta5 ), FT_FLOAT}, + {"phys_falldelta10", CFG_OFS( phys_falldelta10 ), FT_FLOAT}, + {"rs_waterjump", CFG_OFS( rs_waterjump ), FT_FLOAT}, + {"rs_teleport", CFG_OFS( rs_teleport ), FT_FLOAT}, + {"rs_barrierjump", CFG_OFS( rs_barrierjump ), FT_FLOAT}, + {"rs_startcrouch", CFG_OFS( rs_startcrouch ), FT_FLOAT}, + {"rs_startgrapple", CFG_OFS( rs_startgrapple ), FT_FLOAT}, + {"rs_startwalkoffledge", CFG_OFS( rs_startwalkoffledge ), FT_FLOAT}, + {"rs_startjump", CFG_OFS( rs_startjump ), FT_FLOAT}, + {"rs_rocketjump", CFG_OFS( rs_rocketjump ), FT_FLOAT}, + {"rs_bfgjump", CFG_OFS( rs_bfgjump ), FT_FLOAT}, + {"rs_jumppad", CFG_OFS( rs_jumppad ), FT_FLOAT}, + {"rs_aircontrolledjumppad", CFG_OFS( rs_aircontrolledjumppad ), FT_FLOAT}, + {"rs_funcbob", CFG_OFS( rs_funcbob ), FT_FLOAT}, + {"rs_startelevator", CFG_OFS( rs_startelevator ), FT_FLOAT}, + {"rs_falldamage5", CFG_OFS( rs_falldamage5 ), FT_FLOAT}, + {"rs_falldamage10", CFG_OFS( rs_falldamage10 ), FT_FLOAT}, + {"rs_maxjumpfallheight", CFG_OFS( rs_maxjumpfallheight ), FT_FLOAT}, + {"rs_allowladders", CFG_OFS( rs_allowladders ), FT_INT}, + {NULL, 0, 0, 0} +}; + +structdef_t bbox_struct = +{ + sizeof( aas_bbox_t ), bbox_fields +}; +structdef_t cfg_struct = +{ + sizeof( cfg_t ), cfg_fields +}; + +//global cfg +cfg_t cfg; + +#if 0 +//=========================================================================== +// the default Q3A configuration +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DefaultCfg( void ) { + int i; + + // default all float values to infinite + for ( i = 0; cfg_fields[i].name; i++ ) + { + if ( ( cfg_fields[i].type & FT_TYPE ) == FT_FLOAT ) { + *( float * )( ( (char*)&cfg ) + cfg_fields[i].offset ) = FLT_MAX; + } + } //end for + // + cfg.numbboxes = 2; + //bbox 0 + cfg.bboxes[0].presencetype = PRESENCE_NORMAL; + cfg.bboxes[0].flags = 0; + cfg.bboxes[0].mins[0] = -18; + cfg.bboxes[0].mins[1] = -18; + cfg.bboxes[0].mins[2] = -24; + cfg.bboxes[0].maxs[0] = 18; + cfg.bboxes[0].maxs[1] = 18; + cfg.bboxes[0].maxs[2] = 48; + //bbox 1 + cfg.bboxes[1].presencetype = PRESENCE_CROUCH; + cfg.bboxes[1].flags = 1; + cfg.bboxes[1].mins[0] = -18; + cfg.bboxes[1].mins[1] = -18; + cfg.bboxes[1].mins[2] = -24; + cfg.bboxes[1].maxs[0] = 18; + cfg.bboxes[1].maxs[1] = 18; + cfg.bboxes[1].maxs[2] = 24; + // + cfg.allpresencetypes = PRESENCE_NORMAL | PRESENCE_CROUCH; + cfg.phys_gravitydirection[0] = 0; + cfg.phys_gravitydirection[1] = 0; + cfg.phys_gravitydirection[2] = -1; + cfg.phys_maxsteepness = 0.7; + +// cfg.phys_maxbarrier = -999;//32; // RIDAH: this is calculated at run-time now, from the gravity and jump velocity settings +} //end of the function DefaultCfg +#else +//=========================================================================== +// the default Q3A configuration +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DefaultCfg( void ) { + int i; + + // default all float values to infinite + for ( i = 0; cfg_fields[i].name; i++ ) + { + if ( ( cfg_fields[i].type & FT_TYPE ) == FT_FLOAT ) { + *( float * )( ( (char*)&cfg ) + cfg_fields[i].offset ) = FLT_MAX; + } + } //end for + // + cfg.numbboxes = 2; + //bbox 0 + cfg.bboxes[0].presencetype = PRESENCE_NORMAL; + cfg.bboxes[0].flags = 0; + cfg.bboxes[0].mins[0] = -15; + cfg.bboxes[0].mins[1] = -15; + cfg.bboxes[0].mins[2] = -24; + cfg.bboxes[0].maxs[0] = 15; + cfg.bboxes[0].maxs[1] = 15; + cfg.bboxes[0].maxs[2] = 32; + //bbox 1 + cfg.bboxes[1].presencetype = PRESENCE_CROUCH; + cfg.bboxes[1].flags = 1; + cfg.bboxes[1].mins[0] = -15; + cfg.bboxes[1].mins[1] = -15; + cfg.bboxes[1].mins[2] = -24; + cfg.bboxes[1].maxs[0] = 15; + cfg.bboxes[1].maxs[1] = 15; + cfg.bboxes[1].maxs[2] = 16; + // + cfg.allpresencetypes = PRESENCE_NORMAL | PRESENCE_CROUCH; + cfg.phys_gravitydirection[0] = 0; + cfg.phys_gravitydirection[1] = 0; + cfg.phys_gravitydirection[2] = -1; + cfg.phys_maxsteepness = 0.7; + +// cfg.phys_maxbarrier = -999;//32; // RIDAH: this is calculated at run-time now, from the gravity and jump velocity settings +} //end of the function DefaultCfg +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char * QDECL va( char *format, ... ) { + va_list argptr; + static char string[2][32000]; // in case va is called by nested functions + static int index = 0; + char *buf; + + buf = string[index & 1]; + index++; + + va_start( argptr, format ); + vsprintf( buf, format,argptr ); + va_end( argptr ); + + return buf; +} //end of the function va +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SetCfgLibVars( void ) { + int i; + float value; + + for ( i = 0; cfg_fields[i].name; i++ ) + { + if ( ( cfg_fields[i].type & FT_TYPE ) == FT_FLOAT ) { + value = *( float * )( ( (char*)&cfg ) + cfg_fields[i].offset ); + if ( value != FLT_MAX ) { + LibVarSet( cfg_fields[i].name, va( "%f", value ) ); + } //end if + } //end if + } //end for +} //end of the function SetCfgLibVars +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int LoadCfgFile( char *filename ) { + source_t *source; + token_t token; + int settingsdefined; + + source = LoadSourceFile( filename ); + if ( !source ) { + Log_Print( "couldn't open cfg file %s\n", filename ); + return false; + } //end if + + settingsdefined = false; + memset( &cfg, 0, sizeof( cfg_t ) ); + + while ( PC_ReadToken( source, &token ) ) + { + if ( !stricmp( token.string, "bbox" ) ) { + if ( cfg.numbboxes >= AAS_MAX_BBOXES ) { + SourceError( source, "too many bounding box volumes defined" ); + } //end if + if ( !ReadStructure( source, &bbox_struct, (char *) &cfg.bboxes[cfg.numbboxes] ) ) { + FreeSource( source ); + return false; + } //end if + cfg.allpresencetypes |= cfg.bboxes[cfg.numbboxes].presencetype; + cfg.numbboxes++; + } //end if + else if ( !stricmp( token.string, "settings" ) ) { + if ( settingsdefined ) { + SourceWarning( source, "settings already defined\n" ); + } //end if + settingsdefined = true; + if ( !ReadStructure( source, &cfg_struct, (char *) &cfg ) ) { + FreeSource( source ); + return false; + } //end if + } //end else if + } //end while + if ( VectorLength( cfg.phys_gravitydirection ) < 0.9 || VectorLength( cfg.phys_gravitydirection ) > 1.1 ) { + SourceError( source, "invalid gravity direction specified" ); + } //end if + if ( cfg.numbboxes <= 0 ) { + SourceError( source, "no bounding volumes specified" ); + } //end if + FreeSource( source ); + SetCfgLibVars(); + Log_Print( "using cfg file %s\n", filename ); + return true; +} //end of the function LoadCfgFile diff --git a/src/bspc/aas_cfg.h b/src/bspc/aas_cfg.h new file mode 100644 index 0000000..88a98f5 --- /dev/null +++ b/src/bspc/aas_cfg.h @@ -0,0 +1,89 @@ +/* +=========================================================================== + +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: cfg.h +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#define BBOXFL_GROUNDED 1 //bounding box only valid when on ground +#define BBOXFL_NOTGROUNDED 2 //bounding box only valid when NOT on ground + +typedef struct cfg_s +{ + int numbboxes; //number of bounding boxes + aas_bbox_t bboxes[AAS_MAX_BBOXES]; //all the bounding boxes + int allpresencetypes; //or of all presence types + // aas settings + vec3_t phys_gravitydirection; + float phys_friction; + float phys_stopspeed; + float phys_gravity; + float phys_waterfriction; + float phys_watergravity; + float phys_maxvelocity; + float phys_maxwalkvelocity; + float phys_maxcrouchvelocity; + float phys_maxswimvelocity; + float phys_walkaccelerate; + float phys_airaccelerate; + float phys_swimaccelerate; + float phys_maxstep; + float phys_maxsteepness; + float phys_maxwaterjump; + float phys_maxbarrier; + float phys_jumpvel; + float phys_falldelta5; + float phys_falldelta10; + float rs_waterjump; + float rs_teleport; + float rs_barrierjump; + float rs_startcrouch; + float rs_startgrapple; + float rs_startwalkoffledge; + float rs_startjump; + float rs_rocketjump; + float rs_bfgjump; + float rs_jumppad; + float rs_aircontrolledjumppad; + float rs_funcbob; + float rs_startelevator; + float rs_falldamage5; + float rs_falldamage10; + float rs_maxjumpfallheight; + qboolean rs_allowladders; +} cfg_t; + +extern cfg_t cfg; + +void DefaultCfg( void ); +int LoadCfgFile( char *filename ); diff --git a/src/bspc/aas_create.c b/src/bspc/aas_create.c new file mode 100644 index 0000000..f87ac7c --- /dev/null +++ b/src/bspc/aas_create.c @@ -0,0 +1,1370 @@ +/* +=========================================================================== + +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: aas_create.c +// Function: Creation of AAS +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_create.h" +#include "aas_store.h" +#include "aas_gsubdiv.h" +#include "aas_facemerging.h" +#include "aas_areamerging.h" +#include "aas_edgemelting.h" +#include "aas_prunenodes.h" +#include "aas_cfg.h" +#include "..\game\surfaceflags.h" + +//#define AW_DEBUG +//#define L_DEBUG + +#define AREAONFACESIDE( face, area ) ( face->frontarea != area ) + +tmp_aas_t tmpaasworld; + +extern int mingroundarea; // areas with ground surface area less than this will be removed + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitTmpAAS( void ) { + //tmp faces + tmpaasworld.numfaces = 0; + tmpaasworld.facenum = 0; + tmpaasworld.faces = NULL; + //tmp convex areas + tmpaasworld.numareas = 0; + tmpaasworld.areanum = 0; + tmpaasworld.areas = NULL; + //tmp nodes + tmpaasworld.numnodes = 0; + tmpaasworld.nodes = NULL; + // + tmpaasworld.nodebuffer = NULL; +} //end of the function AAS_InitTmpAAS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeTmpAAS( void ) { + tmp_face_t *f, *nextf; + tmp_area_t *a, *nexta; + tmp_nodebuf_t *nb, *nextnb; + + //free all the faces + for ( f = tmpaasworld.faces; f; f = nextf ) + { + nextf = f->l_next; + if ( f->winding ) { + FreeWinding( f->winding ); + } + FreeMemory( f ); + } //end if + //free all tmp areas + for ( a = tmpaasworld.areas; a; a = nexta ) + { + nexta = a->l_next; + if ( a->settings ) { + FreeMemory( a->settings ); + } + FreeMemory( a ); + } //end for + //free all the tmp nodes + for ( nb = tmpaasworld.nodebuffer; nb; nb = nextnb ) + { + nextnb = nb->next; + FreeMemory( nb ); + } //end for +} //end of the function AAS_FreeTmpAAS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_face_t *AAS_AllocTmpFace( void ) { + tmp_face_t *tmpface; + + tmpface = (tmp_face_t *) GetClearedMemory( sizeof( tmp_face_t ) ); + tmpface->num = tmpaasworld.facenum++; + tmpface->l_prev = NULL; + tmpface->l_next = tmpaasworld.faces; + if ( tmpaasworld.faces ) { + tmpaasworld.faces->l_prev = tmpface; + } + tmpaasworld.faces = tmpface; + tmpaasworld.numfaces++; + return tmpface; +} //end of the function AAS_AllocTmpFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeTmpFace( tmp_face_t *tmpface ) { + if ( tmpface->l_next ) { + tmpface->l_next->l_prev = tmpface->l_prev; + } + if ( tmpface->l_prev ) { + tmpface->l_prev->l_next = tmpface->l_next; + } else { tmpaasworld.faces = tmpface->l_next;} + //free the winding + if ( tmpface->winding ) { + FreeWinding( tmpface->winding ); + } + //free the face + FreeMemory( tmpface ); + tmpaasworld.numfaces--; +} //end of the function AAS_FreeTmpFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_area_t *AAS_AllocTmpArea( void ) { + tmp_area_t *tmparea; + + tmparea = (tmp_area_t *) GetClearedMemory( sizeof( tmp_area_t ) ); + tmparea->areanum = tmpaasworld.areanum++; + tmparea->l_prev = NULL; + tmparea->l_next = tmpaasworld.areas; + if ( tmpaasworld.areas ) { + tmpaasworld.areas->l_prev = tmparea; + } + tmpaasworld.areas = tmparea; + tmpaasworld.numareas++; + return tmparea; +} //end of the function AAS_AllocTmpArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeTmpArea( tmp_area_t *tmparea ) { + if ( tmparea->l_next ) { + tmparea->l_next->l_prev = tmparea->l_prev; + } + if ( tmparea->l_prev ) { + tmparea->l_prev->l_next = tmparea->l_next; + } else { tmpaasworld.areas = tmparea->l_next;} + if ( tmparea->settings ) { + FreeMemory( tmparea->settings ); + } + FreeMemory( tmparea ); + tmpaasworld.numareas--; +} //end of the function AAS_FreeTmpArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_AllocTmpNode( void ) { + tmp_nodebuf_t *nodebuf; + + if ( !tmpaasworld.nodebuffer || + tmpaasworld.nodebuffer->numnodes >= NODEBUF_SIZE ) { + nodebuf = (tmp_nodebuf_t *) GetClearedMemory( sizeof( tmp_nodebuf_t ) ); + nodebuf->next = tmpaasworld.nodebuffer; + nodebuf->numnodes = 0; + tmpaasworld.nodebuffer = nodebuf; + } //end if + tmpaasworld.numnodes++; + return &tmpaasworld.nodebuffer->nodes[tmpaasworld.nodebuffer->numnodes++]; +} //end of the function AAS_AllocTmpNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeTmpNode( tmp_node_t *tmpnode ) { + tmpaasworld.numnodes--; +} //end of the function AAS_FreeTmpNode +//=========================================================================== +// returns true if the face is a gap from the given side +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GapFace( tmp_face_t *tmpface, int side ) { + vec3_t invgravity; + + //if the face is a solid or ground face it can't be a gap + if ( tmpface->faceflags & ( FACE_GROUND | FACE_SOLID ) ) { + return 0; + } + + VectorCopy( cfg.phys_gravitydirection, invgravity ); + VectorInverse( invgravity ); + + return ( DotProduct( invgravity, mapplanes[tmpface->planenum ^ side].normal ) > cfg.phys_maxsteepness ); +} //end of the function AAS_GapFace +//=========================================================================== +// returns true if the face is a ground face +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_GroundFace( tmp_face_t *tmpface ) { + vec3_t invgravity; + + //must be a solid face + if ( !( tmpface->faceflags & FACE_SOLID ) ) { + return 0; + } + + VectorCopy( cfg.phys_gravitydirection, invgravity ); + VectorInverse( invgravity ); + + return ( DotProduct( invgravity, mapplanes[tmpface->planenum].normal ) > cfg.phys_maxsteepness ); +} //end of the function AAS_GroundFace +//=========================================================================== +// adds the side of a face to an area +// +// side : 0 = front side +// 1 = back side +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddFaceSideToArea( tmp_face_t *tmpface, int side, tmp_area_t *tmparea ) { + int tmpfaceside; + + if ( side ) { + if ( tmpface->backarea ) { + Error( "AAS_AddFaceSideToArea: already a back area\n" ); + } + } //end if + else + { + if ( tmpface->frontarea ) { + Error( "AAS_AddFaceSideToArea: already a front area\n" ); + } + } //end else + + if ( side ) { + tmpface->backarea = tmparea; + } else { tmpface->frontarea = tmparea;} + + if ( tmparea->tmpfaces ) { + tmpfaceside = tmparea->tmpfaces->frontarea != tmparea; + tmparea->tmpfaces->prev[tmpfaceside] = tmpface; + } //end if + tmpface->next[side] = tmparea->tmpfaces; + tmpface->prev[side] = NULL; + tmparea->tmpfaces = tmpface; +} //end of the function AAS_AddFaceSideToArea +//=========================================================================== +// remove (a side of) a face from an area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveFaceFromArea( tmp_face_t *tmpface, tmp_area_t *tmparea ) { + int side, prevside, nextside; + + if ( tmpface->frontarea != tmparea && + tmpface->backarea != tmparea ) { + Error( "AAS_RemoveFaceFromArea: face not part of the area" ); + } //end if + side = tmpface->frontarea != tmparea; + if ( tmpface->prev[side] ) { + prevside = tmpface->prev[side]->frontarea != tmparea; + tmpface->prev[side]->next[prevside] = tmpface->next[side]; + } //end if + else + { + tmparea->tmpfaces = tmpface->next[side]; + } //end else + if ( tmpface->next[side] ) { + nextside = tmpface->next[side]->frontarea != tmparea; + tmpface->next[side]->prev[nextside] = tmpface->prev[side]; + } //end if + //remove the area number from the face depending on the side + if ( side ) { + tmpface->backarea = NULL; + } else { tmpface->frontarea = NULL;} + tmpface->prev[side] = NULL; + tmpface->next[side] = NULL; +} //end of the function AAS_RemoveFaceFromArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CheckArea( tmp_area_t *tmparea ) { + int side; + tmp_face_t *face; + plane_t *plane; + vec3_t wcenter, acenter = {0, 0, 0}; + vec3_t normal; + float n, dist; + + if ( tmparea->invalid ) { + Log_Print( "AAS_CheckArea: invalid area\n" ); + } + for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] ) + { + //side of the face the area is on + side = face->frontarea != tmparea; + WindingCenter( face->winding, wcenter ); + VectorAdd( acenter, wcenter, acenter ); + n++; + } //end for + n = 1 / n; + VectorScale( acenter, n, acenter ); + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + //side of the face the area is on + side = face->frontarea != tmparea; + +#ifdef L_DEBUG + if ( WindingError( face->winding ) ) { + Log_Write( "AAS_CheckArea: area %d face %d: %s\r\n", tmparea->areanum, + face->num, WindingErrorString() ); + } //end if +#endif L_DEBUG + + plane = &mapplanes[face->planenum ^ side]; + + if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) { + Log_Print( "AAS_CheckArea: area %d face %d is flipped\n", tmparea->areanum, face->num ); + Log_Print( "AAS_CheckArea: area %d center is %f %f %f\n", tmparea->areanum, acenter[0], acenter[1], acenter[2] ); + } //end if + //check if the winding plane is the same as the face plane + WindingPlane( face->winding, normal, &dist ); + plane = &mapplanes[face->planenum]; +#ifdef L_DEBUG + if ( fabs( dist - plane->dist ) > 0.4 || + fabs( normal[0] - plane->normal[0] ) > 0.0001 || + fabs( normal[1] - plane->normal[1] ) > 0.0001 || + fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { + Log_Write( "AAS_CheckArea: area %d face %d winding plane unequal to face plane\r\n", + tmparea->areanum, face->num ); + } //end if +#endif L_DEBUG + } //end for +} //end of the function AAS_CheckArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CheckFaceWindingPlane( tmp_face_t *face ) { + float dist, sign1, sign2; + vec3_t normal; + plane_t *plane; + winding_t *w; + + //check if the winding plane is the same as the face plane + WindingPlane( face->winding, normal, &dist ); + plane = &mapplanes[face->planenum]; + // + sign1 = DotProduct( plane->normal, normal ); + // + if ( fabs( dist - plane->dist ) > 0.4 || + fabs( normal[0] - plane->normal[0] ) > 0.0001 || + fabs( normal[1] - plane->normal[1] ) > 0.0001 || + fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { + VectorInverse( normal ); + dist = -dist; + if ( fabs( dist - plane->dist ) > 0.4 || + fabs( normal[0] - plane->normal[0] ) > 0.0001 || + fabs( normal[1] - plane->normal[1] ) > 0.0001 || + fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { + Log_Write( "AAS_CheckFaceWindingPlane: face %d winding plane unequal to face plane\r\n", + face->num ); + // + sign2 = DotProduct( plane->normal, normal ); + if ( ( sign1 < 0 && sign2 > 0 ) || + ( sign1 > 0 && sign2 < 0 ) ) { + Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", + face->num ); + w = face->winding; + face->winding = ReverseWinding( w ); + FreeWinding( w ); + } //end if + } //end if + else + { + Log_Write( "AAS_CheckFaceWindingPlane: face %d winding reversed\r\n", + face->num ); + w = face->winding; + face->winding = ReverseWinding( w ); + FreeWinding( w ); + } //end else + } //end if +} //end of the function AAS_CheckFaceWindingPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CheckAreaWindingPlanes( void ) { + int side; + tmp_area_t *tmparea; + tmp_face_t *face; + + Log_Write( "AAS_CheckAreaWindingPlanes:\r\n" ); + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + if ( tmparea->invalid ) { + continue; + } + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = face->frontarea != tmparea; + AAS_CheckFaceWindingPlane( face ); + } //end for + } //end for +} //end of the function AAS_CheckAreaWindingPlanes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FlipAreaFaces( tmp_area_t *tmparea ) { + int side; + tmp_face_t *face; + plane_t *plane; + vec3_t wcenter, acenter = {0, 0, 0}; + //winding_t *w; + float n; + + for ( n = 0, face = tmparea->tmpfaces; face; face = face->next[side] ) + { + if ( !face->frontarea ) { + Error( "face %d has no front area\n", face->num ); + } + //side of the face the area is on + side = face->frontarea != tmparea; + WindingCenter( face->winding, wcenter ); + VectorAdd( acenter, wcenter, acenter ); + n++; + } //end for + n = 1 / n; + VectorScale( acenter, n, acenter ); + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + //side of the face the area is on + side = face->frontarea != tmparea; + + plane = &mapplanes[face->planenum ^ side]; + + if ( DotProduct( plane->normal, acenter ) - plane->dist < 0 ) { + Log_Print( "area %d face %d flipped: front area %d, back area %d\n", tmparea->areanum, face->num, + face->frontarea ? face->frontarea->areanum : 0, + face->backarea ? face->backarea->areanum : 0 ); + /* + face->planenum = face->planenum ^ 1; + w = face->winding; + face->winding = ReverseWinding(w); + FreeWinding(w); + */ + } //end if +#ifdef L_DEBUG + { + float dist; + vec3_t normal; + + //check if the winding plane is the same as the face plane + WindingPlane( face->winding, normal, &dist ); + plane = &mapplanes[face->planenum]; + if ( fabs( dist - plane->dist ) > 0.4 || + fabs( normal[0] - plane->normal[0] ) > 0.0001 || + fabs( normal[1] - plane->normal[1] ) > 0.0001 || + fabs( normal[2] - plane->normal[2] ) > 0.0001 ) { + Log_Write( "area %d face %d winding plane unequal to face plane\r\n", + tmparea->areanum, face->num ); + } //end if + } +#endif + } //end for +} //end of the function AAS_FlipAreaFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveAreaFaceColinearPoints( void ) { + int side; + tmp_face_t *face; + tmp_area_t *tmparea; + + //FIXME: loop over the faces instead of area->faces + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = face->frontarea != tmparea; + RemoveColinearPoints( face->winding ); +// RemoveEqualPoints(face->winding, 0.1); + } //end for + } //end for +} //end of the function AAS_RemoveAreaFaceColinearPoints +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveTinyFaces( void ) { + int side, num; + tmp_face_t *face, *nextface; + tmp_area_t *tmparea; + + //FIXME: loop over the faces instead of area->faces + Log_Write( "AAS_RemoveTinyFaces\r\n" ); + num = 0; + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + for ( face = tmparea->tmpfaces; face; face = nextface ) + { + side = face->frontarea != tmparea; + nextface = face->next[side]; + // + if ( WindingArea( face->winding ) < 1 ) { + if ( face->frontarea ) { + AAS_RemoveFaceFromArea( face, face->frontarea ); + } + if ( face->backarea ) { + AAS_RemoveFaceFromArea( face, face->backarea ); + } + AAS_FreeTmpFace( face ); + //Log_Write("area %d face %d is tiny\r\n", tmparea->areanum, face->num); + num++; + } //end if + } //end for + } //end for + Log_Write( "%d tiny faces removed\r\n", num ); +} //end of the function AAS_RemoveTinyFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateAreaSettings( void ) { + int i, flags, side, numgrounded, numladderareas, numliquidareas; + tmp_face_t *face; + tmp_area_t *tmparea; + int count; + + numgrounded = 0; + numladderareas = 0; + numliquidareas = 0; + Log_Write( "AAS_CreateAreaSettings\r\n" ); + i = 0; + qprintf( "%6d areas provided with settings", i ); + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + //if the area is invalid there no need to create settings for it + if ( tmparea->invalid ) { + continue; + } + + tmparea->settings = (tmp_areasettings_t *) GetClearedMemory( sizeof( tmp_areasettings_t ) ); + tmparea->settings->contents = tmparea->contents; + tmparea->settings->modelnum = tmparea->modelnum; + flags = 0; + count = 0; + tmparea->settings->groundsteepness = 0.0; + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = face->frontarea != tmparea; + flags |= face->faceflags; + // Ridah, add this face's steepness + if ( face->faceflags & FACE_GROUND ) { + tmparea->settings->groundsteepness += ( 1.0 - mapplanes[face->planenum ^ side].normal[2] ); + count++; + } + } //end for + tmparea->settings->groundsteepness /= (float)count; + if ( tmparea->settings->groundsteepness > 1.0 ) { + tmparea->settings->groundsteepness = 1.0; + } + if ( tmparea->settings->groundsteepness < 0.0 ) { + tmparea->settings->groundsteepness = 0.0; + } + tmparea->settings->areaflags = 0; + if ( flags & FACE_GROUND ) { + tmparea->settings->areaflags |= AREA_GROUNDED; + numgrounded++; + } //end if + if ( flags & FACE_LADDER ) { + tmparea->settings->areaflags |= AREA_LADDER; + numladderareas++; + } //end if + if ( tmparea->contents & ( AREACONTENTS_WATER | + AREACONTENTS_SLIME | + AREACONTENTS_LAVA ) ) { + tmparea->settings->areaflags |= AREA_LIQUID; + numliquidareas++; + } //end if + //presence type of the area + tmparea->settings->presencetype = tmparea->presencetype; + // + qprintf( "\r%6d", ++i ); + } //end for + qprintf( "\n" ); +#ifdef AASINFO + Log_Print( "%6d grounded areas\n", numgrounded ); + Log_Print( "%6d ladder areas\n", numladderareas ); + Log_Print( "%6d liquid areas\n", numliquidareas ); +#endif //AASINFO +} //end of the function AAS_CreateAreaSettings +//=========================================================================== +// create a tmp AAS area from a leaf node +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +//portal_t *globalPortalDebug; + +float VectorDistance( vec3_t v1, vec3_t v2 ); + +int numGroundOnlyFreed; +int numTinyFreed; + +tmp_area_t *garea; + +tmp_node_t *AAS_CreateArea( node_t *node ) { + int pside; + int areafaceflags; + portal_t *p; + tmp_face_t *tmpface; + tmp_area_t *tmparea; + tmp_node_t *tmpnode; + vec3_t up = {0, 0, 1}; +// vec3_t mins, maxs; +// qboolean allowFreeIfSmall = 1; + +// VectorClear( mins ); +// VectorClear( maxs ); + + //create an area from this leaf + tmparea = AAS_AllocTmpArea(); + tmparea->tmpfaces = NULL; + //clear the area face flags + areafaceflags = 0; + //make aas faces from the portals + for ( p = node->portals; p; p = p->next[pside] ) + { + pside = ( p->nodes[1] == node ); + //don't create faces from very small portals +// if (WindingArea(p->winding) < 1) continue; + //if there's already a face created for this portal + if ( p->tmpface ) { + //add the back side of the face to the area + AAS_AddFaceSideToArea( p->tmpface, 1, tmparea ); + } //end if + else + { + tmpface = AAS_AllocTmpFace(); + //set the face pointer at the portal so we can see from + //the portal there's a face created for it + p->tmpface = tmpface; + //FIXME: test this change + //tmpface->planenum = (p->planenum & ~1) | pside; + tmpface->planenum = p->planenum ^ pside; + if ( pside ) { + tmpface->winding = ReverseWinding( p->winding ); + } else { tmpface->winding = CopyWinding( p->winding );} +#ifdef L_DEBUG + // + AAS_CheckFaceWindingPlane( tmpface ); +#endif //L_DEBUG + //if there's solid at the other side of the portal + if ( p->nodes[!pside]->contents & ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + tmpface->faceflags |= FACE_SOLID; + } //end if + //else there is no solid at the other side and if there + //is a liquid at this side + else if ( node->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + tmpface->faceflags |= FACE_LIQUID; + //if there's no liquid at the other side + if ( !( p->nodes[!pside]->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { + tmpface->faceflags |= FACE_LIQUIDSURFACE; + } //end if + } //end else + //if there's ladder contents at other side of the portal + if ( ( p->nodes[pside]->contents & CONTENTS_LADDER ) || + ( p->nodes[!pside]->contents & CONTENTS_LADDER ) ) { + + //NOTE: doesn't have to be solid at the other side because + // when standing one can use a crouch area (which is not solid) + // as a ladder + // imagine a ladder one can walk underthrough, + // under the ladder against the ladder is a crouch area + // the (vertical) sides of this crouch area area also used as + // ladder sides when standing (not crouched) + tmpface->faceflags |= FACE_LADDER; + } //end if + //if it is possible to stand on the face + if ( AAS_GroundFace( tmpface ) ) { + tmpface->faceflags |= FACE_GROUND; + } //end if + // + areafaceflags |= tmpface->faceflags; + //no aas face number yet (zero is a dummy in the aasworld faces) + tmpface->aasfacenum = 0; + //add the front side of the face to the area + AAS_AddFaceSideToArea( tmpface, 0, tmparea ); + } //end else +/* + // RF, add this face to the bounds + if (p->tmpface->faceflags & FACE_GROUND) { + WindingBounds( p->winding, mins, maxs ); + } + + // RF, if this face has a solid at the other side, and it is not a GROUND, then we cannot free if its too small + if (allowFreeIfSmall && (p->tmpface->faceflags & FACE_SOLID) && !(p->tmpface->faceflags & FACE_GROUND)) { + // make sure it's not a ceiling + if (!(DotProduct(cfg.phys_gravitydirection, mapplanes[p->tmpface->planenum].normal) > cfg.phys_maxsteepness)) { + allowFreeIfSmall = 0; + } + } +*/ + } //end for + qprintf( "\r%6d", tmparea->areanum ); + //presence type in the area + tmparea->presencetype = ~node->expansionbboxes & cfg.allpresencetypes; + // + tmparea->contents = 0; + if ( node->contents & CONTENTS_CLUSTERPORTAL ) { + tmparea->contents |= AREACONTENTS_CLUSTERPORTAL; + } + if ( node->contents & CONTENTS_MOVER ) { + tmparea->contents |= AREACONTENTS_MOVER; + } + if ( node->contents & CONTENTS_TELEPORTER ) { + tmparea->contents |= AREACONTENTS_TELEPORTER; + } + if ( node->contents & CONTENTS_JUMPPAD ) { + tmparea->contents |= AREACONTENTS_JUMPPAD; + } + if ( node->contents & CONTENTS_DONOTENTER ) { + tmparea->contents |= AREACONTENTS_DONOTENTER; + } + if ( node->contents & CONTENTS_DONOTENTER_LARGE ) { + tmparea->contents |= AREACONTENTS_DONOTENTER_LARGE; + } + if ( node->contents & CONTENTS_WATER ) { + tmparea->contents |= AREACONTENTS_WATER; + } + if ( node->contents & CONTENTS_LAVA ) { + tmparea->contents |= AREACONTENTS_LAVA; + } + if ( node->contents & CONTENTS_SLIME ) { + tmparea->contents |= AREACONTENTS_SLIME; + } +/* + // RF, if we are using only ground areas, then ignore areas that arent grounded or attached to ladders, or underwater + if (groundonly && !(areafaceflags & (FACE_GROUND|FACE_LADDER)) && !(tmparea->contents & (AREACONTENTS_WATER|AREACONTENTS_SLIME|AREACONTENTS_LAVA))) { + numGroundOnlyFreed++; + tmparea->invalid = true; + } +*/ +/* + // RF, if this is a really small area, and it is surrounded by non-solid portals, then nuke it + if (allowFreeIfSmall && !(areafaceflags & (FACE_LADDER)) && VectorDistance( mins, maxs ) < 32) { + numTinyFreed++; + tmparea->invalid = true; + } +*/ + //store the bsp model that's inside this node + tmparea->modelnum = node->modelnum; + //sorta check for flipped area faces (remove??) + AAS_FlipAreaFaces( tmparea ); + //check if the area is ok (remove??) + AAS_CheckArea( tmparea ); + // + tmpnode = AAS_AllocTmpNode(); + tmpnode->planenum = 0; + tmpnode->children[0] = 0; + tmpnode->children[1] = 0; + tmpnode->tmparea = tmparea; + // + return tmpnode; +} //end of the function AAS_CreateArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_CreateAreas_r( node_t *node ) { + tmp_node_t *tmpnode; + + //recurse down to leafs + if ( node->planenum != PLANENUM_LEAF ) { + //the first tmp node is a dummy + tmpnode = AAS_AllocTmpNode(); + tmpnode->planenum = node->planenum; + tmpnode->children[0] = AAS_CreateAreas_r( node->children[0] ); + tmpnode->children[1] = AAS_CreateAreas_r( node->children[1] ); + return tmpnode; + } //end if + //areas won't be created for solid leafs + if ( node->contents & CONTENTS_SOLID ) { + //just return zero for a solid leaf (in tmp AAS NULL is a solid leaf) + return NULL; + } //end if + + return AAS_CreateArea( node ); +} //end of the function AAS_CreateAreas_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateAreas( node_t *node ) { + Log_Write( "AAS_CreateAreas\r\n" ); + qprintf( "%6d areas created", 0 ); + + numGroundOnlyFreed = 0; + numTinyFreed = 0; + + tmpaasworld.nodes = AAS_CreateAreas_r( node ); + qprintf( "\n" ); + +// Log_Print("%6d non-grounded areas freed\n", numGroundOnlyFreed); +// Log_Write("%6d non-grounded areas freed\n", numGroundOnlyFreed); +// Log_Print("%6d tiny areas freed\n", numTinyFreed); +// Log_Write("%6d tiny areas freed\n", numTinyFreed); + + Log_Write( "%6d areas created\r\n", tmpaasworld.numareas ); +} //end of the function AAS_CreateAreas +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PrintNumGroundFaces( void ) { + tmp_face_t *tmpface; + int numgroundfaces = 0; + + for ( tmpface = tmpaasworld.faces; tmpface; tmpface = tmpface->l_next ) + { + if ( tmpface->faceflags & FACE_GROUND ) { + numgroundfaces++; + } //end if + } //end for + qprintf( "%6d ground faces\n", numgroundfaces ); +} //end of the function AAS_PrintNumGroundFaces +//=========================================================================== +// checks the number of shared faces between the given two areas +// since areas are convex they should only have ONE shared face +// however due to crappy face merging there are sometimes several +// shared faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CheckAreaSharedFaces( tmp_area_t *tmparea1, tmp_area_t *tmparea2 ) { + int numsharedfaces, side; + tmp_face_t *face1, *sharedface; + + if ( tmparea1->invalid || tmparea2->invalid ) { + return; + } + + sharedface = NULL; + numsharedfaces = 0; + for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] ) + { + side = face1->frontarea != tmparea1; + if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) { + sharedface = face1; + numsharedfaces++; + } //end if + } //end if + if ( !sharedface ) { + return; + } + //the areas should only have one shared face + if ( numsharedfaces > 1 ) { + Log_Write( "---- tmp area %d and %d have %d shared faces\r\n", + tmparea1->areanum, tmparea2->areanum, numsharedfaces ); + for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side] ) + { + side = face1->frontarea != tmparea1; + if ( face1->backarea == tmparea2 || face1->frontarea == tmparea2 ) { + Log_Write( "face %d, planenum = %d, face->frontarea = %d face->backarea = %d\r\n", + face1->num, face1->planenum, face1->frontarea->areanum, face1->backarea->areanum ); + } //end if + } //end if + } //end if +} //end of the function AAS_CheckAreaSharedFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CheckSharedFaces( void ) { + tmp_area_t *tmparea1, *tmparea2; + + for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next ) + { + for ( tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next ) + { + if ( tmparea1 == tmparea2 ) { + continue; + } + AAS_CheckAreaSharedFaces( tmparea1, tmparea2 ); + } //end for + } //end for +} //end of the function AAS_CheckSharedFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FlipFace( tmp_face_t *face ) { + tmp_area_t *frontarea, *backarea; + winding_t *w; + + frontarea = face->frontarea; + backarea = face->backarea; + //must have an area at both sides before flipping is allowed + if ( !frontarea || !backarea ) { + return; + } + //flip the face winding + w = face->winding; + face->winding = ReverseWinding( w ); + FreeWinding( w ); + //flip the face plane + face->planenum ^= 1; + //flip the face areas + AAS_RemoveFaceFromArea( face, frontarea ); + AAS_RemoveFaceFromArea( face, backarea ); + AAS_AddFaceSideToArea( face, 1, frontarea ); + AAS_AddFaceSideToArea( face, 0, backarea ); +} //end of the function AAS_FlipFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void AAS_FlipAreaSharedFaces(tmp_area_t *tmparea1, tmp_area_t *tmparea2) +{ + int numsharedfaces, side, area1facing, area2facing; + tmp_face_t *face1, *sharedface; + + if (tmparea1->invalid || tmparea2->invalid) return; + + sharedface = NULL; + numsharedfaces = 0; + area1facing = 0; //number of shared faces facing towards area 1 + area2facing = 0; //number of shared faces facing towards area 2 + for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) + { + side = face1->frontarea != tmparea1; + if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) + { + sharedface = face1; + numsharedfaces++; + if (face1->frontarea == tmparea1) area1facing++; + else area2facing++; + } //end if + } //end if + if (!sharedface) return; + //if there's only one shared face + if (numsharedfaces <= 1) return; + //if all the shared faces are facing to the same area + if (numsharedfaces == area1facing || numsharedfaces == area2facing) return; + // + do + { + for (face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side]) + { + side = face1->frontarea != tmparea1; + if (face1->backarea == tmparea2 || face1->frontarea == tmparea2) + { + if (face1->frontarea != tmparea1) + { + AAS_FlipFace(face1); + break; + } //end if + } //end if + } //end for + } while(face1); +} //end of the function AAS_FlipAreaSharedFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FlipSharedFaces(void) +{ + int i; + tmp_area_t *tmparea1, *tmparea2; + + i = 0; + qprintf("%6d areas checked for shared face flipping", i); + for (tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next) + { + if (tmparea1->invalid) continue; + for (tmparea2 = tmpaasworld.areas; tmparea2; tmparea2 = tmparea2->l_next) + { + if (tmparea2->invalid) continue; + if (tmparea1 == tmparea2) continue; + AAS_FlipAreaSharedFaces(tmparea1, tmparea2); + } //end for + qprintf("\r%6d", ++i); + } //end for + Log_Print("\r%6d areas checked for shared face flipping\n", i); +} //end of the function AAS_FlipSharedFaces +*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FlipSharedFaces( void ) { + int i, side1, side2; + tmp_area_t *tmparea1; + tmp_face_t *face1, *face2; + + i = 0; + qprintf( "%6d areas checked for shared face flipping", i ); + for ( tmparea1 = tmpaasworld.areas; tmparea1; tmparea1 = tmparea1->l_next ) + { + if ( tmparea1->invalid ) { + continue; + } + for ( face1 = tmparea1->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea1; + if ( !face1->frontarea || !face1->backarea ) { + continue; + } + // + for ( face2 = face1->next[side1]; face2; face2 = face2->next[side2] ) + { + side2 = face2->frontarea != tmparea1; + if ( !face2->frontarea || !face2->backarea ) { + continue; + } + // + if ( face1->frontarea == face2->backarea && + face1->backarea == face2->frontarea ) { + AAS_FlipFace( face2 ); + } //end if + //recheck side + side2 = face2->frontarea != tmparea1; + } //end for + } //end for + qprintf( "\r%6d", ++i ); + } //end for + qprintf( "\n" ); + Log_Print( "%6d areas checked for shared face flipping\r\n", i ); +} //end of the function AAS_FlipSharedFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FacesTouching( tmp_face_t *face1, tmp_face_t *face2 ) { + int i; + winding_t *w2; + plane_t *plane1; + float dot; + +#ifdef DEBUG + if ( !face1->winding ) { + Error( "face1 %d without winding", face1->num ); + } + if ( !face2->winding ) { + Error( "face2 %d without winding", face2->num ); + } +#endif //DEBUG + w2 = face2->winding; + plane1 = &mapplanes[face1->planenum]; + for ( i = 0; i < w2->numpoints; i++ ) + { + //the point must be on the winding plane + dot = DotProduct( w2->p[i], plane1->normal ) - plane1->dist; + if ( dot < -CLIP_EPSILON || dot > CLIP_EPSILON ) { + return false; + } + { + return 1; + } //end if + } //end for + return 0; +} //end of the function AAS_MeltFaceWinding +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_RemoveTinyAreas( void ) { + //RF, disabled, can cause problems, should do checks on minsidelength instead, and also this should be a post-clustering + // process, as in "-reachableonly" + return; +#if 0 + int side, gside, nummerges; + tmp_area_t *tmparea; + tmp_face_t *face, *gface; + vec_t windingArea; + + if ( !mingroundarea ) { + return; + } + + nummerges = 0; + Log_Write( "AAS_RemoveTinyAreas\r\n" ); + qprintf( "%6d tiny areas removed", 1 ); + // + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + //if the area is invalid + if ( tmparea->invalid ) { + continue; + } //end if + // + // never remove liquid areas + if ( tmparea->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + continue; + } + // + windingArea = 0; + // + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + side = ( face->frontarea != tmparea ); + // + // never remove ladder areas + if ( face->faceflags & FACE_LADDER ) { + break; + } + // + //if this face touches a grounded face, and there is no area on the other side, then dont remove it + if ( !( face->faceflags & FACE_GROUND ) ) { + // does this face share an edge with a ground face? + for ( gface = tmparea->tmpfaces; gface; gface = gface->next[gside] ) { + gside = ( gface->frontarea != tmparea ); + // + if ( gface == face ) { + continue; + } + if ( !( gface->faceflags & FACE_GROUND ) ) { + continue; + } + // + if ( AAS_FacesTouching( face, gface ) ) { + break; + } + } + + // if this face touches a grounded face, and there is no area on the other side, then this area must stay + if ( gface && ( !face->frontarea || face->frontarea->invalid || !face->backarea || face->backarea->invalid || ( face->faceflags & FACE_SOLID ) ) ) { + break; + } + } + // + if ( face->faceflags & FACE_GROUND ) { + // get the area of this face + windingArea += WindingArea( face->winding ); + } + } //end for + // + // if this area has a windingArea low enough, then remove it + if ( !face && windingArea && windingArea < mingroundarea ) { + qprintf( "\r%6d", ++nummerges ); + tmparea->invalid = 1; + } + + } //end for + qprintf( "\n" ); + Log_Write( "%6d tiny areas removed\r\n", nummerges ); +#endif +} //end of the function AAS_MergeAreas +//=========================================================================== +// creates an .AAS file with the given name +// a MAP should be loaded before calling this +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Create( char *aasfile ) { + entity_t *e; + tree_t *tree; + double start_time; + + //for a possible leak file + strcpy( source, aasfile ); + StripExtension( source ); + //the time started + start_time = I_FloatTime(); + //set the default number of threads (depends on number of processors) + ThreadSetDefault(); + //set the global entity number to the world model + entity_num = 0; + //the world entity + e = &entities[entity_num]; + // + ResetBrushBSP(); + // + if ( writebrushmap ) { + char brushMapName[1024]; + strcpy( brushMapName, aasfile ); + StripExtension( brushMapName ); + strcat( brushMapName, "_aas.map" ); + OpenBSPBrushMap( brushMapName, CONTENTS_SOLID | CONTENTS_MOVER ); + } + //process the whole world + tree = ProcessWorldBrushes( e->firstbrush, e->firstbrush + e->numbrushes ); + //if the conversion is cancelled + if ( cancelconversion ) { + Tree_Free( tree ); + return; + } //end if + //display BSP tree creation time + Log_Print( "BSP tree created in %5.0f seconds\n", I_FloatTime() - start_time ); + //prune the bsp tree + Tree_PruneNodes( tree->headnode ); + //if the conversion is cancelled + if ( cancelconversion ) { + Tree_Free( tree ); + return; + } //end if + //create the tree portals + MakeTreePortals( tree ); + //if the conversion is cancelled + if ( cancelconversion ) { + Tree_Free( tree ); + return; + } //end if + //Marks all nodes that can be reached by entites + if ( FloodEntities( tree ) ) { + //fill out nodes that can't be reached + FillOutside( tree->headnode ); + } //end if + else + { + LeakFile( tree ); + Error( "**** leaked ****\n" ); + return; + } //end else + //create AAS from the BSP tree + //========================================== + //initialize tmp aas + AAS_InitTmpAAS(); + //create the convex areas from the leaves + AAS_CreateAreas( tree->headnode ); + //free the BSP tree because it isn't used anymore + if ( freetree ) { + Tree_Free( tree ); + } + //try to merge area faces + AAS_MergeAreaFaces(); + //do gravitational subdivision + AAS_GravitationalSubdivision(); + //merge faces if possible + AAS_MergeAreaFaces(); + AAS_RemoveAreaFaceColinearPoints(); + //merge areas if possible + AAS_MergeAreas(); + //remove tiny areas + AAS_RemoveTinyAreas(); + //NOTE: prune nodes directly after area merging + AAS_PruneNodes(); + //flip shared faces so they are all facing to the same area + AAS_FlipSharedFaces(); + AAS_RemoveAreaFaceColinearPoints(); + //merge faces if possible + AAS_MergeAreaFaces(); + //merge area faces in the same plane + AAS_MergeAreaPlaneFaces(); + //do ladder subdivision + AAS_LadderSubdivision(); + //FIXME: melting is buggy + AAS_MeltAreaFaceWindings(); + //remove tiny faces + AAS_RemoveTinyFaces(); + //create area settings + AAS_CreateAreaSettings(); + //check if the winding plane is equal to the face plane + //AAS_CheckAreaWindingPlanes(); + // + //AAS_CheckSharedFaces(); + //========================================== + //if the conversion is cancelled + if ( cancelconversion ) { + Tree_Free( tree ); + AAS_FreeTmpAAS(); + return; + } //end if + //store the created AAS stuff in the AAS file format and write the file + AAS_StoreFile( aasfile ); + //free the temporary AAS memory + AAS_FreeTmpAAS(); + //display creation time + Log_Print( "\nAAS created in %5.0f seconds\n", I_FloatTime() - start_time ); +} //end of the function AAS_Create diff --git a/src/bspc/aas_create.h b/src/bspc/aas_create.h new file mode 100644 index 0000000..2b2f954 --- /dev/null +++ b/src/bspc/aas_create.h @@ -0,0 +1,153 @@ +/* +=========================================================================== + +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: aas_create.h +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#define AREA_PORTAL 1 + +//temporary AAS face +typedef struct tmp_face_s +{ + int num; //face number + int planenum; //number of the plane the face is in + winding_t *winding; //winding of the face + struct tmp_area_s *frontarea; //area at the front of the face + struct tmp_area_s *backarea; //area at the back of the face + int faceflags; //flags of this face + int aasfacenum; //the number of the aas face used for this face + //double link list pointers for front and back area + struct tmp_face_s *prev[2], *next[2]; + //links in the list with faces + struct tmp_face_s *l_prev, *l_next; +} tmp_face_t; + +//temporary AAS area settings +typedef struct tmp_areasettings_s +{ + //could also add all kind of statistic fields + int contents; //contents of the area + int modelnum; //bsp model inside this area + int areaflags; //area flags + int presencetype; //how a bot can be present in this area + int numreachableareas; //number of reachable areas from this one + int firstreachablearea; //first reachable area in the reachable area index + // Ridah, steepness + float groundsteepness; +} tmp_areasettings_t; + +//temporary AAS area +typedef struct tmp_area_s +{ + int areanum; //number of the area + struct tmp_face_s *tmpfaces; //the faces of the area + int presencetype; //presence type of the area + int contents; //area contents + int modelnum; //bsp model inside this area + int invalid; //true if the area is invalid + tmp_areasettings_t *settings; //area settings + struct tmp_area_s *mergedarea; //points to the new area after merging + //when mergedarea != 0 the area has only the + //seperating face of the merged areas + int aasareanum; //number of the aas area created for this tmp area + //links in the list with areas + struct tmp_area_s *l_prev, *l_next; +} tmp_area_t; + +//temporary AAS node +typedef struct tmp_node_s +{ + int planenum; //node plane number + struct tmp_area_s *tmparea; //points to an area if this node is an area + struct tmp_node_s *children[2]; //child nodes of this node +} tmp_node_t; + +#define NODEBUF_SIZE 128 +//node buffer +typedef struct tmp_nodebuf_s +{ + int numnodes; + struct tmp_nodebuf_s *next; + tmp_node_t nodes[NODEBUF_SIZE]; +} tmp_nodebuf_t; + +//the whole temorary AAS +typedef struct tmp_aas_s +{ + //faces + int numfaces; + int facenum; + tmp_face_t *faces; + //areas + int numareas; + int areanum; + tmp_area_t *areas; + //area settings + int numareasettings; + tmp_areasettings_t *areasettings; + //nodes + int numnodes; + tmp_node_t *nodes; + //node buffer + tmp_nodebuf_t *nodebuffer; +} tmp_aas_t; + +extern tmp_aas_t tmpaasworld; + +//creates a .AAS file with the given name from an already loaded map +void AAS_Create( char *aasfile ); +//adds a face side to an area +void AAS_AddFaceSideToArea( tmp_face_t *tmpface, int side, tmp_area_t *tmparea ); +//remvoes a face from an area +void AAS_RemoveFaceFromArea( tmp_face_t *tmpface, tmp_area_t *tmparea ); +//allocate a tmp face +tmp_face_t *AAS_AllocTmpFace( void ); +//free the tmp face +void AAS_FreeTmpFace( tmp_face_t *tmpface ); +//allocate a tmp area +tmp_area_t *AAS_AllocTmpArea( void ); +//free a tmp area +void AAS_FreeTmpArea( tmp_area_t *tmparea ); +//allocate a tmp node +tmp_node_t *AAS_AllocTmpNode( void ); +//free a tmp node +void AAS_FreeTmpNode( tmp_node_t *node ); +//checks if an area is ok +void AAS_CheckArea( tmp_area_t *tmparea ); +//flips the area faces where needed +void AAS_FlipAreaFaces( tmp_area_t *tmparea ); +//returns true if the face is a gap seen from the given side +int AAS_GapFace( tmp_face_t *tmpface, int side ); +//returns true if the face is a ground face +int AAS_GroundFace( tmp_face_t *tmpface ); diff --git a/src/bspc/aas_edgemelting.c b/src/bspc/aas_edgemelting.c new file mode 100644 index 0000000..f7032e2 --- /dev/null +++ b/src/bspc/aas_edgemelting.c @@ -0,0 +1,125 @@ +/* +=========================================================================== + +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: aas_edgemelting.c +// Function: Melting of Edges +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_create.h" + +//=========================================================================== +// try to melt the windings of the two faces +// FIXME: this is buggy +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_MeltFaceWinding( tmp_face_t *face1, tmp_face_t *face2 ) { + int i, n; + int splits = 0; + winding_t *w2, *neww; + plane_t *plane1; + +#ifdef DEBUG + if ( !face1->winding ) { + Error( "face1 %d without winding", face1->num ); + } + if ( !face2->winding ) { + Error( "face2 %d without winding", face2->num ); + } +#endif //DEBUG + w2 = face2->winding; + plane1 = &mapplanes[face1->planenum]; + for ( i = 0; i < w2->numpoints; i++ ) + { + if ( PointOnWinding( face1->winding, plane1->normal, plane1->dist, w2->p[i], &n ) ) { + neww = AddWindingPoint( face1->winding, w2->p[i], n ); + FreeWinding( face1->winding ); + face1->winding = neww; + + splits++; + } //end if + } //end for + return splits; +} //end of the function AAS_MeltFaceWinding +//=========================================================================== +// melt the windings of the area faces +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_MeltFaceWindingsOfArea( tmp_area_t *tmparea ) { + int side1, side2, num_windingsplits = 0; + tmp_face_t *face1, *face2; + + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea; + for ( face2 = tmparea->tmpfaces; face2; face2 = face2->next[side2] ) + { + side2 = face2->frontarea != tmparea; + if ( face1 == face2 ) { + continue; + } + num_windingsplits += AAS_MeltFaceWinding( face1, face2 ); + } //end for + } //end for + return num_windingsplits; +} //end of the function AAS_MeltFaceWindingsOfArea +//=========================================================================== +// melt the windings of the faces of all areas +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_MeltAreaFaceWindings( void ) { + tmp_area_t *tmparea; + int num_windingsplits = 0; + + Log_Write( "AAS_MeltAreaFaceWindings\r\n" ); + qprintf( "%6d edges melted", num_windingsplits ); + //NOTE: first convex area (zero) is a dummy + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + num_windingsplits += AAS_MeltFaceWindingsOfArea( tmparea ); + qprintf( "\r%6d", num_windingsplits ); + } //end for + qprintf( "\n" ); + Log_Write( "%6d edges melted\r\n", num_windingsplits ); +} //end of the function AAS_MeltAreaFaceWindings + diff --git a/src/bspc/aas_edgemelting.h b/src/bspc/aas_edgemelting.h new file mode 100644 index 0000000..d46532c --- /dev/null +++ b/src/bspc/aas_edgemelting.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: aas_edgemelting.h +// Function: Melting of Edges +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + + +void AAS_MeltAreaFaceWindings( void ); + diff --git a/src/bspc/aas_facemerging.c b/src/bspc/aas_facemerging.c new file mode 100644 index 0000000..7e5ee04 --- /dev/null +++ b/src/bspc/aas_facemerging.c @@ -0,0 +1,312 @@ +/* +=========================================================================== + +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: aas_facemerging.c +// Function: Merging of Faces +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_create.h" + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TryMergeFaces( tmp_face_t *face1, tmp_face_t *face2 ) { + winding_t *neww; + +#ifdef DEBUG + if ( !face1->winding ) { + Error( "face1 %d without winding", face1->num ); + } + if ( !face2->winding ) { + Error( "face2 %d without winding", face2->num ); + } +#endif //DEBUG + // + if ( face1->faceflags != face2->faceflags ) { + return false; + } + //NOTE: if the front or back area is zero this doesn't mean there's + //a real area. It means there's solid at that side of the face + //if both faces have the same front area + if ( face1->frontarea == face2->frontarea ) { + //if both faces have the same back area + if ( face1->backarea == face2->backarea ) { + //if the faces are in the same plane + if ( face1->planenum == face2->planenum ) { + //if they have both a front and a back area (no solid on either side) + if ( face1->frontarea && face1->backarea ) { + neww = MergeWindings( face1->winding, face2->winding, + mapplanes[face1->planenum].normal ); + } //end if + else + { + //this function is to be found in l_poly.c + neww = TryMergeWinding( face1->winding, face2->winding, + mapplanes[face1->planenum].normal ); + } //end else + if ( neww ) { + FreeWinding( face1->winding ); + face1->winding = neww; + if ( face2->frontarea ) { + AAS_RemoveFaceFromArea( face2, face2->frontarea ); + } + if ( face2->backarea ) { + AAS_RemoveFaceFromArea( face2, face2->backarea ); + } + AAS_FreeTmpFace( face2 ); + return true; + } //end if + } //end if + else if ( ( face1->planenum & ~1 ) == ( face2->planenum & ~1 ) ) { + Log_Write( "face %d and %d, same front and back area but flipped planes\r\n", + face1->num, face2->num ); + } //end if + } //end if + } //end if + return false; +} //end of the function AAS_TryMergeFaces +/* +int AAS_TryMergeFaces(tmp_face_t *face1, tmp_face_t *face2) +{ + winding_t *neww; + +#ifdef DEBUG + if (!face1->winding) Error("face1 %d without winding", face1->num); + if (!face2->winding) Error("face2 %d without winding", face2->num); +#endif //DEBUG + //if the faces are in the same plane + if ((face1->planenum & ~1) != (face2->planenum & ~1)) return false; +// if (face1->planenum != face2->planenum) return false; + //NOTE: if the front or back area is zero this doesn't mean there's + //a real area. It means there's solid at that side of the face + //if both faces have the same front area + if (face1->frontarea != face2->frontarea || + face1->backarea != face2->backarea) + { + if (!face1->frontarea || !face1->backarea || + !face2->frontarea || !face2->backarea) return false; + else if (face1->frontarea != face2->backarea || + face1->backarea != face2->frontarea) return false; +// return false; + } //end if + //this function is to be found in l_poly.c + neww = TryMergeWinding(face1->winding, face2->winding, + mapplanes[face1->planenum].normal); + if (!neww) return false; + // + FreeWinding(face1->winding); + face1->winding = neww; + //remove face2 + if (face2->frontarea) + AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->frontarea]); + if (face2->backarea) + AAS_RemoveFaceFromArea(face2, &tmpaasworld.areas[face2->backarea]); + return true; +} //end of the function AAS_TryMergeFaces*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_MergeAreaFaces( void ) { + int num_facemerges = 0; + int side1, side2, restart; + tmp_area_t *tmparea, *lasttmparea; + tmp_face_t *face1, *face2; + + Log_Write( "AAS_MergeAreaFaces\r\n" ); + qprintf( "%6d face merges", num_facemerges ); + //NOTE: first convex area is a dummy + lasttmparea = tmpaasworld.areas; + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) + { + restart = false; + // + if ( tmparea->invalid ) { + continue; + } + // + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea; + for ( face2 = face1->next[side1]; face2; face2 = face2->next[side2] ) + { + side2 = face2->frontarea != tmparea; + //if succesfully merged + if ( AAS_TryMergeFaces( face1, face2 ) ) { + //start over again after merging two faces + restart = true; + num_facemerges++; + qprintf( "\r%6d", num_facemerges ); + AAS_CheckArea( tmparea ); + break; + } //end if + } //end for + if ( restart ) { + tmparea = lasttmparea; + break; + } //end if + } //end for + lasttmparea = tmparea; + } //end for + qprintf( "\n" ); + Log_Write( "%6d face merges\r\n", num_facemerges ); +} //end of the function AAS_MergeAreaFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_MergePlaneFaces( tmp_area_t *tmparea, int planenum ) { + tmp_face_t *face1, *face2, *nextface2; + winding_t *neww; + int side1, side2; + + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea; + if ( face1->planenum != planenum ) { + continue; + } + // + for ( face2 = face1->next[side1]; face2; face2 = nextface2 ) + { + side2 = face2->frontarea != tmparea; + nextface2 = face2->next[side2]; + // + if ( ( face2->planenum & ~1 ) != ( planenum & ~1 ) ) { + continue; + } + // + neww = MergeWindings( face1->winding, face2->winding, + mapplanes[face1->planenum].normal ); + FreeWinding( face1->winding ); + face1->winding = neww; + if ( face2->frontarea ) { + AAS_RemoveFaceFromArea( face2, face2->frontarea ); + } + if ( face2->backarea ) { + AAS_RemoveFaceFromArea( face2, face2->backarea ); + } + AAS_FreeTmpFace( face2 ); + // + nextface2 = face1->next[side1]; + } //end for + } //end for +} //end of the function AAS_MergePlaneFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_CanMergePlaneFaces( tmp_area_t *tmparea, int planenum ) { + tmp_area_t *frontarea, *backarea; + tmp_face_t *face1; + int side1, merge, faceflags; + + frontarea = backarea = NULL; + merge = false; + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea; + if ( ( face1->planenum & ~1 ) != ( planenum & ~1 ) ) { + continue; + } + if ( !frontarea && !backarea ) { + frontarea = face1->frontarea; + backarea = face1->backarea; + faceflags = face1->faceflags; + } //end if + else + { + if ( frontarea != face1->frontarea ) { + return false; + } + if ( backarea != face1->backarea ) { + return false; + } + if ( faceflags != face1->faceflags ) { + return false; + } + merge = true; + } //end else + } //end for + return merge; +} //end of the function AAS_CanMergePlaneFaces +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_MergeAreaPlaneFaces( void ) { + int num_facemerges = 0; + int side1; + tmp_area_t *tmparea, *nexttmparea; + tmp_face_t *face1; + + Log_Write( "AAS_MergePlaneFaces\r\n" ); + qprintf( "%6d plane face merges", num_facemerges ); + //NOTE: first convex area is a dummy + for ( tmparea = tmpaasworld.areas; tmparea; tmparea = nexttmparea ) + { + nexttmparea = tmparea->l_next; + // + if ( tmparea->invalid ) { + continue; + } + // + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + side1 = face1->frontarea != tmparea; + // + if ( AAS_CanMergePlaneFaces( tmparea, face1->planenum ) ) { + AAS_MergePlaneFaces( tmparea, face1->planenum ); + nexttmparea = tmparea; + num_facemerges++; + qprintf( "\r%6d", num_facemerges ); + break; + } //end if + } //end for + } //end for + qprintf( "\n" ); + Log_Write( "%6d plane face merges\r\n", num_facemerges ); +} //end of the function AAS_MergeAreaPlaneFaces diff --git a/src/bspc/aas_facemerging.h b/src/bspc/aas_facemerging.h new file mode 100644 index 0000000..80fcac7 --- /dev/null +++ b/src/bspc/aas_facemerging.h @@ -0,0 +1,39 @@ +/* +=========================================================================== + +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: aas_facemerging.h +// Function: Merging of Faces +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +void AAS_MergeAreaFaces( void ); +void AAS_MergeAreaPlaneFaces( void ); diff --git a/src/bspc/aas_file.c b/src/bspc/aas_file.c new file mode 100644 index 0000000..c8a8661 --- /dev/null +++ b/src/bspc/aas_file.c @@ -0,0 +1,603 @@ +/* +=========================================================================== + +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: aas_file.c +// Function: AAS file loading and writing +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_file.h" +#include "aas_store.h" +#include "aas_create.h" + +#define AAS_Error Error + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SwapAASData( void ) { + int i, j; + //bounding boxes + for ( i = 0; i < ( *aasworld ).numbboxes; i++ ) + { + ( *aasworld ).bboxes[i].presencetype = LittleLong( ( *aasworld ).bboxes[i].presencetype ); + ( *aasworld ).bboxes[i].flags = LittleLong( ( *aasworld ).bboxes[i].flags ); + for ( j = 0; j < 3; j++ ) + { + ( *aasworld ).bboxes[i].mins[j] = LittleLong( ( *aasworld ).bboxes[i].mins[j] ); + ( *aasworld ).bboxes[i].maxs[j] = LittleLong( ( *aasworld ).bboxes[i].maxs[j] ); + } //end for + } //end for + //vertexes + for ( i = 0; i < ( *aasworld ).numvertexes; i++ ) + { + for ( j = 0; j < 3; j++ ) + ( *aasworld ).vertexes[i][j] = LittleFloat( ( *aasworld ).vertexes[i][j] ); + } //end for + //planes + for ( i = 0; i < ( *aasworld ).numplanes; i++ ) + { + for ( j = 0; j < 3; j++ ) + ( *aasworld ).planes[i].normal[j] = LittleFloat( ( *aasworld ).planes[i].normal[j] ); + ( *aasworld ).planes[i].dist = LittleFloat( ( *aasworld ).planes[i].dist ); + ( *aasworld ).planes[i].type = LittleLong( ( *aasworld ).planes[i].type ); + } //end for + //edges + for ( i = 0; i < ( *aasworld ).numedges; i++ ) + { + ( *aasworld ).edges[i].v[0] = LittleLong( ( *aasworld ).edges[i].v[0] ); + ( *aasworld ).edges[i].v[1] = LittleLong( ( *aasworld ).edges[i].v[1] ); + } //end for + //edgeindex + for ( i = 0; i < ( *aasworld ).edgeindexsize; i++ ) + { + ( *aasworld ).edgeindex[i] = LittleLong( ( *aasworld ).edgeindex[i] ); + } //end for + //faces + for ( i = 0; i < ( *aasworld ).numfaces; i++ ) + { + ( *aasworld ).faces[i].planenum = LittleLong( ( *aasworld ).faces[i].planenum ); + ( *aasworld ).faces[i].faceflags = LittleLong( ( *aasworld ).faces[i].faceflags ); + ( *aasworld ).faces[i].numedges = LittleLong( ( *aasworld ).faces[i].numedges ); + ( *aasworld ).faces[i].firstedge = LittleLong( ( *aasworld ).faces[i].firstedge ); + ( *aasworld ).faces[i].frontarea = LittleLong( ( *aasworld ).faces[i].frontarea ); + ( *aasworld ).faces[i].backarea = LittleLong( ( *aasworld ).faces[i].backarea ); + } //end for + //face index + for ( i = 0; i < ( *aasworld ).faceindexsize; i++ ) + { + ( *aasworld ).faceindex[i] = LittleLong( ( *aasworld ).faceindex[i] ); + } //end for + //convex areas + for ( i = 0; i < ( *aasworld ).numareas; i++ ) + { + ( *aasworld ).areas[i].areanum = LittleLong( ( *aasworld ).areas[i].areanum ); + ( *aasworld ).areas[i].numfaces = LittleLong( ( *aasworld ).areas[i].numfaces ); + ( *aasworld ).areas[i].firstface = LittleLong( ( *aasworld ).areas[i].firstface ); + for ( j = 0; j < 3; j++ ) + { + ( *aasworld ).areas[i].mins[j] = LittleFloat( ( *aasworld ).areas[i].mins[j] ); + ( *aasworld ).areas[i].maxs[j] = LittleFloat( ( *aasworld ).areas[i].maxs[j] ); + ( *aasworld ).areas[i].center[j] = LittleFloat( ( *aasworld ).areas[i].center[j] ); + } //end for + } //end for + //area settings + for ( i = 0; i < ( *aasworld ).numareasettings; i++ ) + { + ( *aasworld ).areasettings[i].contents = LittleLong( ( *aasworld ).areasettings[i].contents ); + ( *aasworld ).areasettings[i].areaflags = LittleLong( ( *aasworld ).areasettings[i].areaflags ); + ( *aasworld ).areasettings[i].presencetype = LittleLong( ( *aasworld ).areasettings[i].presencetype ); + ( *aasworld ).areasettings[i].cluster = LittleLong( ( *aasworld ).areasettings[i].cluster ); + ( *aasworld ).areasettings[i].clusterareanum = LittleLong( ( *aasworld ).areasettings[i].clusterareanum ); + ( *aasworld ).areasettings[i].numreachableareas = LittleLong( ( *aasworld ).areasettings[i].numreachableareas ); + ( *aasworld ).areasettings[i].firstreachablearea = LittleLong( ( *aasworld ).areasettings[i].firstreachablearea ); + // Ridah + ( *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].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 ) { + /* + if ((*aasworld).vertexes) FreeMemory((*aasworld).vertexes); + (*aasworld).vertexes = NULL; + if ((*aasworld).planes) FreeMemory((*aasworld).planes); + (*aasworld).planes = NULL; + if ((*aasworld).edges) FreeMemory((*aasworld).edges); + (*aasworld).edges = NULL; + if ((*aasworld).edgeindex) FreeMemory((*aasworld).edgeindex); + (*aasworld).edgeindex = NULL; + if ((*aasworld).faces) FreeMemory((*aasworld).faces); + (*aasworld).faces = NULL; + if ((*aasworld).faceindex) FreeMemory((*aasworld).faceindex); + (*aasworld).faceindex = NULL; + if ((*aasworld).areas) FreeMemory((*aasworld).areas); + (*aasworld).areas = NULL; + if ((*aasworld).areasettings) FreeMemory((*aasworld).areasettings); + (*aasworld).areasettings = NULL; + if ((*aasworld).reachability) FreeMemory((*aasworld).reachability); + (*aasworld).reachability = NULL; + */ + ( *aasworld ).loaded = false; +} //end of the function AAS_DumpAASData +//=========================================================================== +// allocate memory and read a lump of a AAS file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *AAS_LoadAASLump( FILE *fp, int offset, int length, void *buf ) { + if ( !length ) { + printf( "lump size 0\n" ); + return buf; + } //end if + //seek to the data + if ( fseek( fp, offset, SEEK_SET ) ) { + AAS_Error( "can't seek to lump\n" ); + AAS_DumpAASData(); + fclose( fp ); + return 0; + } //end if + //allocate memory + if ( !buf ) { + buf = (void *) GetClearedMemory( length ); + } + //read the data + if ( fread( (char *) buf, 1, length, fp ) != length ) { + AAS_Error( "can't read lump\n" ); + FreeMemory( buf ); + AAS_DumpAASData(); + fclose( fp ); + return NULL; + } //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: - +//=========================================================================== +qboolean AAS_LoadAASFile( char *filename, int fpoffset, int fplength ) { + FILE *fp; + aas_header_t header; + int offset, length; + + //dump current loaded aas file + AAS_DumpAASData(); + //open the file + fp = fopen( filename, "rb" ); + if ( !fp ) { + AAS_Error( "can't open %s\n", filename ); + return false; + } //end if + //seek to the correct position (in the pak file) + if ( fseek( fp, fpoffset, SEEK_SET ) ) { + AAS_Error( "can't seek to file %s\n" ); + fclose( fp ); + return false; + } //end if + //read the header + if ( fread( &header, sizeof( aas_header_t ), 1, fp ) != 1 ) { + AAS_Error( "can't read header of file %s\n", filename ); + fclose( fp ); + return false; + } //end if + //check header identification + header.ident = LittleLong( header.ident ); + if ( header.ident != AASID ) { + AAS_Error( "%s is not an AAS file\n", filename ); + fclose( fp ); + return false; + } //end if + //check the version + header.version = LittleLong( header.version ); + if ( header.version != AASVERSION ) { + AAS_Error( "%s is version %i, not %i\n", filename, header.version, AASVERSION ); + fclose( fp ); + return false; + } //end if + // + if ( header.version == AASVERSION ) { + AAS_DData( (unsigned char *) &header + 8, sizeof( aas_header_t ) - 8 ); + } //end if + ( *aasworld ).bspchecksum = LittleLong( header.bspchecksum ); + //load the lumps: + //bounding boxes + offset = fpoffset + LittleLong( header.lumps[AASLUMP_BBOXES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_BBOXES].filelen ); + ( *aasworld ).bboxes = (aas_bbox_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).bboxes ); + if ( !( *aasworld ).bboxes ) { + return false; + } + ( *aasworld ).numbboxes = length / sizeof( aas_bbox_t ); + //vertexes + offset = fpoffset + LittleLong( header.lumps[AASLUMP_VERTEXES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_VERTEXES].filelen ); + ( *aasworld ).vertexes = (aas_vertex_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).vertexes ); + if ( !( *aasworld ).vertexes ) { + return false; + } + ( *aasworld ).numvertexes = length / sizeof( aas_vertex_t ); + //planes + offset = fpoffset + LittleLong( header.lumps[AASLUMP_PLANES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_PLANES].filelen ); + ( *aasworld ).planes = (aas_plane_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).planes ); + if ( !( *aasworld ).planes ) { + return false; + } + ( *aasworld ).numplanes = length / sizeof( aas_plane_t ); + //edges + offset = fpoffset + LittleLong( header.lumps[AASLUMP_EDGES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_EDGES].filelen ); + ( *aasworld ).edges = (aas_edge_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).edges ); + if ( !( *aasworld ).edges ) { + return false; + } + ( *aasworld ).numedges = length / sizeof( aas_edge_t ); + //edgeindex + offset = fpoffset + LittleLong( header.lumps[AASLUMP_EDGEINDEX].fileofs ); + length = LittleLong( header.lumps[AASLUMP_EDGEINDEX].filelen ); + ( *aasworld ).edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).edgeindex ); + if ( !( *aasworld ).edgeindex ) { + return false; + } + ( *aasworld ).edgeindexsize = length / sizeof( aas_edgeindex_t ); + //faces + offset = fpoffset + LittleLong( header.lumps[AASLUMP_FACES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_FACES].filelen ); + ( *aasworld ).faces = (aas_face_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).faces ); + if ( !( *aasworld ).faces ) { + return false; + } + ( *aasworld ).numfaces = length / sizeof( aas_face_t ); + //faceindex + offset = fpoffset + LittleLong( header.lumps[AASLUMP_FACEINDEX].fileofs ); + length = LittleLong( header.lumps[AASLUMP_FACEINDEX].filelen ); + ( *aasworld ).faceindex = (aas_faceindex_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).faceindex ); + if ( !( *aasworld ).faceindex ) { + return false; + } + ( *aasworld ).faceindexsize = length / sizeof( int ); + //convex areas + offset = fpoffset + LittleLong( header.lumps[AASLUMP_AREAS].fileofs ); + length = LittleLong( header.lumps[AASLUMP_AREAS].filelen ); + ( *aasworld ).areas = (aas_area_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).areas ); + if ( !( *aasworld ).areas ) { + return false; + } + ( *aasworld ).numareas = length / sizeof( aas_area_t ); + //area settings + offset = fpoffset + LittleLong( header.lumps[AASLUMP_AREASETTINGS].fileofs ); + length = LittleLong( header.lumps[AASLUMP_AREASETTINGS].filelen ); + ( *aasworld ).areasettings = (aas_areasettings_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).areasettings ); + if ( !( *aasworld ).areasettings ) { + return false; + } + ( *aasworld ).numareasettings = length / sizeof( aas_areasettings_t ); + //reachability list + offset = fpoffset + LittleLong( header.lumps[AASLUMP_REACHABILITY].fileofs ); + length = LittleLong( header.lumps[AASLUMP_REACHABILITY].filelen ); + ( *aasworld ).reachability = (aas_reachability_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).reachability ); + if ( length && !( *aasworld ).reachability ) { + return false; + } + ( *aasworld ).reachabilitysize = length / sizeof( aas_reachability_t ); + //nodes + offset = fpoffset + LittleLong( header.lumps[AASLUMP_NODES].fileofs ); + length = LittleLong( header.lumps[AASLUMP_NODES].filelen ); + ( *aasworld ).nodes = (aas_node_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).nodes ); + if ( !( *aasworld ).nodes ) { + return false; + } + ( *aasworld ).numnodes = length / sizeof( aas_node_t ); + //cluster portals + offset = fpoffset + LittleLong( header.lumps[AASLUMP_PORTALS].fileofs ); + length = LittleLong( header.lumps[AASLUMP_PORTALS].filelen ); + ( *aasworld ).portals = (aas_portal_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).portals ); + if ( length && !( *aasworld ).portals ) { + return false; + } + ( *aasworld ).numportals = length / sizeof( aas_portal_t ); + //cluster portal index + offset = fpoffset + LittleLong( header.lumps[AASLUMP_PORTALINDEX].fileofs ); + length = LittleLong( header.lumps[AASLUMP_PORTALINDEX].filelen ); + ( *aasworld ).portalindex = (aas_portalindex_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).portalindex ); + if ( length && !( *aasworld ).portalindex ) { + return false; + } + ( *aasworld ).portalindexsize = length / sizeof( aas_portalindex_t ); + //clusters + offset = fpoffset + LittleLong( header.lumps[AASLUMP_CLUSTERS].fileofs ); + length = LittleLong( header.lumps[AASLUMP_CLUSTERS].filelen ); + ( *aasworld ).clusters = (aas_cluster_t *) AAS_LoadAASLump( fp, offset, length, ( *aasworld ).clusters ); + if ( length && !( *aasworld ).clusters ) { + return false; + } + ( *aasworld ).numclusters = length / sizeof( aas_cluster_t ); + //swap everything + AAS_SwapAASData(); + //aas file is loaded + ( *aasworld ).loaded = true; + //close the file + fclose( fp ); + return true; +} //end of the function AAS_LoadAASFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_WriteAASLump( FILE *fp, aas_header_t *h, int lumpnum, void *data, int length ) { + aas_lump_t *lump; + + lump = &h->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( fp ) ); + lump->filelen = LittleLong( length ); + + if ( length > 0 ) { + if ( fwrite( data, length, 1, fp ) < 1 ) { + Log_Print( "error writing lump %s\n", lumpnum ); + fclose( fp ); + return false; + } //end if + } //end if + return true; +} //end of the function AAS_WriteAASLump +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowNumReachabilities( int tt, char *name ) { + int i, num; + + num = 0; + for ( i = 0; i < ( *aasworld ).reachabilitysize; i++ ) + { + if ( ( ( *aasworld ).reachability[i].traveltype & TRAVELTYPE_MASK ) == tt ) { + num++; + } + } //end for + Log_Print( "%6d %s\n", num, name ); +} //end of the function AAS_ShowNumReachabilities +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ShowTotals( void ) { + Log_Print( "numvertexes = %d\r\n", ( *aasworld ).numvertexes ); + Log_Print( "numplanes = %d\r\n", ( *aasworld ).numplanes ); + Log_Print( "numedges = %d\r\n", ( *aasworld ).numedges ); + Log_Print( "edgeindexsize = %d\r\n", ( *aasworld ).edgeindexsize ); + Log_Print( "numfaces = %d\r\n", ( *aasworld ).numfaces ); + Log_Print( "faceindexsize = %d\r\n", ( *aasworld ).faceindexsize ); + Log_Print( "numareas = %d\r\n", ( *aasworld ).numareas ); + Log_Print( "numareasettings = %d\r\n", ( *aasworld ).numareasettings ); + Log_Print( "reachabilitysize = %d\r\n", ( *aasworld ).reachabilitysize ); + Log_Print( "numnodes = %d\r\n", ( *aasworld ).numnodes ); + Log_Print( "numportals = %d\r\n", ( *aasworld ).numportals ); + Log_Print( "portalindexsize = %d\r\n", ( *aasworld ).portalindexsize ); + Log_Print( "numclusters = %d\r\n", ( *aasworld ).numclusters ); + AAS_ShowNumReachabilities( TRAVEL_WALK, "walk" ); + AAS_ShowNumReachabilities( TRAVEL_CROUCH, "crouch" ); + AAS_ShowNumReachabilities( TRAVEL_BARRIERJUMP, "barrier jump" ); + AAS_ShowNumReachabilities( TRAVEL_JUMP, "jump" ); + AAS_ShowNumReachabilities( TRAVEL_LADDER, "ladder" ); + AAS_ShowNumReachabilities( TRAVEL_WALKOFFLEDGE, "walk off ledge" ); + AAS_ShowNumReachabilities( TRAVEL_SWIM, "swim" ); + AAS_ShowNumReachabilities( TRAVEL_WATERJUMP, "water jump" ); + AAS_ShowNumReachabilities( TRAVEL_TELEPORT, "teleport" ); + AAS_ShowNumReachabilities( TRAVEL_ELEVATOR, "elevator" ); + AAS_ShowNumReachabilities( TRAVEL_ROCKETJUMP, "rocket jump" ); + AAS_ShowNumReachabilities( TRAVEL_BFGJUMP, "bfg jump" ); + AAS_ShowNumReachabilities( TRAVEL_GRAPPLEHOOK, "grapple hook" ); + AAS_ShowNumReachabilities( TRAVEL_DOUBLEJUMP, "double jump" ); + AAS_ShowNumReachabilities( TRAVEL_RAMPJUMP, "ramp jump" ); + AAS_ShowNumReachabilities( TRAVEL_STRAFEJUMP, "strafe jump" ); + AAS_ShowNumReachabilities( TRAVEL_JUMPPAD, "jump pad" ); + AAS_ShowNumReachabilities( TRAVEL_FUNCBOB, "func bob" ); + +} //end of the function AAS_ShowTotals +//=========================================================================== +// 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; + FILE *fp; + + Log_Print( "writing %s\n", filename ); + AAS_ShowTotals(); + //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 + fp = fopen( filename, "wb" ); + if ( !fp ) { + Log_Print( "error opening %s\n", filename ); + return false; + } //end if + //write the header + if ( fwrite( &header, sizeof( aas_header_t ), 1, fp ) < 1 ) { + fclose( fp ); + return false; + } //end if + //add the data lumps to the file + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_BBOXES, ( *aasworld ).bboxes, + ( *aasworld ).numbboxes * sizeof( aas_bbox_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_VERTEXES, ( *aasworld ).vertexes, + ( *aasworld ).numvertexes * sizeof( aas_vertex_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_PLANES, ( *aasworld ).planes, + ( *aasworld ).numplanes * sizeof( aas_plane_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_EDGES, ( *aasworld ).edges, + ( *aasworld ).numedges * sizeof( aas_edge_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_EDGEINDEX, ( *aasworld ).edgeindex, + ( *aasworld ).edgeindexsize * sizeof( aas_edgeindex_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_FACES, ( *aasworld ).faces, + ( *aasworld ).numfaces * sizeof( aas_face_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_FACEINDEX, ( *aasworld ).faceindex, + ( *aasworld ).faceindexsize * sizeof( aas_faceindex_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_AREAS, ( *aasworld ).areas, + ( *aasworld ).numareas * sizeof( aas_area_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_AREASETTINGS, ( *aasworld ).areasettings, + ( *aasworld ).numareasettings * sizeof( aas_areasettings_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_REACHABILITY, ( *aasworld ).reachability, + ( *aasworld ).reachabilitysize * sizeof( aas_reachability_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_NODES, ( *aasworld ).nodes, + ( *aasworld ).numnodes * sizeof( aas_node_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_PORTALS, ( *aasworld ).portals, + ( *aasworld ).numportals * sizeof( aas_portal_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_PORTALINDEX, ( *aasworld ).portalindex, + ( *aasworld ).portalindexsize * sizeof( aas_portalindex_t ) ) ) { + return false; + } + if ( !AAS_WriteAASLump( fp, &header, AASLUMP_CLUSTERS, ( *aasworld ).clusters, + ( *aasworld ).numclusters * sizeof( aas_cluster_t ) ) ) { + return false; + } + //rewrite the header with the added lumps + fseek( fp, 0, SEEK_SET ); + AAS_DData( (unsigned char *) &header + 8, sizeof( aas_header_t ) - 8 ); + if ( fwrite( &header, sizeof( aas_header_t ), 1, fp ) < 1 ) { + fclose( fp ); + return false; + } //end if + //close the file + fclose( fp ); + return true; +} //end of the function AAS_WriteAASFile + diff --git a/src/bspc/aas_file.h b/src/bspc/aas_file.h new file mode 100644 index 0000000..0536024 --- /dev/null +++ b/src/bspc/aas_file.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: aas_write.h +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +qboolean AAS_WriteAASFile( char *filename ); +qboolean AAS_LoadAASFile( char *filename, int fpoffset, int fplength ); + diff --git a/src/bspc/aas_gsubdiv.c b/src/bspc/aas_gsubdiv.c new file mode 100644 index 0000000..7ecd7a8 --- /dev/null +++ b/src/bspc/aas_gsubdiv.c @@ -0,0 +1,687 @@ +/* +=========================================================================== + +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: aas_gsubdiv.c +// Function: Gravitational Subdivision +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_create.h" +#include "aas_store.h" +#include "aas_cfg.h" + +#define FACECLIP_EPSILON 0.2 +#define FACE_EPSILON 1.0 + +int numgravitationalsubdivisions = 0; +int numladdersubdivisions = 0; + +//NOTE: only do gravitational subdivision BEFORE area merging!!!!!!! +// because the bsp tree isn't refreshes like with ladder subdivision + +//=========================================================================== +// NOTE: the original face is invalid after splitting +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SplitFace( tmp_face_t *face, vec3_t normal, float dist, + tmp_face_t **frontface, tmp_face_t **backface ) { + winding_t *frontw, *backw; + + // + *frontface = *backface = NULL; + + ClipWindingEpsilon( face->winding, normal, dist, FACECLIP_EPSILON, &frontw, &backw ); + +#ifdef DEBUG + // + if ( frontw ) { + if ( WindingIsTiny( frontw ) ) { + Log_Write( "AAS_SplitFace: tiny back face\r\n" ); + FreeWinding( frontw ); + frontw = NULL; + } //end if + } //end if + if ( backw ) { + if ( WindingIsTiny( backw ) ) { + Log_Write( "AAS_SplitFace: tiny back face\r\n" ); + FreeWinding( backw ); + backw = NULL; + } //end if + } //end if +#endif //DEBUG + //if the winding was split + if ( frontw ) { + //check bounds + ( *frontface ) = AAS_AllocTmpFace(); + ( *frontface )->planenum = face->planenum; + ( *frontface )->winding = frontw; + ( *frontface )->faceflags = face->faceflags; + } //end if + if ( backw ) { + //check bounds + ( *backface ) = AAS_AllocTmpFace(); + ( *backface )->planenum = face->planenum; + ( *backface )->winding = backw; + ( *backface )->faceflags = face->faceflags; + } //end if +} //end of the function AAS_SplitFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +winding_t *AAS_SplitWinding( tmp_area_t *tmparea, int planenum ) { + tmp_face_t *face; + plane_t *plane; + int side; + winding_t *splitwinding; + + // + plane = &mapplanes[planenum]; + //create a split winding, first base winding for plane + splitwinding = BaseWindingForPlane( plane->normal, plane->dist ); + //chop with all the faces of the area + for ( face = tmparea->tmpfaces; face && splitwinding; face = face->next[side] ) + { + //side of the face the original area was on + side = face->frontarea != tmparea; + plane = &mapplanes[face->planenum ^ side]; + ChopWindingInPlace( &splitwinding, plane->normal, plane->dist, 0 ); // PLANESIDE_EPSILON); + } //end for + return splitwinding; +} //end of the function AAS_SplitWinding +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TestSplitPlane( tmp_area_t *tmparea, vec3_t normal, float dist, int *facesplits, int *groundsplits, int *epsilonfaces, vec3_t* points ) { + int j, side, front, back, planenum; + float d, d_front, d_back; + tmp_face_t *face; + winding_t *w; + + *facesplits = *groundsplits = *epsilonfaces = 0; + + planenum = FindFloatPlane( normal, dist, 4, points ); + + w = AAS_SplitWinding( tmparea, planenum ); + if ( !w ) { + return false; + } + FreeWinding( w ); + // + for ( face = tmparea->tmpfaces; face; face = face->next[side] ) + { + //side of the face the area is on + side = face->frontarea != tmparea; + + if ( ( face->planenum & ~1 ) == ( planenum & ~1 ) ) { + Log_Print( "AAS_TestSplitPlane: tried face plane as splitter\n" ); + return false; + } //end if + w = face->winding; + //reset distance at front and back side of plane + d_front = d_back = 0; + //reset front and back flags + front = back = 0; + for ( j = 0; j < w->numpoints; j++ ) + { + d = DotProduct( w->p[j], normal ) - dist; + if ( d > d_front ) { + d_front = d; + } + if ( d < d_back ) { + d_back = d; + } + + if ( d > 0.4 ) { // PLANESIDE_EPSILON) + front = 1; + } + if ( d < -0.4 ) { // PLANESIDE_EPSILON) + back = 1; + } + } //end for + //check for an epsilon face + if ( ( d_front > FACECLIP_EPSILON && d_front < FACE_EPSILON ) + || ( d_back < -FACECLIP_EPSILON && d_back > -FACE_EPSILON ) ) { + ( *epsilonfaces )++; + } //end if + //if the face has points at both sides of the plane + if ( front && back ) { + ( *facesplits )++; + if ( face->faceflags & FACE_GROUND ) { + ( *groundsplits )++; + } //end if + } //end if + } //end for + return true; +} //end of the function AAS_TestSplitPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SplitArea( tmp_area_t *tmparea, int planenum, tmp_area_t **frontarea, tmp_area_t **backarea ) { + int side; + tmp_area_t *facefrontarea, *facebackarea, *faceotherarea; + tmp_face_t *face, *frontface, *backface, *splitface, *nextface; + winding_t *splitwinding; + plane_t *splitplane; + +/* +#ifdef AW_DEBUG + int facesplits, groundsplits, epsilonface; + Log_Print("\n----------------------\n"); + Log_Print("splitting area %d\n", areanum); + Log_Print("with normal = \'%f %f %f\', dist = %f\n", normal[0], normal[1], normal[2], dist); + AAS_TestSplitPlane(areanum, normal, dist, &facesplits, &groundsplits, &epsilonface); + Log_Print("face splits = %d\nground splits = %d\n", facesplits, groundsplits); + if (epsilonface) Log_Print("aaahh epsilon face\n"); +#endif //AW_DEBUG*/ + //the original area + + AAS_FlipAreaFaces( tmparea ); + AAS_CheckArea( tmparea ); + // + splitplane = &mapplanes[planenum]; +/* //create a split winding, first base winding for plane + splitwinding = BaseWindingForPlane(splitplane->normal, splitplane->dist); + //chop with all the faces of the area + for (face = tmparea->tmpfaces; face && splitwinding; face = face->next[side]) + { + //side of the face the original area was on + side = face->frontarea != tmparea->areanum; + plane = &mapplanes[face->planenum ^ side]; + ChopWindingInPlace(&splitwinding, plane->normal, plane->dist, 0); // PLANESIDE_EPSILON); + } //end for*/ + splitwinding = AAS_SplitWinding( tmparea, planenum ); + if ( !splitwinding ) { +/* +#ifdef DEBUG + AAS_TestSplitPlane(areanum, normal, dist, &facesplits, &groundsplits, &epsilonface); + Log_Print("\nface splits = %d\nground splits = %d\n", facesplits, groundsplits); + if (epsilonface) Log_Print("aaahh epsilon face\n"); +#endif //DEBUG*/ + Error( "AAS_SplitArea: no split winding when splitting area %d\n", tmparea->areanum ); + } //end if + //create a split face + splitface = AAS_AllocTmpFace(); + //get the map plane + splitface->planenum = planenum; + //store the split winding + splitface->winding = splitwinding; + //the new front area + ( *frontarea ) = AAS_AllocTmpArea(); + ( *frontarea )->presencetype = tmparea->presencetype; + ( *frontarea )->contents = tmparea->contents; + ( *frontarea )->modelnum = tmparea->modelnum; + ( *frontarea )->tmpfaces = NULL; + //the new back area + ( *backarea ) = AAS_AllocTmpArea(); + ( *backarea )->presencetype = tmparea->presencetype; + ( *backarea )->contents = tmparea->contents; + ( *backarea )->modelnum = tmparea->modelnum; + ( *backarea )->tmpfaces = NULL; + //add the split face to the new areas + AAS_AddFaceSideToArea( splitface, 0, ( *frontarea ) ); + AAS_AddFaceSideToArea( splitface, 1, ( *backarea ) ); + + //split all the faces of the original area + for ( face = tmparea->tmpfaces; face; face = nextface ) + { + //side of the face the original area was on + side = face->frontarea != tmparea; + //next face of the original area + nextface = face->next[side]; + //front area of the face + facefrontarea = face->frontarea; + //back area of the face + facebackarea = face->backarea; + //remove the face from both the front and back areas + if ( facefrontarea ) { + AAS_RemoveFaceFromArea( face, facefrontarea ); + } + if ( facebackarea ) { + AAS_RemoveFaceFromArea( face, facebackarea ); + } + //split the face + AAS_SplitFace( face, splitplane->normal, splitplane->dist, &frontface, &backface ); + //free the original face + AAS_FreeTmpFace( face ); + //get the number of the area at the other side of the face + if ( side ) { + faceotherarea = facefrontarea; + } else { faceotherarea = facebackarea;} + //if there is an area at the other side of the original face + if ( faceotherarea ) { + if ( frontface ) { + AAS_AddFaceSideToArea( frontface, !side, faceotherarea ); + } + if ( backface ) { + AAS_AddFaceSideToArea( backface, !side, faceotherarea ); + } + } //end if + //add the front and back part left after splitting the original face to the new areas + if ( frontface ) { + AAS_AddFaceSideToArea( frontface, side, ( *frontarea ) ); + } + if ( backface ) { + AAS_AddFaceSideToArea( backface, side, ( *backarea ) ); + } + } //end for + + if ( !( *frontarea )->tmpfaces ) { + Log_Print( "AAS_SplitArea: front area without faces\n" ); + } + if ( !( *backarea )->tmpfaces ) { + Log_Print( "AAS_SplitArea: back area without faces\n" ); + } + + tmparea->invalid = true; +/* +#ifdef AW_DEBUG + for (i = 0, face = frontarea->tmpfaces; face; face = face->next[side]) + { + side = face->frontarea != frontarea->areanum; + i++; + } //end for + Log_Print("created front area %d with %d faces\n", frontarea->areanum, i); + + for (i = 0, face = backarea->tmpfaces; face; face = face->next[side]) + { + side = face->frontarea != backarea->areanum; + i++; + } //end for + Log_Print("created back area %d with %d faces\n", backarea->areanum, i); +#endif //AW_DEBUG*/ + + AAS_FlipAreaFaces( ( *frontarea ) ); + AAS_FlipAreaFaces( ( *backarea ) ); + // + AAS_CheckArea( ( *frontarea ) ); + AAS_CheckArea( ( *backarea ) ); +} //end of the function AAS_SplitArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_FindBestAreaSplitPlane( tmp_area_t *tmparea, vec3_t normal, float *dist ) { + int side1, side2; + int foundsplitter, facesplits, groundsplits, epsilonfaces, bestepsilonfaces; + float bestvalue, value; + tmp_face_t *face1, *face2; + vec3_t tmpnormal, invgravity; + float tmpdist; + vec3_t points[4]; + + //get inverse of gravity direction + VectorCopy( cfg.phys_gravitydirection, invgravity ); + VectorInverse( invgravity ); + + foundsplitter = false; + bestvalue = -999999; + bestepsilonfaces = 0; + // +#ifdef AW_DEBUG + Log_Print( "finding split plane for area %d\n", tmparea->areanum ); +#endif //AW_DEBUG + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) { + //side of the face the area is on + side1 = face1->frontarea != tmparea; + + if ( WindingIsTiny( face1->winding ) ) { + Log_Write( "gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum ); + continue; + } + + //if the face isn't a gap or ground there's no split edge + if ( !( face1->faceflags & FACE_GROUND ) && !AAS_GapFace( face1, side1 ) ) { + continue; + } + + for ( face2 = face1->next[side1]; face2; face2 = face2->next[side2] ) { + //side of the face the area is on + side2 = face2->frontarea != tmparea; + if ( WindingIsTiny( face1->winding ) ) { + Log_Write( "gsubdiv: area %d has a tiny winding\r\n", tmparea->areanum ); + continue; + } + + //if the face isn't a gap or ground there's no split edge + if ( !( face2->faceflags & FACE_GROUND ) && !AAS_GapFace( face2, side2 ) ) { + continue; + } + + //only split between gaps and ground + if ( !( ( ( face1->faceflags & FACE_GROUND ) && AAS_GapFace( face2, side2 ) ) || ( ( face2->faceflags & FACE_GROUND ) && AAS_GapFace( face1, side1 ) ) ) ) { + continue; + } + + //find a plane seperating the windings of the faces + if ( !FindPlaneSeperatingWindings( face1->winding, face2->winding, invgravity, tmpnormal, &tmpdist, points ) ) { + continue; + } + +#ifdef AW_DEBUG + Log_Print( "normal = \'%f %f %f\', dist = %f\n", tmpnormal[0], tmpnormal[1], tmpnormal[2], tmpdist ); +#endif //AW_DEBUG + + //get metrics for this vertical plane + if ( !AAS_TestSplitPlane( tmparea, tmpnormal, tmpdist, &facesplits, &groundsplits, &epsilonfaces, points ) ) { + continue; + } //end if + +#ifdef AW_DEBUG + Log_Print( "face splits = %d\nground splits = %d\n", facesplits, groundsplits ); +#endif //AW_DEBUG + + value = 100 - facesplits - 2 * groundsplits; + //avoid epsilon faces + value += epsilonfaces * -1000; + if ( value > bestvalue ) { + VectorCopy( tmpnormal, normal ); + *dist = tmpdist; + bestvalue = value; + bestepsilonfaces = epsilonfaces; + foundsplitter = true; + } + } + } + + if ( bestepsilonfaces ) { + Log_Write( "found %d epsilon faces trying to split area %d\r\n", epsilonfaces, tmparea->areanum ); + } + + return foundsplitter; +} //end of the function AAS_FindBestAreaSplitPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_SubdivideArea_r( tmp_node_t *tmpnode ) { + int planenum; + tmp_area_t *frontarea, *backarea; + tmp_node_t *tmpnode1, *tmpnode2; + vec3_t normal; + float dist; + + if ( AAS_FindBestAreaSplitPlane( tmpnode->tmparea, normal, &dist ) ) { + qprintf( "\r%6d", ++numgravitationalsubdivisions ); + // + planenum = FindFloatPlane( normal, dist, 0, NULL ); + //split the area + AAS_SplitArea( tmpnode->tmparea, planenum, &frontarea, &backarea ); + // + tmpnode->tmparea = NULL; + tmpnode->planenum = FindFloatPlane( normal, dist, 0, NULL ); + // + tmpnode1 = AAS_AllocTmpNode(); + tmpnode1->planenum = 0; + tmpnode1->tmparea = frontarea; + // + tmpnode2 = AAS_AllocTmpNode(); + tmpnode2->planenum = 0; + tmpnode2->tmparea = backarea; + //subdivide the areas created by splitting recursively + tmpnode->children[0] = AAS_SubdivideArea_r( tmpnode1 ); + tmpnode->children[1] = AAS_SubdivideArea_r( tmpnode2 ); + } //end if + return tmpnode; +} //end of the function AAS_SubdivideArea_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_GravitationalSubdivision_r( tmp_node_t *tmpnode ) { + //if this is a solid leaf + if ( !tmpnode ) { + return NULL; + } + //negative so it's an area + if ( tmpnode->tmparea ) { + return AAS_SubdivideArea_r( tmpnode ); + } + //do the children recursively + tmpnode->children[0] = AAS_GravitationalSubdivision_r( tmpnode->children[0] ); + tmpnode->children[1] = AAS_GravitationalSubdivision_r( tmpnode->children[1] ); + return tmpnode; +} //end of the function AAS_GravitationalSubdivision_r +//=========================================================================== +// NOTE: merge faces and melt edges first +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_GravitationalSubdivision( void ) { + Log_Write( "AAS_GravitationalSubdivision\r\n" ); + numgravitationalsubdivisions = 0; + qprintf( "%6i gravitational subdivisions", numgravitationalsubdivisions ); + //start with the head node + AAS_GravitationalSubdivision_r( tmpaasworld.nodes ); + qprintf( "\n" ); + Log_Write( "%6i gravitational subdivisions\r\n", numgravitationalsubdivisions ); +} //end of the function AAS_GravitationalSubdivision +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_RefreshLadderSubdividedTree_r( tmp_node_t *tmpnode, tmp_area_t *tmparea, + tmp_node_t *tmpnode1, tmp_node_t *tmpnode2, int planenum ) { + //if this is a solid leaf + if ( !tmpnode ) { + return NULL; + } + //negative so it's an area + if ( tmpnode->tmparea ) { + if ( tmpnode->tmparea == tmparea ) { + tmpnode->tmparea = NULL; + tmpnode->planenum = planenum; + tmpnode->children[0] = tmpnode1; + tmpnode->children[1] = tmpnode2; + } //end if + return tmpnode; + } //end if + //do the children recursively + tmpnode->children[0] = AAS_RefreshLadderSubdividedTree_r( tmpnode->children[0], + tmparea, tmpnode1, tmpnode2, planenum ); + tmpnode->children[1] = AAS_RefreshLadderSubdividedTree_r( tmpnode->children[1], + tmparea, tmpnode1, tmpnode2, planenum ); + return tmpnode; +} //end of the function AAS_RefreshLadderSubdividedTree_r +//=========================================================================== +// find an area with ladder faces and ground faces that are not connected +// split the area with a horizontal plane at the lowest vertex of all +// ladder faces in the area +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_LadderSubdivideArea_r( tmp_node_t *tmpnode ) { + int side1, i, planenum; + int foundladderface, foundgroundface; + float dist; + tmp_area_t *tmparea, *frontarea, *backarea; + tmp_face_t *face1; + tmp_node_t *tmpnode1, *tmpnode2; + vec3_t lowestpoint, normal = {0, 0, 1}; + plane_t *plane; + winding_t *w; + + tmparea = tmpnode->tmparea; + //skip areas with a liquid + if ( tmparea->contents & ( AREACONTENTS_WATER + | AREACONTENTS_LAVA + | AREACONTENTS_SLIME ) ) { + return tmpnode; + } + //must be possible to stand in the area + if ( !( tmparea->presencetype & PRESENCE_NORMAL ) ) { + return tmpnode; + } + // + foundladderface = false; + foundgroundface = false; + lowestpoint[2] = 99999; + // + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + //side of the face the area is on + side1 = face1->frontarea != tmparea; + //if the face is a ladder face + if ( face1->faceflags & FACE_LADDER ) { + plane = &mapplanes[face1->planenum]; + //the ladder face plane should be pretty much vertical + if ( DotProduct( plane->normal, normal ) > -0.1 ) { + foundladderface = true; + //find lowest point + for ( i = 0; i < face1->winding->numpoints; i++ ) + { + if ( face1->winding->p[i][2] < lowestpoint[2] ) { + VectorCopy( face1->winding->p[i], lowestpoint ); + } //end if + } //end for + } //end if + } //end if + else if ( face1->faceflags & FACE_GROUND ) { + foundgroundface = true; + } //end else if + } //end for + // + if ( ( !foundladderface ) || ( !foundgroundface ) ) { + return tmpnode; + } + // + for ( face1 = tmparea->tmpfaces; face1; face1 = face1->next[side1] ) + { + //side of the face the area is on + side1 = face1->frontarea != tmparea; + //if the face isn't a ground face + if ( !( face1->faceflags & FACE_GROUND ) ) { + continue; + } + //the ground plane + plane = &mapplanes[face1->planenum]; + //get the difference between the ground plane and the lowest point + dist = DotProduct( plane->normal, lowestpoint ) - plane->dist; + //if the lowest point is very near one of the ground planes + if ( dist > -1 && dist < 1 ) { + return tmpnode; + } //end if + } //end for + // + dist = DotProduct( normal, lowestpoint ); + planenum = FindFloatPlane( normal, dist, 1, (vec3_t*)&lowestpoint ); + // + w = AAS_SplitWinding( tmparea, planenum ); + if ( !w ) { + return tmpnode; + } + FreeWinding( w ); + //split the area with a horizontal plane through the lowest point + qprintf( "\r%6d", ++numladdersubdivisions ); + // + AAS_SplitArea( tmparea, planenum, &frontarea, &backarea ); + // + tmpnode->tmparea = NULL; + tmpnode->planenum = planenum; + // + tmpnode1 = AAS_AllocTmpNode(); + tmpnode1->planenum = 0; + tmpnode1->tmparea = frontarea; + // + tmpnode2 = AAS_AllocTmpNode(); + tmpnode2->planenum = 0; + tmpnode2->tmparea = backarea; + //subdivide the areas created by splitting recursively + tmpnode->children[0] = AAS_LadderSubdivideArea_r( tmpnode1 ); + tmpnode->children[1] = AAS_LadderSubdivideArea_r( tmpnode2 ); + //refresh the tree + AAS_RefreshLadderSubdividedTree_r( tmpaasworld.nodes, tmparea, tmpnode1, tmpnode2, planenum ); + // + return tmpnode; +} //end of the function AAS_LadderSubdivideArea_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_LadderSubdivision_r( tmp_node_t *tmpnode ) { + //if this is a solid leaf + if ( !tmpnode ) { + return 0; + } + //negative so it's an area + if ( tmpnode->tmparea ) { + return AAS_LadderSubdivideArea_r( tmpnode ); + } + //do the children recursively + tmpnode->children[0] = AAS_LadderSubdivision_r( tmpnode->children[0] ); + tmpnode->children[1] = AAS_LadderSubdivision_r( tmpnode->children[1] ); + return tmpnode; +} //end of the function AAS_LadderSubdivision_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_LadderSubdivision( void ) { + Log_Write( "AAS_LadderSubdivision\r\n" ); + numladdersubdivisions = 0; + qprintf( "%6i ladder subdivisions", numladdersubdivisions ); + //start with the head node + AAS_LadderSubdivision_r( tmpaasworld.nodes ); + // + qprintf( "\n" ); + Log_Write( "%6i ladder subdivisions\r\n", numladdersubdivisions ); +} //end of the function AAS_LadderSubdivision + diff --git a/src/bspc/aas_gsubdiv.h b/src/bspc/aas_gsubdiv.h new file mode 100644 index 0000000..ac7605e --- /dev/null +++ b/src/bspc/aas_gsubdiv.h @@ -0,0 +1,40 @@ +/* +=========================================================================== + +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: aas_gsubdiv.h +// Function: Gravitational subdivision +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +//works with the global tmpaasworld +void AAS_GravitationalSubdivision( void ); +void AAS_LadderSubdivision( void ); diff --git a/src/bspc/aas_map.c b/src/bspc/aas_map.c new file mode 100644 index 0000000..08c3a55 --- /dev/null +++ b/src/bspc/aas_map.c @@ -0,0 +1,886 @@ +/* +=========================================================================== + +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: aas_map.c +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-03 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_mem.h" +#include "..\botlib\aasfile.h" //aas_bbox_t +#include "aas_store.h" //AAS_MAX_BBOXES +#include "aas_cfg.h" +#include "..\game\surfaceflags.h" + +#define SPAWNFLAG_NOT_EASY 0x00000100 +#define SPAWNFLAG_NOT_MEDIUM 0x00000200 +#define SPAWNFLAG_NOT_HARD 0x00000400 +#define SPAWNFLAG_NOT_DEATHMATCH 0x00000800 +#define SPAWNFLAG_NOT_COOP 0x00001000 + +#define STATE_TOP 0 +#define STATE_BOTTOM 1 +#define STATE_UP 2 +#define STATE_DOWN 3 + +#define DOOR_START_OPEN 1 +#define DOOR_REVERSE 2 +#define DOOR_CRUSHER 4 +#define DOOR_NOMONSTER 8 +#define DOOR_TOGGLE 32 +#define DOOR_X_AXIS 64 +#define DOOR_Y_AXIS 128 + +#define BBOX_NORMAL_EPSILON 0.0f //0.00001 + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t BoxOriginDistanceFromPlane( vec3_t normal, vec3_t mins, vec3_t maxs, int side ) { + vec3_t v1, v2; + int i; + + if ( side ) { + for ( i = 0; i < 3; i++ ) + { + if ( normal[i] > BBOX_NORMAL_EPSILON ) { + v1[i] = maxs[i]; + } else if ( normal[i] < -BBOX_NORMAL_EPSILON ) { + v1[i] = mins[i]; + } else { v1[i] = 0;} + } //end for + } //end if + else + { + for ( i = 0; i < 3; i++ ) + { + if ( normal[i] > BBOX_NORMAL_EPSILON ) { + v1[i] = mins[i]; + } else if ( normal[i] < -BBOX_NORMAL_EPSILON ) { + v1[i] = maxs[i]; + } else { v1[i] = 0;} + } //end for + } //end else + VectorCopy( normal, v2 ); + VectorInverse( v2 ); + return DotProduct( v1, v2 ); +} //end of the function BoxOriginDistanceFromPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t CapsuleOriginDistanceFromPlane( vec3_t normal, vec3_t mins, vec3_t maxs ) { + float offset_up, offset_down, width, radius; + + width = maxs[0] - mins[0]; + // if the box is less high then it is wide + if ( maxs[2] - mins[2] < width ) { + width = maxs[2] - mins[2]; + } + radius = width * 0.5; + // offset to upper and lower sphere + offset_up = maxs[2] - radius; + offset_down = -mins[2] - radius; + + // if normal points upward + if ( normal[2] > 0 ) { + // touches lower sphere first + return normal[2] * offset_down + radius; + } else { + // touched upper sphere first + return -normal[2] * offset_up + radius; + } +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ExpandMapBrush( mapbrush_t *brush, vec3_t mins, vec3_t maxs ) { + int sn; + float dist; + side_t *s; + plane_t *plane; + + for ( sn = 0; sn < brush->numsides; sn++ ) { + s = brush->original_sides + sn; + plane = &mapplanes[s->planenum]; + dist = plane->dist; + if ( capsule_collision ) { + dist += CapsuleOriginDistanceFromPlane( plane->normal, mins, maxs ); + } else { + dist += BoxOriginDistanceFromPlane( plane->normal, mins, maxs, 0 ); + } + + s->planenum = FindFloatPlane( plane->normal, dist, 0, NULL ); + + + //the side isn't a bevel after expanding + s->flags &= ~SFL_BEVEL; + //don't skip the surface + s->surf &= ~SURF_SKIP; + //make sure the texinfo is not TEXINFO_NODE + //when player clip contents brushes are read from the bsp tree + //they have the texinfo field set to TEXINFO_NODE + //s->texinfo = 0; + } //end for +} //end of the function AAS_ExpandMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetTexinfo( mapbrush_t *brush ) { + int n; + side_t *side; + + if ( brush->contents & ( CONTENTS_LADDER + | CONTENTS_AREAPORTAL + | CONTENTS_CLUSTERPORTAL + | CONTENTS_TELEPORTER + | CONTENTS_JUMPPAD + | CONTENTS_DONOTENTER + | CONTENTS_DONOTENTER_LARGE + | CONTENTS_WATER + | CONTENTS_LAVA + | CONTENTS_SLIME + | CONTENTS_WINDOW + | CONTENTS_PLAYERCLIP + | CONTENTS_MONSTERCLIP + | CONTENTS_MOVER ) ) { + //we just set texinfo to 0 because these brush sides MUST be used as + //bsp splitters textured or not textured + for ( n = 0; n < brush->numsides; n++ ) + { + side = brush->original_sides + n; + //side->flags |= SFL_TEXTURED|SFL_VISIBLE; + side->texinfo = 0; + } //end for + } //end if + else + { + //only use brush sides as splitters if they are textured + //texinfo of non-textured sides will be set to TEXINFO_NODE + for ( n = 0; n < brush->numsides; n++ ) + { + side = brush->original_sides + n; + //don't use side as splitter (set texinfo to TEXINFO_NODE) if not textured + if ( side->flags & ( SFL_TEXTURED | SFL_BEVEL ) ) { + side->texinfo = 0; + } else { side->texinfo = TEXINFO_NODE;} + } //end for + } //end else +} //end of the function AAS_SetTexinfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeBrushWindings( mapbrush_t *brush ) { + int n; + side_t *side; + // + for ( n = 0; n < brush->numsides; n++ ) + { + side = brush->original_sides + n; + // + if ( side->winding ) { + FreeWinding( side->winding ); + } + } //end for +} //end of the function FreeBrushWindings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddMapBrushSide( mapbrush_t *brush, int planenum ) { + side_t *side; + // + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } + // + side = brush->original_sides + brush->numsides; + side->original = NULL; + side->winding = NULL; + side->contents = brush->contents; + side->flags &= ~( SFL_BEVEL | SFL_VISIBLE ); + side->surf = 0; + side->planenum = planenum; + side->texinfo = 0; + // + nummapbrushsides++; + brush->numsides++; +} //end of the function AAS_AddMapBrushSide +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FixMapBrush( mapbrush_t *brush ) { + int i, j, planenum; + float dist; + winding_t *w; + plane_t *plane, *plane1, *plane2; + side_t *side; + vec3_t normal; + + //calculate the brush bounds + ClearBounds( brush->mins, brush->maxs ); + for ( i = 0; i < brush->numsides; i++ ) + { + plane = &mapplanes[brush->original_sides[i].planenum]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( j = 0; j < brush->numsides && w; j++ ) + { + if ( i == j ) { + continue; + } + //there are no brush bevels marked but who cares :) + if ( brush->original_sides[j].flags & SFL_BEVEL ) { + continue; + } + plane = &mapplanes[brush->original_sides[j].planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } //end for + + side = &brush->original_sides[i]; + side->winding = w; + if ( w ) { + for ( j = 0; j < w->numpoints; j++ ) + { + AddPointToBounds( w->p[j], brush->mins, brush->maxs ); + } //end for + } //end if + } //end for + // + for ( i = 0; i < brush->numsides; i++ ) + { + for ( j = 0; j < brush->numsides; j++ ) + { + if ( i == j ) { + continue; + } + plane1 = &mapplanes[brush->original_sides[i].planenum]; + plane2 = &mapplanes[brush->original_sides[j].planenum]; + if ( WindingsNonConvex( brush->original_sides[i].winding, + brush->original_sides[j].winding, + plane1->normal, plane2->normal, + plane1->dist, plane2->dist ) ) { + Log_Print( "non convex brush" ); + } //end if + } //end for + } //end for + + //NOW close the fucking brush!! + for ( i = 0; i < 3; i++ ) { + if ( brush->mins[i] < -MAX_MAP_BOUNDS ) { + VectorClear( normal ); + normal[i] = -1; + dist = MAX_MAP_BOUNDS - 10; + planenum = FindFloatPlane( normal, dist, 0, NULL ); + + Log_Print( "mins out of range: added extra brush side\n" ); + AAS_AddMapBrushSide( brush, planenum ); + } + + if ( brush->maxs[i] > MAX_MAP_BOUNDS ) { + VectorClear( normal ); + normal[i] = 1; + dist = MAX_MAP_BOUNDS - 10; + planenum = FindFloatPlane( normal, dist, 0, NULL ); + + Log_Print( "maxs out of range: added extra brush side\n" ); + AAS_AddMapBrushSide( brush, planenum ); + } + + if ( brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum ); + } //end if + } //end for + //free all the windings + FreeBrushWindings( brush ); +} //end of the function AAS_FixMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_MakeBrushWindings( mapbrush_t *ob ) { + int i, j; + winding_t *w; + side_t *side; + plane_t *plane, *plane1, *plane2; + + ClearBounds( ob->mins, ob->maxs ); + + for ( i = 0; i < ob->numsides; i++ ) + { + plane = &mapplanes[ob->original_sides[i].planenum]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( j = 0; j < ob->numsides && w; j++ ) + { + if ( i == j ) { + continue; + } + if ( ob->original_sides[j].flags & SFL_BEVEL ) { + continue; + } + plane = &mapplanes[ob->original_sides[j].planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } + + side = &ob->original_sides[i]; + side->winding = w; + if ( w ) { + side->flags |= SFL_VISIBLE; + for ( j = 0; j < w->numpoints; j++ ) + AddPointToBounds( w->p[j], ob->mins, ob->maxs ); + } + } + //check if the brush is convex + for ( i = 0; i < ob->numsides; i++ ) + { + for ( j = 0; j < ob->numsides; j++ ) + { + if ( i == j ) { + continue; + } + plane1 = &mapplanes[ob->original_sides[i].planenum]; + plane2 = &mapplanes[ob->original_sides[j].planenum]; + if ( WindingsNonConvex( ob->original_sides[i].winding, + ob->original_sides[j].winding, + plane1->normal, plane2->normal, + plane1->dist, plane2->dist ) ) { + Log_Print( "non convex brush" ); + } //end if + } //end for + } //end for + //check for out of bound brushes + for ( i = 0; i < 3; i++ ) + { + //IDBUG: all the indexes into the mins and maxs were zero (not using i) + if ( ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum ); + Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i] ); + ob->numsides = 0; //remove the brush + break; + } //end if + if ( ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum ); + Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, ob->mins[i], i, ob->maxs[i] ); + ob->numsides = 0; //remove the brush + break; + } //end if + } //end for + return true; +} //end of the function AAS_MakeBrushWindings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +mapbrush_t *AAS_CopyMapBrush( mapbrush_t *brush, entity_t *mapent ) { + int n; + mapbrush_t *newbrush; + side_t *side, *newside; + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "MAX_MAPFILE_BRUSHES" ); + } + + newbrush = &mapbrushes[nummapbrushes]; + newbrush->original_sides = &brushsides[nummapbrushsides]; + newbrush->entitynum = brush->entitynum; + newbrush->brushnum = nummapbrushes - mapent->firstbrush; + newbrush->numsides = brush->numsides; + newbrush->contents = brush->contents; + + //copy the sides + for ( n = 0; n < brush->numsides; n++ ) + { + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } + side = brush->original_sides + n; + + newside = newbrush->original_sides + n; + newside->original = NULL; + newside->winding = NULL; + newside->contents = side->contents; + newside->flags = side->flags; + newside->surf = side->surf; + newside->planenum = side->planenum; + newside->texinfo = side->texinfo; + nummapbrushsides++; + } //end for + // + nummapbrushes++; + mapent->numbrushes++; + return newbrush; +} //end of the function AAS_CopyMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_AlwaysTriggered( char *targetname ) { + int i; + + if ( !strlen( targetname ) ) { + return false; + } + // + for ( i = 0; i < num_entities; i++ ) + { + //if the entity will activate the given targetname + if ( !strcmp( targetname, ValueForKey( &entities[i], "target" ) ) ) { + //if this activator is present in deathmatch + if ( !( atoi( ValueForKey( &entities[i], "spawnflags" ) ) & SPAWNFLAG_NOT_DEATHMATCH ) ) { + //if it is a trigger_always entity + if ( !strcmp( "trigger_always", ValueForKey( &entities[i], "classname" ) ) ) { + return true; + } //end if + //check for possible trigger_always entities activating this entity + if ( AAS_AlwaysTriggered( ValueForKey( &entities[i], "targetname" ) ) ) { + return true; + } //end if + } //end if + } //end if + } //end for + return false; +} //end of the function AAS_AlwaysTriggered +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_ValidEntity( entity_t *mapent ) { + int i; + char target[1024]; + + //all world brushes are used for AAS + if ( mapent == &entities[0] ) { + return true; + } //end if + //some of the func_wall brushes are also used for AAS + else if ( !strcmp( "func_wall", ValueForKey( mapent, "classname" ) ) ) { + //Log_Print("found func_wall entity %d\n", mapent - entities); + //if the func wall is used in deathmatch + //if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) + { + //Log_Print("func_wall USED in deathmatch mode %d\n", atoi(ValueForKey(mapent, "spawnflags"))); + return true; + } //end if + } //end else if + else if ( !strcmp( "func_door_rotating", ValueForKey( mapent, "classname" ) ) || + !strcmp( "func_door", ValueForKey( mapent, "classname" ) ) || + !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) { + //if the func_door_rotating is present in deathmatch + //if (!(atoi(ValueForKey(mapent, "spawnflags")) & SPAWNFLAG_NOT_DEATHMATCH)) + { + //if the func_door_rotating is always activated in deathmatch + if ( AAS_AlwaysTriggered( ValueForKey( mapent, "targetname" ) ) ) { + //Log_Print("found func_door_rotating in deathmatch\ntargetname %s\n", ValueForKey(mapent, "targetname")); + return true; + } //end if + } //end if + } //end else if + else if ( !strcmp( "trigger_hurt", ValueForKey( mapent, "classname" ) ) ) { + // RF, spawnflag & 1 is for delayed spawn, so ignore it + if ( atoi( ValueForKey( mapent, "spawnflags" ) ) & 1 ) { + return false; + } + + //"dmg" is the damage, for instance: "dmg" "666" + return false; + } //end else if + else if ( !strcmp( "trigger_push", ValueForKey( mapent, "classname" ) ) ) { + return true; + } //end else if + else if ( !strcmp( "trigger_multiple", ValueForKey( mapent, "classname" ) ) ) { + //find out if the trigger_multiple is pointing to a target_teleporter + strcpy( target, ValueForKey( mapent, "target" ) ); + for ( i = 0; i < num_entities; i++ ) + { + //if the entity will activate the given targetname + if ( !strcmp( target, ValueForKey( &entities[i], "targetname" ) ) ) { + if ( !strcmp( "target_teleporter", ValueForKey( &entities[i], "classname" ) ) ) { + return true; + } //end if + } //end if + } //end for + } //end else if + else if ( !strcmp( "trigger_teleport", ValueForKey( mapent, "classname" ) ) ) { + return true; + } //end else if + else if ( !strcmp( "func_tramcar", ValueForKey( mapent, "classname" ) ) ) { + return true; + } //end else if + else if ( !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) { + return true; + } else if ( !strcmp( "func_static", ValueForKey( mapent, "classname" ) ) ) { + //FIXME: easy/medium/hard/deathmatch specific? + return true; + } //end else if + // RF, missionpack + else if ( !strcmp( "func_constructible", ValueForKey( mapent, "classname" ) ) ) { +// RF, debugging + if ( strcmp( "reardump", ValueForKey( mapent, "targetname" ) ) ) { + Log_Print( "\n\n\nTEMP!!!\nTEMP!!!\nIgnoring all func_constructibles except \"reardump\"\nTEMP!!!\nTEMP!!!\n\n\n" ); + return false; + } + + if ( ( atoi( ValueForKey( mapent, "spawnflags" ) ) & 512 ) ) { + // ignored by AAS + return false; + } + // constructibles are used now by default + return true; + } //end else if + else if ( !strcmp( "func_explosive", ValueForKey( mapent, "classname" ) ) ) { + // explosives are now used as MOVER so they only disable areas inside them + return true; + } //end else if + + return false; +} //end of the function AAS_ValidEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_TransformPlane( int planenum, vec3_t origin, vec3_t angles ) { + float newdist, matrix[3][3]; + vec3_t normal; + + //rotate the node plane + VectorCopy( mapplanes[planenum].normal, normal ); + CreateRotationMatrix( angles, matrix ); + RotatePoint( normal, matrix ); + newdist = mapplanes[planenum].dist + DotProduct( normal, origin ); + + return FindFloatPlane( normal, newdist, 0, NULL ); +} //end of the function AAS_TransformPlane +//=========================================================================== +// this function sets the func_rotating_door in it's final position +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PositionFuncRotatingBrush( entity_t *mapent, mapbrush_t *brush ) { + int spawnflags, i; + float distance; + vec3_t movedir, angles, pos1, pos2; + side_t *s; + + spawnflags = FloatForKey( mapent, "spawnflags" ); + VectorClear( movedir ); + if ( spawnflags & DOOR_X_AXIS ) { + movedir[2] = 1.0; //roll + } else if ( spawnflags & DOOR_Y_AXIS ) { + movedir[0] = 1.0; //pitch + } else { // Z_AXIS + movedir[1] = 1.0; //yaw + + } + // check for reverse rotation + if ( spawnflags & DOOR_REVERSE ) { + VectorInverse( movedir ); + } + + distance = FloatForKey( mapent, "distance" ); + if ( !distance ) { + distance = 90; + } + + GetVectorForKey( mapent, "angles", angles ); + VectorCopy( angles, pos1 ); + VectorMA( angles, -distance, movedir, pos2 ); + // if it starts open, switch the positions + if ( spawnflags & DOOR_START_OPEN ) { + VectorCopy( pos2, angles ); + VectorCopy( pos1, pos2 ); + VectorCopy( angles, pos1 ); + VectorInverse( movedir ); + } //end if + // + for ( i = 0; i < brush->numsides; i++ ) + { + s = &brush->original_sides[i]; + s->planenum = AAS_TransformPlane( s->planenum, mapent->origin, pos2 ); + } //end for + // + FreeBrushWindings( brush ); + AAS_MakeBrushWindings( brush ); + AddBrushBevels( brush ); + FreeBrushWindings( brush ); +} //end of the function AAS_PositionFuncRotatingBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PositionBrush( entity_t *mapent, mapbrush_t *brush ) { + side_t *s; + float newdist; + int i; + char *model; + + if ( !strcmp( ValueForKey( mapent, "classname" ), "func_door_rotating" ) ) { + AAS_PositionFuncRotatingBrush( mapent, brush ); + } else { + if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) { + for ( i = 0; i < brush->numsides; i++ ) { + s = &brush->original_sides[i]; + + newdist = mapplanes[s->planenum].dist + DotProduct( mapplanes[s->planenum].normal, mapent->origin ); + + s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist, 0, NULL ); + } + } + } + + // RF, disabled for Wolf, we dont use trigger_hurt for lava + //if it's a trigger hurt + //if (!strcmp("trigger_hurt", ValueForKey(mapent, "classname"))) + //{ + // //set the lava contents + // brush->contents |= CONTENTS_LAVA; + // //Log_Print("found trigger_hurt brush\n"); + //} //end if + // + if ( !strcmp( "trigger_push", ValueForKey( mapent, "classname" ) ) ) { + //set the jumppad contents + brush->contents = CONTENTS_JUMPPAD; + //Log_Print("found trigger_push brush\n"); + } //end if + // + else if ( !strcmp( "trigger_multiple", ValueForKey( mapent, "classname" ) ) ) { + //set teleporter contents + brush->contents = CONTENTS_TELEPORTER; + //Log_Print("found trigger_multiple teleporter brush\n"); + } //end if + // + else if ( !strcmp( "trigger_teleport", ValueForKey( mapent, "classname" ) ) ) { + //set teleporter contents + brush->contents = CONTENTS_TELEPORTER; + //Log_Print("found trigger_teleport teleporter brush\n"); + } //end if + else if ( !strcmp( "func_door", ValueForKey( mapent, "classname" ) ) || !strcmp( "func_door_rotating", ValueForKey( mapent, "classname" ) ) ) { + //set mover contents + brush->contents = CONTENTS_MOVER; + //get the model number + model = ValueForKey( mapent, "model" ); + brush->modelnum = atoi( model + 1 ); + } //end if + else if ( !strcmp( "func_invisible_user", ValueForKey( mapent, "classname" ) ) ) { + //set mover contents + brush->contents = CONTENTS_TRIGGER; + } //end if + // RF + else if ( !strcmp( "func_explosive", ValueForKey( mapent, "classname" ) ) ) { + // Gordon: only if the noaasblock flag isnt set + if ( !( atoi( ValueForKey( mapent, "spawnflags" ) ) & 16 ) ) { + //set mover contents + brush->contents = CONTENTS_MOVER; + //get the model number + model = ValueForKey( mapent, "model" ); + brush->modelnum = atoi( model + 1 ); + } + } //end if + else if ( !strcmp( "func_constructible", ValueForKey( mapent, "classname" ) ) ) { + // is it a blocking constructible + if ( ( atoi( ValueForKey( mapent, "spawnflags" ) ) & 128 ) ) { + // blocking constructible + if ( !( atoi( ValueForKey( mapent, "spawnflags" ) ) & 512 ) ) { + // not ignored by AAS + //set mover contents + brush->contents = CONTENTS_MOVER; + //get the model number + model = ValueForKey( mapent, "model" ); + brush->modelnum = atoi( model + 1 ); + } + } + } //end if + +} //end of the function AAS_PositionBrush +//=========================================================================== +// uses the global cfg_t cfg +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_CreateMapBrushes( mapbrush_t *brush, entity_t *mapent, int addbevels ) { + int i; + //side_t *s; + mapbrush_t *bboxbrushes[16]; + + //if the brushes are not from an entity used for AAS + if ( !AAS_ValidEntity( mapent ) ) { + nummapbrushsides -= brush->numsides; + brush->numsides = 0; + return; + } //end if + // + AAS_PositionBrush( mapent, brush ); + //from all normal solid brushes only the textured brush sides will + //be used as bsp splitters, so set the right texinfo reference here + AAS_SetTexinfo( brush ); + //remove contents detail flag, otherwise player clip contents won't be + //bsped correctly for AAS! + brush->contents &= ~CONTENTS_DETAIL; + //if the brush has contents area portal it should be the only contents + if ( brush->contents & ( CONTENTS_AREAPORTAL | CONTENTS_CLUSTERPORTAL ) ) { + brush->contents = CONTENTS_CLUSTERPORTAL; + brush->leafnum = -1; + } //end if + //window and playerclip are used for player clipping, make them solid + if ( brush->contents & ( CONTENTS_WINDOW | CONTENTS_PLAYERCLIP ) ) { + // + brush->contents &= ~( CONTENTS_WINDOW | CONTENTS_PLAYERCLIP ); + brush->contents |= CONTENTS_SOLID; + brush->leafnum = -1; + } //end if + // +// Rafael TBD: no flag to support CONTENTS_BOTCLIP +/* + if (brush->contents & CONTENTS_BOTCLIP) + { + brush->contents = CONTENTS_SOLID; + brush->leafnum = -1; + } // end if +*/ + // + //Log_Write("brush %d contents = ", brush->brushnum); + //PrintContents(brush->contents); + //Log_Write("\r\n"); + //if not one of the following brushes then the brush is NOT used for AAS + if ( !( brush->contents & ( CONTENTS_SOLID + | CONTENTS_LADDER + | CONTENTS_CLUSTERPORTAL + | CONTENTS_DONOTENTER + | CONTENTS_DONOTENTER_LARGE + | CONTENTS_TELEPORTER + | CONTENTS_JUMPPAD + | CONTENTS_WATER + | CONTENTS_LAVA + | CONTENTS_SLIME + | CONTENTS_MOVER + ) ) ) { + nummapbrushsides -= brush->numsides; + brush->numsides = 0; + return; + } //end if + //fix the map brush + //AAS_FixMapBrush(brush); + //if brush bevels should be added (for real map brushes, not bsp map brushes) + if ( addbevels ) { + //NOTE: we first have to get the mins and maxs of the brush before + // creating the brush bevels... the mins and maxs are used to + // create them. so we call MakeBrushWindings to get the mins + // and maxs and then after creating the bevels we free the + // windings because they are created for all sides (including + // bevels) a little later + AAS_MakeBrushWindings( brush ); + AddBrushBevels( brush ); + FreeBrushWindings( brush ); + } //end if + //NOTE: add the brush to the WORLD entity!!! + mapent = &entities[0]; + //there's at least one new brush for now + nummapbrushes++; + mapent->numbrushes++; + //liquid brushes are expanded for the maximum possible bounding box + if ( brush->contents & ( CONTENTS_WATER + | CONTENTS_LAVA + | CONTENTS_SLIME + | CONTENTS_TELEPORTER + | CONTENTS_JUMPPAD + | CONTENTS_DONOTENTER + | CONTENTS_DONOTENTER_LARGE + | CONTENTS_MOVER + ) ) { + brush->expansionbbox = 0; + //NOTE: the first bounding box is the max + //FIXME: use max bounding box created from all bboxes + AAS_ExpandMapBrush( brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs ); + AAS_MakeBrushWindings( brush ); + } //end if + //area portal brushes are NOT expanded + else if ( brush->contents & CONTENTS_CLUSTERPORTAL ) { + brush->expansionbbox = 0; + //NOTE: the first bounding box is the max + //FIXME: use max bounding box created from all bboxes + AAS_ExpandMapBrush( brush, cfg.bboxes[0].mins, cfg.bboxes[0].maxs ); + AAS_MakeBrushWindings( brush ); + } //end if + //all solid brushes are expanded for all bounding boxes + else if ( brush->contents & ( CONTENTS_SOLID + | CONTENTS_LADDER + ) ) { + //brush for the first bounding box + bboxbrushes[0] = brush; + //make a copy for the other bounding boxes + for ( i = 1; i < cfg.numbboxes; i++ ) + { + bboxbrushes[i] = AAS_CopyMapBrush( brush, mapent ); + } //end for + //expand every brush for it's bounding box and create windings + for ( i = 0; i < cfg.numbboxes; i++ ) + { + AAS_ExpandMapBrush( bboxbrushes[i], cfg.bboxes[i].mins, cfg.bboxes[i].maxs ); + bboxbrushes[i]->expansionbbox = cfg.bboxes[i].presencetype; + AAS_MakeBrushWindings( bboxbrushes[i] ); + } //end for + } //end else +} //end of the function AAS_CreateMapBrushes diff --git a/src/bspc/aas_map.h b/src/bspc/aas_map.h new file mode 100644 index 0000000..65c420c --- /dev/null +++ b/src/bspc/aas_map.h @@ -0,0 +1,38 @@ +/* +=========================================================================== + +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: aas_map.h +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +void AAS_CreateMapBrushes( mapbrush_t *brush, entity_t *mapent, int addbevels ); diff --git a/src/bspc/aas_prunenodes.c b/src/bspc/aas_prunenodes.c new file mode 100644 index 0000000..07640f0 --- /dev/null +++ b/src/bspc/aas_prunenodes.c @@ -0,0 +1,103 @@ +/* +=========================================================================== + +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: aas_prunenodes.c +// Function: Prune Nodes +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_create.h" + +int c_numprunes; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tmp_node_t *AAS_PruneNodes_r( tmp_node_t *tmpnode ) { + tmp_area_t *tmparea1, *tmparea2; + + //if it is a solid leaf + if ( !tmpnode ) { + return NULL; + } + // + if ( tmpnode->tmparea ) { + return tmpnode; + } + //process the children first + tmpnode->children[0] = AAS_PruneNodes_r( tmpnode->children[0] ); + tmpnode->children[1] = AAS_PruneNodes_r( tmpnode->children[1] ); + //if both children are areas + if ( tmpnode->children[0] && tmpnode->children[1] && + tmpnode->children[0]->tmparea && tmpnode->children[1]->tmparea ) { + tmparea1 = tmpnode->children[0]->tmparea; + while ( tmparea1->mergedarea ) tmparea1 = tmparea1->mergedarea; + + tmparea2 = tmpnode->children[1]->tmparea; + while ( tmparea2->mergedarea ) tmparea2 = tmparea2->mergedarea; + + if ( tmparea1 == tmparea2 ) { + c_numprunes++; + tmpnode->tmparea = tmparea1; + tmpnode->planenum = 0; + AAS_FreeTmpNode( tmpnode->children[0] ); + AAS_FreeTmpNode( tmpnode->children[1] ); + tmpnode->children[0] = NULL; + tmpnode->children[1] = NULL; + return tmpnode; + } //end if + } //end if + //if both solid leafs + if ( !tmpnode->children[0] && !tmpnode->children[1] ) { + c_numprunes++; + AAS_FreeTmpNode( tmpnode ); + return NULL; + } //end if + // + return tmpnode; +} //end of the function AAS_PruneNodes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_PruneNodes( void ) { + Log_Write( "AAS_PruneNodes\r\n" ); + AAS_PruneNodes_r( tmpaasworld.nodes ); + Log_Print( "%6d nodes pruned\r\n", c_numprunes ); +} //end of the function AAS_PruneNodes diff --git a/src/bspc/aas_prunenodes.h b/src/bspc/aas_prunenodes.h new file mode 100644 index 0000000..e4af91a --- /dev/null +++ b/src/bspc/aas_prunenodes.h @@ -0,0 +1,39 @@ +/* +=========================================================================== + +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: aas_prunenodes.h +// Function: Prune Nodes +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +void AAS_PruneNodes( void ); + diff --git a/src/bspc/aas_store.c b/src/bspc/aas_store.c new file mode 100644 index 0000000..260063f --- /dev/null +++ b/src/bspc/aas_store.c @@ -0,0 +1,1124 @@ +/* +=========================================================================== + +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: aas_store.c +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "..\botlib\aasfile.h" +#include "aas_file.h" +#include "aas_store.h" +#include "aas_create.h" +#include "aas_cfg.h" + + +//#define NOTHREEVERTEXFACES + +#define STOREPLANESDOUBLE + +#define VERTEX_EPSILON 0.1 //NOTE: changed from 0.5 + +// MrE: use same epsilons as q3map! + +//NOTE: changed from 0.0001 +#define NORMAL_EPSILON 0.00001 +//NOTE: changed from 0.05 +#define DIST_EPSILON 0.01 + +#define INTEGRAL_EPSILON 0.01 +#define VEREX_EPSILON 0.1 //NOTE: changed from 0.5 +#define VERTEX_HASHING +#define VERTEX_HASH_SHIFT 7 +#define VERTEX_HASH_SIZE ( ( MAX_MAP_BOUNDS >> ( VERTEX_HASH_SHIFT - 1 ) ) + 1 ) //was 64 +// +#define PLANE_HASHING +#define PLANE_HASH_SIZE 1024 //must be power of 2 +// +#define EDGE_HASHING +#define EDGE_HASH_SIZE 1024 //must be power of 2 + +// Ridah +aas_t aasworlds[1]; +aas_t( *aasworld ); +// done. + +//vertex hash +int *aas_vertexchain; // the next vertex in a hash chain +int aas_hashverts[VERTEX_HASH_SIZE * VERTEX_HASH_SIZE]; // a vertex number, or 0 for no verts +//plane hash +int *aas_planechain; +int aas_hashplanes[PLANE_HASH_SIZE]; +//edge hash +int *aas_edgechain; +int aas_hashedges[EDGE_HASH_SIZE]; + +int allocatedaasmem = 0; + +int groundfacesonly = false; //true; +// +typedef struct max_aas_s +{ + int max_bboxes; + int max_vertexes; + int max_planes; + int max_edges; + int max_edgeindexsize; + int max_faces; + int max_faceindexsize; + int max_areas; + int max_areasettings; + int max_reachabilitysize; + int max_nodes; + int max_portals; + int max_portalindexsize; + int max_clusters; +} max_aas_t; +//maximums of everything +max_aas_t max_aas; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_CountTmpNodes( tmp_node_t *tmpnode ) { + if ( !tmpnode ) { + return 0; + } + return AAS_CountTmpNodes( tmpnode->children[0] ) + + AAS_CountTmpNodes( tmpnode->children[1] ) + 1; +} //end of the function AAS_CountTmpNodes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitMaxAAS( void ) { + int numfaces, numpoints, numareas; + tmp_face_t *f; + tmp_area_t *a; + + numpoints = 0; + numfaces = 0; + for ( f = tmpaasworld.faces; f; f = f->l_next ) + { + numfaces++; + if ( f->winding ) { + numpoints += f->winding->numpoints; + } + } //end for + // + numareas = 0; + for ( a = tmpaasworld.areas; a; a = a->l_next ) + { + numareas++; + } //end for + max_aas.max_bboxes = AAS_MAX_BBOXES; + max_aas.max_vertexes = numpoints + 1; + max_aas.max_planes = nummapplanes + 1024; + max_aas.max_edges = numpoints + 1; + max_aas.max_edgeindexsize = ( numpoints + 1 ) * 3; + max_aas.max_faces = numfaces + 10; + max_aas.max_faceindexsize = ( numfaces + 10 ) * 2; + max_aas.max_areas = numareas + 10; + max_aas.max_areasettings = numareas + 10; + max_aas.max_reachabilitysize = 0; + max_aas.max_nodes = AAS_CountTmpNodes( tmpaasworld.nodes ) + 10; + max_aas.max_portals = 0; + max_aas.max_portalindexsize = 0; + max_aas.max_clusters = 0; +} //end of the function AAS_InitMaxAAS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AllocMaxAAS( void ) { + int i; + + AAS_InitMaxAAS(); + //bounding boxes + ( *aasworld ).numbboxes = 0; + ( *aasworld ).bboxes = (aas_bbox_t *) GetClearedMemory( max_aas.max_bboxes * sizeof( aas_bbox_t ) ); + allocatedaasmem += max_aas.max_bboxes * sizeof( aas_bbox_t ); + //vertexes + ( *aasworld ).numvertexes = 0; + ( *aasworld ).vertexes = (aas_vertex_t *) GetClearedMemory( max_aas.max_vertexes * sizeof( aas_vertex_t ) ); + allocatedaasmem += max_aas.max_vertexes * sizeof( aas_vertex_t ); + //planes + ( *aasworld ).numplanes = 0; + ( *aasworld ).planes = (aas_plane_t *) GetClearedMemory( max_aas.max_planes * sizeof( aas_plane_t ) ); + allocatedaasmem += max_aas.max_planes * sizeof( aas_plane_t ); + //edges + ( *aasworld ).numedges = 0; + ( *aasworld ).edges = (aas_edge_t *) GetClearedMemory( max_aas.max_edges * sizeof( aas_edge_t ) ); + allocatedaasmem += max_aas.max_edges * sizeof( aas_edge_t ); + //edge index + ( *aasworld ).edgeindexsize = 0; + ( *aasworld ).edgeindex = (aas_edgeindex_t *) GetClearedMemory( max_aas.max_edgeindexsize * sizeof( aas_edgeindex_t ) ); + allocatedaasmem += max_aas.max_edgeindexsize * sizeof( aas_edgeindex_t ); + //faces + ( *aasworld ).numfaces = 0; + ( *aasworld ).faces = (aas_face_t *) GetClearedMemory( max_aas.max_faces * sizeof( aas_face_t ) ); + allocatedaasmem += max_aas.max_faces * sizeof( aas_face_t ); + //face index + ( *aasworld ).faceindexsize = 0; + ( *aasworld ).faceindex = (aas_faceindex_t *) GetClearedMemory( max_aas.max_faceindexsize * sizeof( aas_faceindex_t ) ); + allocatedaasmem += max_aas.max_faceindexsize * sizeof( aas_faceindex_t ); + //convex areas + ( *aasworld ).numareas = 0; + ( *aasworld ).areas = (aas_area_t *) GetClearedMemory( max_aas.max_areas * sizeof( aas_area_t ) ); + allocatedaasmem += max_aas.max_areas * sizeof( aas_area_t ); + //convex area settings + ( *aasworld ).numareasettings = 0; + ( *aasworld ).areasettings = (aas_areasettings_t *) GetClearedMemory( max_aas.max_areasettings * sizeof( aas_areasettings_t ) ); + allocatedaasmem += max_aas.max_areasettings * sizeof( aas_areasettings_t ); + //reachablity list + ( *aasworld ).reachabilitysize = 0; + ( *aasworld ).reachability = (aas_reachability_t *) GetClearedMemory( max_aas.max_reachabilitysize * sizeof( aas_reachability_t ) ); + allocatedaasmem += max_aas.max_reachabilitysize * sizeof( aas_reachability_t ); + //nodes of the bsp tree + ( *aasworld ).numnodes = 0; + ( *aasworld ).nodes = (aas_node_t *) GetClearedMemory( max_aas.max_nodes * sizeof( aas_node_t ) ); + allocatedaasmem += max_aas.max_nodes * sizeof( aas_node_t ); + //cluster portals + ( *aasworld ).numportals = 0; + ( *aasworld ).portals = (aas_portal_t *) GetClearedMemory( max_aas.max_portals * sizeof( aas_portal_t ) ); + allocatedaasmem += max_aas.max_portals * sizeof( aas_portal_t ); + //cluster portal index + ( *aasworld ).portalindexsize = 0; + ( *aasworld ).portalindex = (aas_portalindex_t *) GetClearedMemory( max_aas.max_portalindexsize * sizeof( aas_portalindex_t ) ); + allocatedaasmem += max_aas.max_portalindexsize * sizeof( aas_portalindex_t ); + //cluster + ( *aasworld ).numclusters = 0; + ( *aasworld ).clusters = (aas_cluster_t *) GetClearedMemory( max_aas.max_clusters * sizeof( aas_cluster_t ) ); + allocatedaasmem += max_aas.max_clusters * sizeof( aas_cluster_t ); + // + Log_Print( "allocated " ); + PrintMemorySize( allocatedaasmem ); + Log_Print( " of AAS memory\n" ); + //reset the has stuff + aas_vertexchain = (int *) GetClearedMemory( max_aas.max_vertexes * sizeof( int ) ); + aas_planechain = (int *) GetClearedMemory( max_aas.max_planes * sizeof( int ) ); + aas_edgechain = (int *) GetClearedMemory( max_aas.max_edges * sizeof( int ) ); + // + for ( i = 0; i < max_aas.max_vertexes; i++ ) aas_vertexchain[i] = -1; + for ( i = 0; i < VERTEX_HASH_SIZE * VERTEX_HASH_SIZE; i++ ) aas_hashverts[i] = -1; + // + for ( i = 0; i < max_aas.max_planes; i++ ) aas_planechain[i] = -1; + for ( i = 0; i < PLANE_HASH_SIZE; i++ ) aas_hashplanes[i] = -1; + // + for ( i = 0; i < max_aas.max_edges; i++ ) aas_edgechain[i] = -1; + for ( i = 0; i < EDGE_HASH_SIZE; i++ ) aas_hashedges[i] = -1; +} //end of the function AAS_AllocMaxAAS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_FreeMaxAAS( void ) { + //bounding boxes + if ( ( *aasworld ).bboxes ) { + FreeMemory( ( *aasworld ).bboxes ); + } + ( *aasworld ).bboxes = NULL; + ( *aasworld ).numbboxes = 0; + //vertexes + if ( ( *aasworld ).vertexes ) { + FreeMemory( ( *aasworld ).vertexes ); + } + ( *aasworld ).vertexes = NULL; + ( *aasworld ).numvertexes = 0; + //planes + if ( ( *aasworld ).planes ) { + FreeMemory( ( *aasworld ).planes ); + } + ( *aasworld ).planes = NULL; + ( *aasworld ).numplanes = 0; + //edges + if ( ( *aasworld ).edges ) { + FreeMemory( ( *aasworld ).edges ); + } + ( *aasworld ).edges = NULL; + ( *aasworld ).numedges = 0; + //edge index + if ( ( *aasworld ).edgeindex ) { + FreeMemory( ( *aasworld ).edgeindex ); + } + ( *aasworld ).edgeindex = NULL; + ( *aasworld ).edgeindexsize = 0; + //faces + if ( ( *aasworld ).faces ) { + FreeMemory( ( *aasworld ).faces ); + } + ( *aasworld ).faces = NULL; + ( *aasworld ).numfaces = 0; + //face index + if ( ( *aasworld ).faceindex ) { + FreeMemory( ( *aasworld ).faceindex ); + } + ( *aasworld ).faceindex = NULL; + ( *aasworld ).faceindexsize = 0; + //convex areas + if ( ( *aasworld ).areas ) { + FreeMemory( ( *aasworld ).areas ); + } + ( *aasworld ).areas = NULL; + ( *aasworld ).numareas = 0; + //convex area settings + if ( ( *aasworld ).areasettings ) { + FreeMemory( ( *aasworld ).areasettings ); + } + ( *aasworld ).areasettings = NULL; + ( *aasworld ).numareasettings = 0; + //reachablity list + if ( ( *aasworld ).reachability ) { + FreeMemory( ( *aasworld ).reachability ); + } + ( *aasworld ).reachability = NULL; + ( *aasworld ).reachabilitysize = 0; + //nodes of the bsp tree + if ( ( *aasworld ).nodes ) { + FreeMemory( ( *aasworld ).nodes ); + } + ( *aasworld ).nodes = NULL; + ( *aasworld ).numnodes = 0; + //cluster portals + if ( ( *aasworld ).portals ) { + FreeMemory( ( *aasworld ).portals ); + } + ( *aasworld ).portals = NULL; + ( *aasworld ).numportals = 0; + //cluster portal index + if ( ( *aasworld ).portalindex ) { + FreeMemory( ( *aasworld ).portalindex ); + } + ( *aasworld ).portalindex = NULL; + ( *aasworld ).portalindexsize = 0; + //clusters + if ( ( *aasworld ).clusters ) { + FreeMemory( ( *aasworld ).clusters ); + } + ( *aasworld ).clusters = NULL; + ( *aasworld ).numclusters = 0; + + Log_Print( "freed " ); + PrintMemorySize( allocatedaasmem ); + Log_Print( " of AAS memory\n" ); + allocatedaasmem = 0; + // + if ( aas_vertexchain ) { + FreeMemory( aas_vertexchain ); + } + aas_vertexchain = NULL; + if ( aas_planechain ) { + FreeMemory( aas_planechain ); + } + aas_planechain = NULL; + if ( aas_edgechain ) { + FreeMemory( aas_edgechain ); + } + aas_edgechain = NULL; +} //end of the function AAS_FreeMaxAAS +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned AAS_HashVec( vec3_t vec ) { + int x, y; + + x = ( MAX_MAP_BOUNDS + (int)( vec[0] + 0.5 ) ) >> VERTEX_HASH_SHIFT; + y = ( MAX_MAP_BOUNDS + (int)( vec[1] + 0.5 ) ) >> VERTEX_HASH_SHIFT; + + if ( x < 0 || x >= VERTEX_HASH_SIZE || y < 0 || y >= VERTEX_HASH_SIZE ) { + Log_Print( "WARNING! HashVec: point %f %f %f outside valid range\n", vec[0], vec[1], vec[2] ); + Log_Print( "This should never happen!\n" ); + return -1; + } //end if + + return y * VERTEX_HASH_SIZE + x; +} //end of the function AAS_HashVec +//=========================================================================== +// returns true if the vertex was found in the list +// stores the vertex number in *vnum +// stores a new vertex if not stored already +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_GetVertex( vec3_t v, int *vnum ) { + int i; +#ifndef VERTEX_HASHING + float diff; +#endif //VERTEX_HASHING + +#ifdef VERTEX_HASHING + int h, vn; + vec3_t vert; + + for ( i = 0; i < 3; i++ ) + { + if ( fabs( v[i] - Q_rint( v[i] ) ) < INTEGRAL_EPSILON ) { + vert[i] = Q_rint( v[i] ); + } else { + vert[i] = v[i]; + } + } //end for + + h = AAS_HashVec( vert ); + //if the vertex was outside the valid range + if ( h == -1 ) { + *vnum = -1; + return true; + } //end if + + for ( vn = aas_hashverts[h]; vn >= 0; vn = aas_vertexchain[vn] ) + { + if ( fabs( ( *aasworld ).vertexes[vn][0] - vert[0] ) < VEREX_EPSILON + && fabs( ( *aasworld ).vertexes[vn][1] - vert[1] ) < VEREX_EPSILON + && fabs( ( *aasworld ).vertexes[vn][2] - vert[2] ) < VEREX_EPSILON ) { + *vnum = vn; + return true; + } //end if + } //end for +#else //VERTEX_HASHING + //check if the vertex is already stored + //stupid linear search + for ( i = 0; i < ( *aasworld ).numvertexes; i++ ) + { + diff = vert[0] - ( *aasworld ).vertexes[i][0]; + if ( diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON ) { + diff = vert[1] - ( *aasworld ).vertexes[i][1]; + if ( diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON ) { + diff = vert[2] - ( *aasworld ).vertexes[i][2]; + if ( diff < VERTEX_EPSILON && diff > -VERTEX_EPSILON ) { + *vnum = i; + return true; + } //end if + } //end if + } //end if + } //end for +#endif //VERTEX_HASHING + + if ( ( *aasworld ).numvertexes >= max_aas.max_vertexes ) { + Error( "AAS_MAX_VERTEXES = %d", max_aas.max_vertexes ); + } //end if + VectorCopy( vert, ( *aasworld ).vertexes[( *aasworld ).numvertexes] ); + *vnum = ( *aasworld ).numvertexes; + +#ifdef VERTEX_HASHING + aas_vertexchain[( *aasworld ).numvertexes] = aas_hashverts[h]; + aas_hashverts[h] = ( *aasworld ).numvertexes; +#endif //VERTEX_HASHING + + ( *aasworld ).numvertexes++; + return false; +} //end of the function AAS_GetVertex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +unsigned AAS_HashEdge( int v1, int v2 ) { + int vnum1, vnum2; + // + if ( v1 < v2 ) { + vnum1 = v1; + vnum2 = v2; + } //end if + else + { + vnum1 = v2; + vnum2 = v1; + } //end else + return ( vnum1 + vnum2 ) & ( EDGE_HASH_SIZE - 1 ); +} //end of the function AAS_HashVec +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddEdgeToHash( int edgenum ) { + int hash; + aas_edge_t *edge; + + edge = &( *aasworld ).edges[edgenum]; + + hash = AAS_HashEdge( edge->v[0], edge->v[1] ); + + aas_edgechain[edgenum] = aas_hashedges[hash]; + aas_hashedges[hash] = edgenum; +} //end of the function AAS_AddEdgeToHash +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_FindHashedEdge( int v1num, int v2num, int *edgenum ) { + int e, hash; + aas_edge_t *edge; + + hash = AAS_HashEdge( v1num, v2num ); + for ( e = aas_hashedges[hash]; e >= 0; e = aas_edgechain[e] ) + { + edge = &( *aasworld ).edges[e]; + if ( edge->v[0] == v1num ) { + if ( edge->v[1] == v2num ) { + *edgenum = e; + return true; + } //end if + } //end if + else if ( edge->v[1] == v1num ) { + if ( edge->v[0] == v2num ) { + //negative for a reversed edge + *edgenum = -e; + return true; + } //end if + } //end else + } //end for + return false; +} //end of the function AAS_FindHashedPlane +//=========================================================================== +// returns true if the edge was found +// stores the edge number in *edgenum (negative if reversed edge) +// stores new edge if not stored already +// returns zero when the edge is degenerate +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_GetEdge( vec3_t v1, vec3_t v2, int *edgenum ) { + int v1num, v2num; + qboolean found; + + //the first edge is a dummy + if ( ( *aasworld ).numedges == 0 ) { + ( *aasworld ).numedges = 1; + } + + found = AAS_GetVertex( v1, &v1num ); + found &= AAS_GetVertex( v2, &v2num ); + //if one of the vertexes was outside the valid range + if ( v1num == -1 || v2num == -1 ) { + *edgenum = 0; + return true; + } //end if + //if both vertexes are the same or snapped onto each other + if ( v1num == v2num ) { + *edgenum = 0; + return true; + } //end if + //if both vertexes where already stored + if ( found ) { +#ifdef EDGE_HASHING + if ( AAS_FindHashedEdge( v1num, v2num, edgenum ) ) { + return true; + } +#else + int i; + for ( i = 1; i < ( *aasworld ).numedges; i++ ) + { + if ( ( *aasworld ).edges[i].v[0] == v1num ) { + if ( ( *aasworld ).edges[i].v[1] == v2num ) { + *edgenum = i; + return true; + } //end if + } //end if + else if ( ( *aasworld ).edges[i].v[1] == v1num ) { + if ( ( *aasworld ).edges[i].v[0] == v2num ) { + //negative for a reversed edge + *edgenum = -i; + return true; + } //end if + } //end else + } //end for +#endif //EDGE_HASHING + } //end if + if ( ( *aasworld ).numedges >= max_aas.max_edges ) { + Error( "AAS_MAX_EDGES = %d", max_aas.max_edges ); + } //end if + ( *aasworld ).edges[( *aasworld ).numedges].v[0] = v1num; + ( *aasworld ).edges[( *aasworld ).numedges].v[1] = v2num; + *edgenum = ( *aasworld ).numedges; +#ifdef EDGE_HASHING + AAS_AddEdgeToHash( *edgenum ); +#endif //EDGE_HASHING + ( *aasworld ).numedges++; + return false; +} //end of the function AAS_GetEdge +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PlaneTypeForNormal( vec3_t normal ) { + vec_t ax, ay, az; + + //NOTE: epsilon used + if ( ( normal[0] >= 1.0 - NORMAL_EPSILON ) || + ( normal[0] <= -1.0 + NORMAL_EPSILON ) ) { + return PLANE_X; + } + if ( ( normal[1] >= 1.0 - NORMAL_EPSILON ) || + ( normal[1] <= -1.0 + NORMAL_EPSILON ) ) { + return PLANE_Y; + } + if ( ( normal[2] >= 1.0 - NORMAL_EPSILON ) || + ( normal[2] <= -1.0 + NORMAL_EPSILON ) ) { + return PLANE_Z; + } + + ax = fabs( normal[0] ); + ay = fabs( normal[1] ); + az = fabs( normal[2] ); + + if ( ax >= ay && ax >= az ) { + return PLANE_ANYX; + } + if ( ay >= ax && ay >= az ) { + return PLANE_ANYY; + } + return PLANE_ANYZ; +} //end of the function AAS_PlaneTypeForNormal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_AddPlaneToHash( int planenum ) { + int hash; + aas_plane_t *plane; + + plane = &( *aasworld ).planes[planenum]; + + hash = ( PLANE_HASH_SIZE - 1 ) & (int) fabs( plane->dist ); + + aas_planechain[planenum] = aas_hashplanes[hash]; + aas_hashplanes[hash] = planenum; +} //end of the function AAS_AddPlaneToHash +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_PlaneEqual( vec3_t normal, float dist, int planenum ) { + float diff; + + diff = dist - ( *aasworld ).planes[planenum].dist; + if ( diff > -DIST_EPSILON && diff < DIST_EPSILON ) { + diff = normal[0] - ( *aasworld ).planes[planenum].normal[0]; + if ( diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON ) { + diff = normal[1] - ( *aasworld ).planes[planenum].normal[1]; + if ( diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON ) { + diff = normal[2] - ( *aasworld ).planes[planenum].normal[2]; + if ( diff > -NORMAL_EPSILON && diff < NORMAL_EPSILON ) { + return true; + } //end if + } //end if + } //end if + } //end if + return false; +} //end of the function AAS_PlaneEqual +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_FindPlane( vec3_t normal, float dist, int *planenum ) { + int i; + + for ( i = 0; i < ( *aasworld ).numplanes; i++ ) + { + if ( AAS_PlaneEqual( normal, dist, i ) ) { + *planenum = i; + return true; + } //end if + } //end for + return false; +} //end of the function AAS_FindPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_FindHashedPlane( vec3_t normal, float dist, int *planenum ) { + int i, p; + aas_plane_t *plane; + int hash, h; + + hash = ( PLANE_HASH_SIZE - 1 ) & (int)fabs( dist ); + + //search the border bins as well + for ( i = -1; i <= 1; i++ ) + { + h = ( hash + i ) & ( PLANE_HASH_SIZE - 1 ); + for ( p = aas_hashplanes[h]; p >= 0; p = aas_planechain[p] ) + { + plane = &( *aasworld ).planes[p]; + if ( AAS_PlaneEqual( normal, dist, p ) ) { + *planenum = p; + return true; + } //end if + } //end for + } //end for + return false; +} //end of the function AAS_FindHashedPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_GetPlane( vec3_t normal, vec_t dist, int *planenum ) { + aas_plane_t *plane, temp; + + //if (AAS_FindPlane(normal, dist, planenum)) return true; + if ( AAS_FindHashedPlane( normal, dist, planenum ) ) { + return true; + } + + if ( ( *aasworld ).numplanes >= max_aas.max_planes - 1 ) { + Error( "AAS_MAX_PLANES = %d", max_aas.max_planes ); + } //end if + +#ifdef STOREPLANESDOUBLE + plane = &( *aasworld ).planes[( *aasworld ).numplanes]; + VectorCopy( normal, plane->normal ); + plane->dist = dist; + plane->type = ( plane + 1 )->type = PlaneTypeForNormal( plane->normal ); + + VectorCopy( normal, ( plane + 1 )->normal ); + VectorNegate( ( plane + 1 )->normal, ( plane + 1 )->normal ); + ( plane + 1 )->dist = -dist; + + ( *aasworld ).numplanes += 2; + + //allways put axial planes facing positive first + if ( plane->type < 3 ) { + if ( plane->normal[0] < 0 || plane->normal[1] < 0 || plane->normal[2] < 0 ) { + // flip order + temp = *plane; + *plane = *( plane + 1 ); + *( plane + 1 ) = temp; + *planenum = ( *aasworld ).numplanes - 1; + return false; + } //end if + } //end if + *planenum = ( *aasworld ).numplanes - 2; + //add the planes to the hash + AAS_AddPlaneToHash( ( *aasworld ).numplanes - 1 ); + AAS_AddPlaneToHash( ( *aasworld ).numplanes - 2 ); + return false; +#else + plane = &( *aasworld ).planes[( *aasworld ).numplanes]; + VectorCopy( normal, plane->normal ); + plane->dist = dist; + plane->type = AAS_PlaneTypeForNormal( normal ); + + *planenum = ( *aasworld ).numplanes; + ( *aasworld ).numplanes++; + //add the plane to the hash + AAS_AddPlaneToHash( ( *aasworld ).numplanes - 1 ); + return false; +#endif //STOREPLANESDOUBLE +} //end of the function AAS_GetPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean AAS_GetFace( winding_t *w, plane_t *p, int side, int *facenum ) { + int edgenum, i, j; + aas_face_t *face; + + //face zero is a dummy, because of the face index with negative numbers + if ( ( *aasworld ).numfaces == 0 ) { + ( *aasworld ).numfaces = 1; + } + + if ( ( *aasworld ).numfaces >= max_aas.max_faces ) { + Error( "AAS_MAX_FACES = %d", max_aas.max_faces ); + } //end if + face = &( *aasworld ).faces[( *aasworld ).numfaces]; + AAS_GetPlane( p->normal, p->dist, &face->planenum ); + face->faceflags = 0; + face->firstedge = ( *aasworld ).edgeindexsize; + face->frontarea = 0; + face->backarea = 0; + face->numedges = 0; + for ( i = 0; i < w->numpoints; i++ ) + { + if ( ( *aasworld ).edgeindexsize >= max_aas.max_edgeindexsize ) { + Error( "AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize ); + } //end if + j = ( i + 1 ) % w->numpoints; + AAS_GetEdge( w->p[i], w->p[j], &edgenum ); + //if the edge wasn't degenerate + if ( edgenum ) { + ( *aasworld ).edgeindex[( *aasworld ).edgeindexsize++] = edgenum; + face->numedges++; + } //end if + else if ( verbose ) { + Log_Write( "AAS_GetFace: face %d had degenerate edge %d-%d\r\n", + ( *aasworld ).numfaces, i, j ); + } //end else + } //end for + if ( face->numedges < 1 +#ifdef NOTHREEVERTEXFACES + || face->numedges < 3 +#endif //NOTHREEVERTEXFACES + ) { + memset( &( *aasworld ).faces[( *aasworld ).numfaces], 0, sizeof( aas_face_t ) ); + Log_Write( "AAS_GetFace: face %d was tiny\r\n", ( *aasworld ).numfaces ); + return false; + } //end if + *facenum = ( *aasworld ).numfaces; + ( *aasworld ).numfaces++; + return true; +} //end of the function AAS_GetFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +qboolean AAS_GetFace(winding_t *w, plane_t *p, int side, int *facenum) +{ + aas_edgeindex_t edges[1024]; + int planenum, numedges, i; + int j, edgenum; + qboolean foundplane, foundedges; + aas_face_t *face; + + //face zero is a dummy, because of the face index with negative numbers + if ((*aasworld).numfaces == 0) (*aasworld).numfaces = 1; + + foundplane = AAS_GetPlane(p->normal, p->dist, &planenum); + + foundedges = true; + numedges = w->numpoints; + for (i = 0; i < w->numpoints; i++) + { + if (i >= 1024) Error("AAS_GetFace: more than %d edges\n", 1024); + foundedges &= AAS_GetEdge(w->p[i], w->p[(i+1 >= w->numpoints ? 0 : i+1)], &edges[i]); + } //end for + + //FIXME: use portal number instead of a search + //if the plane and all edges already existed + if (foundplane && foundedges) + { + for (i = 0; i < (*aasworld).numfaces; i++) + { + face = &(*aasworld).faces[i]; + if (planenum == face->planenum) + { + if (numedges == face->numedges) + { + for (j = 0; j < numedges; j++) + { + edgenum = abs((*aasworld).edgeindex[face->firstedge + j]); + if (abs(edges[i]) != edgenum) break; + } //end for + if (j == numedges) + { + //jippy found the face + *facenum = -i; + return true; + } //end if + } //end if + } //end if + } //end for + } //end if + if ((*aasworld).numfaces >= max_aas.max_faces) + { + Error("AAS_MAX_FACES = %d", max_aas.max_faces); + } //end if + face = &(*aasworld).faces[(*aasworld).numfaces]; + face->planenum = planenum; + face->faceflags = 0; + face->numedges = numedges; + face->firstedge = (*aasworld).edgeindexsize; + face->frontarea = 0; + face->backarea = 0; + for (i = 0; i < numedges; i++) + { + if ((*aasworld).edgeindexsize >= max_aas.max_edgeindexsize) + { + Error("AAS_MAX_EDGEINDEXSIZE = %d", max_aas.max_edgeindexsize); + } //end if + (*aasworld).edgeindex[(*aasworld).edgeindexsize++] = edges[i]; + } //end for + *facenum = (*aasworld).numfaces; + (*aasworld).numfaces++; + return false; +} //end of the function AAS_GetFace*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_StoreAreaSettings( tmp_areasettings_t *tmpareasettings ) { + aas_areasettings_t *areasettings; + + if ( ( *aasworld ).numareasettings == 0 ) { + ( *aasworld ).numareasettings = 1; + } + areasettings = &( *aasworld ).areasettings[( *aasworld ).numareasettings++]; + areasettings->areaflags = tmpareasettings->areaflags; + areasettings->presencetype = tmpareasettings->presencetype; + areasettings->contents = tmpareasettings->contents; + areasettings->groundsteepness = tmpareasettings->groundsteepness; // Ridah + if ( tmpareasettings->modelnum > AREACONTENTS_MAXMODELNUM ) { + Log_Print( "WARNING: more than %d mover models\n", AREACONTENTS_MAXMODELNUM ); + } + areasettings->contents |= ( tmpareasettings->modelnum & AREACONTENTS_MAXMODELNUM ) << AREACONTENTS_MODELNUMSHIFT; +} //end of the function AAS_StoreAreaSettings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_StoreArea( tmp_area_t *tmparea ) { + int side, edgenum, i; + plane_t *plane; + tmp_face_t *tmpface; + aas_area_t *aasarea; + aas_edge_t *edge; + aas_face_t *aasface; + aas_faceindex_t aasfacenum; + vec3_t facecenter; + winding_t *w; + + //when the area is merged go to the merged area + //FIXME: this isn't necessary anymore because the tree + // is refreshed after area merging + while ( tmparea->mergedarea ) tmparea = tmparea->mergedarea; + // + if ( tmparea->invalid ) { + // RF, might be a removed non-grounded area + return 0; + //Error("AAS_StoreArea: tried to store invalid area"); + } + //if there is an aas area already stored for this tmp area + if ( tmparea->aasareanum ) { + return -tmparea->aasareanum; + } + // + if ( ( *aasworld ).numareas >= max_aas.max_areas ) { + Error( "AAS_MAX_AREAS = %d", max_aas.max_areas ); + } //end if + //area zero is a dummy + if ( ( *aasworld ).numareas == 0 ) { + ( *aasworld ).numareas = 1; + } + //create an area from this leaf + aasarea = &( *aasworld ).areas[( *aasworld ).numareas]; + aasarea->areanum = ( *aasworld ).numareas; + aasarea->numfaces = 0; + aasarea->firstface = ( *aasworld ).faceindexsize; + ClearBounds( aasarea->mins, aasarea->maxs ); + VectorClear( aasarea->center ); + // +// Log_Write("tmparea %d became aasarea %d\r\n", tmparea->areanum, aasarea->areanum); + //store the aas area number at the tmp area + tmparea->aasareanum = aasarea->areanum; + // + for ( tmpface = tmparea->tmpfaces; tmpface; tmpface = tmpface->next[side] ) + { + side = tmpface->frontarea != tmparea; + //if there's an aas face created for the tmp face already + if ( tmpface->aasfacenum ) { + //we're at the back of the face so use a negative index + aasfacenum = -tmpface->aasfacenum; +#ifdef DEBUG + if ( tmpface->aasfacenum < 0 || tmpface->aasfacenum > max_aas.max_faces ) { + Error( "AAS_CreateTree_r: face number out of range" ); + } //end if +#endif //DEBUG + aasface = &( *aasworld ).faces[tmpface->aasfacenum]; + aasface->backarea = aasarea->areanum; + } //end if + else + { + plane = &mapplanes[tmpface->planenum ^ side]; + if ( side ) { + w = tmpface->winding; + tmpface->winding = ReverseWinding( tmpface->winding ); + } //end if + if ( !AAS_GetFace( tmpface->winding, plane, 0, &aasfacenum ) ) { + continue; + } + if ( side ) { + FreeWinding( tmpface->winding ); + tmpface->winding = w; + } //end if + aasface = &( *aasworld ).faces[aasfacenum]; + aasface->frontarea = aasarea->areanum; + aasface->backarea = 0; + aasface->faceflags = tmpface->faceflags; + //set the face number at the tmp face + tmpface->aasfacenum = aasfacenum; + } //end else + //add face points to the area bounds and + //calculate the face 'center' + VectorClear( facecenter ); + for ( edgenum = 0; edgenum < aasface->numedges; edgenum++ ) + { + edge = &( *aasworld ).edges[abs( ( *aasworld ).edgeindex[aasface->firstedge + edgenum] )]; + for ( i = 0; i < 2; i++ ) + { + AddPointToBounds( ( *aasworld ).vertexes[edge->v[i]], aasarea->mins, aasarea->maxs ); + VectorAdd( ( *aasworld ).vertexes[edge->v[i]], facecenter, facecenter ); + } //end for + } //end for + VectorScale( facecenter, 1.0 / ( aasface->numedges * 2.0 ), facecenter ); + //add the face 'center' to the area 'center' + VectorAdd( aasarea->center, facecenter, aasarea->center ); + // + if ( ( *aasworld ).faceindexsize >= max_aas.max_faceindexsize ) { + Error( "AAS_MAX_FACEINDEXSIZE = %d", max_aas.max_faceindexsize ); + } //end if + ( *aasworld ).faceindex[( *aasworld ).faceindexsize++] = aasfacenum; + aasarea->numfaces++; + } //end for + //if the area has no faces at all (return 0, = solid leaf) + if ( !aasarea->numfaces ) { + return 0; + } + // + VectorScale( aasarea->center, 1.0 / aasarea->numfaces, aasarea->center ); + //Log_Write("area %d center %f %f %f\r\n", (*aasworld).numareas, + // aasarea->center[0], aasarea->center[1], aasarea->center[2]); + //store the area settings + AAS_StoreAreaSettings( tmparea->settings ); + // + //Log_Write("tmp area %d became aas area %d\r\n", tmpareanum, aasarea->areanum); + qprintf( "\r%6d", aasarea->areanum ); + // + if ( ( *aasworld ).areasettings[aasarea->areanum].contents & AREACONTENTS_CLUSTERPORTAL ) { + static int num; + Log_Write( "***** area %d is a cluster portal %d\n", aasarea->areanum, num++ ); + } //end if + // + ( *aasworld ).numareas++; + return -( ( *aasworld ).numareas - 1 ); +} //end of the function AAS_StoreArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int AAS_StoreTree_r( tmp_node_t *tmpnode ) { + int aasnodenum; + plane_t *plane; + aas_node_t *aasnode; + + //if it is a solid leaf + if ( !tmpnode ) { + return 0; + } + //negative so it's an area + if ( tmpnode->tmparea ) { + return AAS_StoreArea( tmpnode->tmparea ); + } + //it's another node + //the first node is a dummy + if ( ( *aasworld ).numnodes == 0 ) { + ( *aasworld ).numnodes = 1; + } + if ( ( *aasworld ).numnodes >= max_aas.max_nodes ) { + Error( "AAS_MAX_NODES = %d", max_aas.max_nodes ); + } //end if + aasnodenum = ( *aasworld ).numnodes; + aasnode = &( *aasworld ).nodes[( *aasworld ).numnodes++]; + plane = &mapplanes[tmpnode->planenum]; + AAS_GetPlane( plane->normal, plane->dist, &aasnode->planenum ); + aasnode->children[0] = AAS_StoreTree_r( tmpnode->children[0] ); + aasnode->children[1] = AAS_StoreTree_r( tmpnode->children[1] ); + return aasnodenum; +} //end of the function AAS_StoreTree_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_StoreBoundingBoxes( void ) { + if ( cfg.numbboxes > max_aas.max_bboxes ) { + Error( "more than %d bounding boxes", max_aas.max_bboxes ); + } //end if + ( *aasworld ).numbboxes = cfg.numbboxes; + memcpy( ( *aasworld ).bboxes, cfg.bboxes, cfg.numbboxes * sizeof( aas_bbox_t ) ); +} //end of the function AAS_StoreBoundingBoxes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_StoreFile( char *filename ) { + AAS_AllocMaxAAS(); + + Log_Write( "AAS_StoreFile\r\n" ); + // + AAS_StoreBoundingBoxes(); + // + qprintf( "%6d areas stored", 0 ); + //start with node 1 because node zero is a dummy + AAS_StoreTree_r( tmpaasworld.nodes ); + qprintf( "\n" ); + Log_Write( "%6d areas stored\r\n", ( *aasworld ).numareas ); + ( *aasworld ).loaded = true; +} //end of the function AAS_StoreFile diff --git a/src/bspc/aas_store.h b/src/bspc/aas_store.h new file mode 100644 index 0000000..90164fc --- /dev/null +++ b/src/bspc/aas_store.h @@ -0,0 +1,125 @@ +/* +=========================================================================== + +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: aas_store.h +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#define AAS_MAX_BBOXES 5 +#define AAS_MAX_VERTEXES 512000 +#define AAS_MAX_PLANES 65536 +#define AAS_MAX_EDGES 512000 +#define AAS_MAX_EDGEINDEXSIZE 512000 +#define AAS_MAX_FACES 512000 +#define AAS_MAX_FACEINDEXSIZE 512000 +#define AAS_MAX_AREAS 65536 +#define AAS_MAX_AREASETTINGS 65536 +#define AAS_MAX_REACHABILITYSIZE 128000 +#define AAS_MAX_NODES 256000 +#define AAS_MAX_PORTALS 65536 +#define AAS_MAX_PORTALINDEXSIZE 65536 +#define AAS_MAX_CLUSTERS 65536 + +#define BSPCINCLUDE +#include "../game/be_aas.h" +#include "../botlib/be_aas_def.h" + +/* +typedef struct bspc_aas_s +{ + int loaded; + int initialized; //true when AAS has been initialized + int savefile; //set true when file should be saved + //bounding boxes + int numbboxes; + aas_bbox_t *bboxes; + //vertexes + int numvertexes; + aas_vertex_t *vertexes; + //planes + int numplanes; + aas_plane_t *planes; + //edges + int numedges; + aas_edge_t *edges; + //edge index + int edgeindexsize; + aas_edgeindex_t *edgeindex; + //faces + int numfaces; + aas_face_t *faces; + //face index + int faceindexsize; + aas_faceindex_t *faceindex; + //convex areas + int numareas; + aas_area_t *areas; + //convex area settings + int numareasettings; + aas_areasettings_t *areasettings; + //reachablity list + int reachabilitysize; + aas_reachability_t *reachability; + //nodes of the bsp tree + int numnodes; + aas_node_t *nodes; + //cluster portals + int numportals; + aas_portal_t *portals; + //cluster portal index + int portalindexsize; + aas_portalindex_t *portalindex; + //clusters + int numclusters; + aas_cluster_t *clusters; + // + int numreachabilityareas; + float reachabilitytime; +} bspc_aas_t; + +extern bspc_aas_t aasworld; +//*/ + +// Ridah +extern aas_t aasworlds[1]; +extern aas_t *aasworld; +// done. + +//stores the AAS file from the temporary AAS +void AAS_StoreFile( char *filename ); +//returns a number of the given plane +qboolean AAS_FindPlane( vec3_t normal, float dist, int *planenum ); +//allocates the maximum AAS memory for storage +void AAS_AllocMaxAAS( void ); +//frees the maximum AAS memory for storage +void AAS_FreeMaxAAS( void ); diff --git a/src/bspc/be_aas_bspc.c b/src/bspc/be_aas_bspc.c new file mode 100644 index 0000000..e869d99 --- /dev/null +++ b/src/bspc/be_aas_bspc.c @@ -0,0 +1,315 @@ +/* +=========================================================================== + +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: b_aas_bspc.c +// Function: Area Awareness System +// Programmer: Mr Elusive (MrElusive@idsoftware.com) +// Last update: 1999-09-14 +// Tab Size: 3 +//=========================================================================== + +#include "../game/q_shared.h" +#include "../bspc/l_log.h" +#include "../bspc/l_qfiles.h" +#include "../botlib/l_memory.h" +#include "../botlib/l_script.h" +#include "../botlib/l_precomp.h" +#include "../botlib/l_struct.h" +#include "../botlib/aasfile.h" +#include "../game/botlib.h" +#include "../game/be_aas.h" +#include "../botlib/be_aas_def.h" +#include "../qcommon/cm_public.h" + +//#define BSPC + +extern botlib_import_t botimport; +extern qboolean capsule_collision; + +//#define AAS_MOVE_DEBUG + +botlib_import_t botimport; +clipHandle_t worldmodel; + +void Error( char *error, ... ); + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_Error( char *fmt, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + Error( text ); +} //end of the function AAS_Error +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Sys_MilliSeconds( void ) { + return clock() * 1000 / CLOCKS_PER_SEC; +} //end of the function Sys_MilliSeconds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_DebugLine( vec3_t start, vec3_t end, int color ) { +} //end of the function AAS_DebugLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ClearShownDebugLines( void ) { +} //end of the function AAS_ClearShownDebugLines +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *BotImport_BSPEntityData( void ) { + return CM_EntityString(); +} //end of the function AAS_GetEntityData +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotImport_Trace( bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask ) { + trace_t result; + + CM_BoxTrace( &result, start, end, mins, maxs, worldmodel, contentmask, capsule_collision ); + + bsptrace->allsolid = result.allsolid; + bsptrace->contents = result.contents; + VectorCopy( result.endpos, bsptrace->endpos ); + bsptrace->ent = result.entityNum; + bsptrace->fraction = result.fraction; + bsptrace->exp_dist = 0; + bsptrace->plane.dist = result.plane.dist; + VectorCopy( result.plane.normal, bsptrace->plane.normal ); + bsptrace->plane.signbits = result.plane.signbits; + bsptrace->plane.type = result.plane.type; + bsptrace->sidenum = 0; + bsptrace->startsolid = result.startsolid; + bsptrace->surface.flags = result.surfaceFlags; +} //end of the function BotImport_Trace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BotImport_PointContents( vec3_t p ) { + return CM_PointContents( p, worldmodel ); +} //end of the function BotImport_PointContents +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *BotImport_GetMemory( int size ) { + return GetMemory( size ); +} //end of the function BotImport_GetMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotImport_Print( int type, char *fmt, ... ) { + va_list argptr; + char buf[1024]; + + va_start( argptr, fmt ); + Q_vsnprintf( buf, sizeof( buf ), fmt, argptr ); + printf( buf ); + if ( buf[0] != '\r' ) { + Log_Write( buf ); + } + va_end( argptr ); +} //end of the function BotImport_Print +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BotImport_BSPModelMinsMaxsOrigin( int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin ) { + clipHandle_t h; + vec3_t mins, maxs; + float max; + int i; + + h = CM_InlineModel( modelnum ); + CM_ModelBounds( h, mins, maxs ); + //if the model is rotated + if ( ( angles[0] || angles[1] || angles[2] ) ) { // expand for rotation + + max = RadiusFromBounds( mins, maxs ); + for ( i = 0; i < 3; i++ ) + { + mins[i] = ( mins[i] + maxs[i] ) * 0.5 - max; + maxs[i] = ( mins[i] + maxs[i] ) * 0.5 + max; + } //end for + } //end if + if ( outmins ) { + VectorCopy( mins, outmins ); + } + if ( outmaxs ) { + VectorCopy( maxs, outmaxs ); + } + if ( origin ) { + VectorClear( origin ); + } +} //end of the function BotImport_BSPModelMinsMaxsOrigin +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Com_DPrintf( char *fmt, ... ) { + va_list argptr; + char buf[1024]; + + va_start( argptr, fmt ); + Q_vsnprintf( buf, sizeof( buf ), fmt, argptr ); + printf( buf ); + if ( buf[0] != '\r' ) { + Log_Write( buf ); + } + va_end( argptr ); +} //end of the function Com_DPrintf +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int COM_Compress( char *data_p ) { + return strlen( data_p ); +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Com_Memset( void* dest, const int val, const size_t count ) { + memset( dest, val, count ); +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Com_Memcpy( void* dest, const void* src, const size_t count ) { + memcpy( dest, src, count ); +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_InitBotImport( void ) { + botimport.BSPEntityData = BotImport_BSPEntityData; + botimport.GetMemory = BotImport_GetMemory; + botimport.FreeMemory = FreeMemory; + botimport.Trace = BotImport_Trace; + botimport.PointContents = BotImport_PointContents; + botimport.Print = BotImport_Print; + botimport.BSPModelMinsMaxsOrigin = BotImport_BSPModelMinsMaxsOrigin; +} //end of the function AAS_InitBotImport +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_SetViewPortalsAsClusterPortals( void ); + +void AAS_CalcReachAndClusters( struct quakefile_s *qf ) { + float time; + + Log_Print( "loading collision map...\n" ); + // + if ( !qf->pakfile[0] ) { + strcpy( qf->pakfile, qf->filename ); + } + //load the map + CM_LoadMap( (char *) qf, qfalse, &( *aasworld ).bspchecksum ); + //get a handle to the world model + worldmodel = CM_InlineModel( 0 ); // 0 = world, 1 + are bmodels + //initialize bot import structure + AAS_InitBotImport(); + //load the BSP entity string + AAS_LoadBSPFile(); + //init physics settings + AAS_InitSettings(); + //initialize AAS link heap + AAS_InitAASLinkHeap(); + //initialize the AAS linked entities for the new map + AAS_InitAASLinkedEntities(); + //reset all reachabilities and clusters + ( *aasworld ).reachabilitysize = 0; + ( *aasworld ).numclusters = 0; + //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) + AAS_SetViewPortalsAsClusterPortals(); + //calculate reachabilities + AAS_InitReachability(); + time = 0; + while ( AAS_ContinueInitReachability( time ) ) time++; + //calculate clusters + AAS_InitClustering(); +} //end of the function AAS_CalcReachAndClusters + +// Ridah +void AAS_SetWorldPointer( aas_t *newaasworld ) { + aasworld = newaasworld; +} +// done. diff --git a/src/bspc/be_aas_bspc.h b/src/bspc/be_aas_bspc.h new file mode 100644 index 0000000..3b6d750 --- /dev/null +++ b/src/bspc/be_aas_bspc.h @@ -0,0 +1,43 @@ +/* +=========================================================================== + +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: b_aas_bspc.h +// Function: Area Awareness System +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1999-02-28 +// Tab Size: 3 +//=========================================================================== + +void AAS_CalcReachAndClusters( struct quakefile_s *qf ); + +// Ridah +void AAS_SetWorldPointer( aas_t *newaasworld ); +// done. + diff --git a/src/bspc/brushbsp.c b/src/bspc/brushbsp.c new file mode 100644 index 0000000..14fb60e --- /dev/null +++ b/src/bspc/brushbsp.c @@ -0,0 +1,2619 @@ +/* +=========================================================================== + +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: BrushBSP +// Function: Build a BSP tree from a set of brushes +// Programmer: id Software & Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_mem.h" +#include "..\botlib\aasfile.h" +#include "aas_store.h" +#include "aas_cfg.h" +#include "../game/surfaceflags.h" + +#include + +/* +each side has a count of the other sides it splits + +the best split will be the one that minimizes the total split counts +of all remaining sides + +precalc side on plane table + +evaluate split side +{ +cost = 0 +for all sides + for all sides + get + if side splits side and splitside is on same child + cost++; +} +*/ + +int c_nodes; +int c_nonvis; +int c_active_brushes; +int c_solidleafnodes; +int c_totalsides; +int c_brushmemory; +int c_peak_brushmemory; +int c_nodememory; +int c_peak_totalbspmemory; + +FILE *brushMap = NULL; +int brushMapContents = 0; + +// if a brush just barely pokes onto the other side, let it slide by without chopping +#define BRUSH_EPSILON 0.1f +#define PLANESIDE_EPSILON BRUSH_EPSILON //0.001f //0.1f + +//#ifdef 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_Q2TRANSLUCENT,"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 ) { + Log_Write( "%s,", contentnames[i].name ); + } //end if + } //end for +} //end of the function PrintContents + +//#endif DEBUG + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteBSPBrush( FILE *fp, bspbrush_t *brush ) { + int sn, p1, i, j; + winding_t *w; + side_t *s; + plane_t *plane; + + //print the leading { + if ( fprintf( fp, " { // brush\n" ) < 0 ) { + return false; + } + // write brush sides + for ( sn = 0; sn < brush->numsides; sn++ ) { + s = brush->sides + sn; + // always take the first plane, then flip the points if necesary + plane = &mapplanes[s->planenum & ~1]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + + for ( i = 0; i < 3; i++ ) { + for ( j = 0; j < 3; j++ ) { + if ( fabs( w->p[i][j] ) < 0.2 ) { + w->p[i][j] = 0; + } else if ( fabs( (int)w->p[i][j] - w->p[i][j] ) < 0.3 ) { + w->p[i][j] = (int) w->p[i][j]; + } + } + } + // three non-colinear points to define the plane + if ( s->planenum & 1 ) { + p1 = 1; + } else { p1 = 0;} + if ( fprintf( fp," ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] ) < 0 ) { + return false; + } + // free the winding + FreeWinding( w ); + if ( brush->original && brush->original->contents & CONTENTS_MOVER ) { + if ( fprintf( fp, "common/clipweap 16 0 0 0.500000 0.500000 0 4 0\n" ) < 0 ) { + return false; + } + } else if ( fprintf( fp, "common/caulk 16 0 0 0.500000 0.500000 0 4 0\n" ) < 0 ) { + return false; + } + } + if ( fprintf( fp, " }\n" ) < 0 ) { + return false; + } + return true; +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void OpenBSPBrushMap( char *filename, int contents ) { + brushMap = SafeOpenWrite( filename ); + brushMapContents = contents; + qprintf( "writing brush map %s\n", filename ); + fprintf( brushMap, "{\n\"classname\" \"worldspawn\"\n" ); +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CloseBSPBrushMap( void ) { + fprintf( brushMap, "}\n" ); + if ( brushMap ) { + fclose( brushMap ); + } + brushMap = NULL; + brushMapContents = 0; +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WriteBrushListToFile( bspbrush_t *brushlist, char *filename ) { + FILE *fp; + bspbrush_t *brush; + + fp = SafeOpenWrite( filename ); + if ( !fp ) { + qprintf( "ERROR: can't open %s\n", filename ); + return; + } + qprintf( "writing brush map %s\n", filename ); + fprintf( fp, "{\n\"classname\" \"worldspawn\"\n" ); + for ( brush = brushlist; brush; brush = brush->next ) { + WriteBSPBrush( fp, brush ); + } + fprintf( fp, "}\n" ); + fclose( fp ); +} + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ResetBrushBSP( void ) { + c_nodes = 0; + c_nonvis = 0; + c_active_brushes = 0; + c_solidleafnodes = 0; + c_totalsides = 0; + c_brushmemory = 0; + c_peak_brushmemory = 0; + c_nodememory = 0; + c_peak_totalbspmemory = 0; + brushMap = NULL; + brushMapContents = 0; +} //end of the function ResetBrushBSP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FindBrushInTree( node_t *node, int brushnum ) { + bspbrush_t *b; + + if ( node->planenum == PLANENUM_LEAF ) { + for ( b = node->brushlist ; b ; b = b->next ) + if ( b->original->brushnum == brushnum ) { + Log_Print( "here\n" ); + } + return; + } + FindBrushInTree( node->children[0], brushnum ); + FindBrushInTree( node->children[1], brushnum ); +} //end of the function FindBrushInTree +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DrawBrushList( bspbrush_t *brush, node_t *node ) { + int i; + side_t *s; + + GLS_BeginScene(); + for ( ; brush ; brush = brush->next ) + { + for ( i = 0 ; i < brush->numsides ; i++ ) + { + s = &brush->sides[i]; + if ( !s->winding ) { + continue; + } + if ( s->texinfo == TEXINFO_NODE ) { + GLS_Winding( s->winding, 1 ); + } else if ( !( s->flags & SFL_VISIBLE ) ) { + GLS_Winding( s->winding, 2 ); + } else { + GLS_Winding( s->winding, 0 ); + } + } + } + GLS_EndScene(); +} //end of the function DrawBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WriteBrushList( char *name, bspbrush_t *brush, qboolean onlyvis ) { + int i; + side_t *s; + FILE *f; + + qprintf( "writing %s\n", name ); + f = SafeOpenWrite( name ); + + for ( ; brush ; brush = brush->next ) + { + for ( i = 0 ; i < brush->numsides ; i++ ) + { + s = &brush->sides[i]; + if ( !s->winding ) { + continue; + } + if ( onlyvis && !( s->flags & SFL_VISIBLE ) ) { + continue; + } + OutputWinding( brush->sides[i].winding, f ); + } + } + + fclose( f ); +} //end of the function WriteBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintBrush( bspbrush_t *brush ) { + int i; + + printf( "brush: %p\n", brush ); + for ( i = 0; i < brush->numsides ; i++ ) + { + pw( brush->sides[i].winding ); + printf( "\n" ); + } //end for +} //end of the function PrintBrush +//=========================================================================== +// Sets the mins/maxs based on the windings +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BoundBrush( bspbrush_t *brush ) { + int i, j; + winding_t *w; + + ClearBounds( brush->mins, brush->maxs ); + for ( i = 0 ; i < brush->numsides ; i++ ) + { + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + for ( j = 0 ; j < w->numpoints ; j++ ) + AddPointToBounds( w->p[j], brush->mins, brush->maxs ); + } +} //end of the function BoundBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CreateBrushWindings( bspbrush_t *brush ) { + int i, j; + winding_t *w; + side_t *side; + plane_t *plane; + + for ( i = 0 ; i < brush->numsides ; i++ ) + { + side = &brush->sides[i]; + plane = &mapplanes[side->planenum]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( j = 0 ; j < brush->numsides && w; j++ ) + { + if ( i == j ) { + continue; + } + if ( brush->sides[j].flags & SFL_BEVEL ) { + continue; + } + plane = &mapplanes[brush->sides[j].planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } + + side->winding = w; + } + + BoundBrush( brush ); +} //end of the function CreateBrushWindings +//=========================================================================== +// Creates a new axial brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *BrushFromBounds( vec3_t mins, vec3_t maxs ) { + bspbrush_t *b; + int i; + vec3_t normal; + vec_t dist; + + b = AllocBrush( 6 ); + b->numsides = 6; + for ( i = 0 ; i < 3 ; i++ ) + { + VectorClear( normal ); + normal[i] = 1; + dist = maxs[i]; + b->sides[i].planenum = FindFloatPlane( normal, dist, 1, (vec3_t*)maxs ); + + normal[i] = -1; + dist = -mins[i]; + b->sides[3 + i].planenum = FindFloatPlane( normal, dist, 1, (vec3_t*)mins ); + } + + CreateBrushWindings( b ); + + return b; +} //end of the function BrushFromBounds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BrushOutOfBounds( bspbrush_t *brush, vec3_t mins, vec3_t maxs, float epsilon ) { + int i, j, n; + winding_t *w; + side_t *side; + + for ( i = 0; i < brush->numsides; i++ ) + { + side = &brush->sides[i]; + w = side->winding; + for ( j = 0; j < w->numpoints; j++ ) + { + for ( n = 0; n < 3; n++ ) + { + if ( w->p[j][n] < ( mins[n] + epsilon ) || w->p[j][n] > ( maxs[n] - epsilon ) ) { + return true; + } + } //end for + } //end for + } //end for + return false; +} //end of the function BrushOutOfBounds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec_t BrushVolume( bspbrush_t *brush ) { + int i; + winding_t *w; + vec3_t corner; + vec_t d, area, volume; + plane_t *plane; + + if ( !brush ) { + return 0; + } + + // grab the first valid point as the corner + w = NULL; + for ( i = 0; i < brush->numsides; i++ ) + { + w = brush->sides[i].winding; + if ( w ) { + break; + } + } //end for + if ( !w ) { + return 0; + } + VectorCopy( w->p[0], corner ); + + // make tetrahedrons to all other faces + volume = 0; + for ( ; i < brush->numsides; i++ ) + { + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + plane = &mapplanes[brush->sides[i].planenum]; + d = -( DotProduct( corner, plane->normal ) - plane->dist ); + area = WindingArea( w ); + volume += d * area; + } //end for + + volume /= 3; + return volume; +} //end of the function BrushVolume +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CountBrushList( bspbrush_t *brushes ) { + int c; + + c = 0; + for ( ; brushes; brushes = brushes->next ) c++; + return c; +} //end of the function CountBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *AllocNode( void ) { + node_t *node; + + node = GetMemory( sizeof( *node ) ); + memset( node, 0, sizeof( *node ) ); + if ( numthreads == 1 ) { + c_nodememory += MemorySize( node ); + } //end if + return node; +} //end of the function AllocNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *AllocBrush( int numsides ) { + bspbrush_t *bb; + int c; + + c = (int)&( ( (bspbrush_t *)0 )->sides[numsides] ); + bb = GetMemory( c ); + memset( bb, 0, c ); + if ( numthreads == 1 ) { + c_active_brushes++; + c_brushmemory += MemorySize( bb ); + if ( c_brushmemory > c_peak_brushmemory ) { + c_peak_brushmemory = c_brushmemory; + } + } //end if + return bb; +} //end of the function AllocBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeBrush( bspbrush_t *brushes ) { + int i; + + for ( i = 0 ; i < brushes->numsides ; i++ ) + if ( brushes->sides[i].winding ) { + FreeWinding( brushes->sides[i].winding ); + } + if ( numthreads == 1 ) { + c_active_brushes--; + c_brushmemory -= MemorySize( brushes ); + if ( c_brushmemory < 0 ) { + c_brushmemory = 0; + } + } //end if + FreeMemory( brushes ); +} //end of the function FreeBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeBrushList( bspbrush_t *brushes ) { + bspbrush_t *next; + + for ( ; brushes; brushes = next ) + { + next = brushes->next; + + FreeBrush( brushes ); + } //end for +} //end of the function FreeBrushList +//=========================================================================== +// Duplicates the brush, the sides, and the windings +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *CopyBrush( bspbrush_t *brush ) { + bspbrush_t *newbrush; + int size; + int i; + + size = (int)&( ( (bspbrush_t *)0 )->sides[brush->numsides] ); + + newbrush = AllocBrush( brush->numsides ); + memcpy( newbrush, brush, size ); + + for ( i = 0 ; i < brush->numsides ; i++ ) + { + if ( brush->sides[i].winding ) { + newbrush->sides[i].winding = CopyWinding( brush->sides[i].winding ); + } + } + + return newbrush; +} //end of the function CopyBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *PointInLeaf( node_t *node, vec3_t point ) { + vec_t d; + plane_t *plane; + + while ( node->planenum != PLANENUM_LEAF ) + { + plane = &mapplanes[node->planenum]; + d = DotProduct( point, plane->normal ) - plane->dist; + if ( d > 0 ) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + + return node; +} //end of the function PointInLeaf +//=========================================================================== +// Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MRE_ET + +static int MrE_BoxOnPlaneSide( vec3_t mins, vec3_t maxs, plane_t *plane, float epsilon ) { + vec3_t center; + float d1, d2; + + VectorAdd( mins, maxs, center ); + VectorScale( center, 0.5f, center ); + + d1 = DotProduct( plane->normal, center ) - plane->dist; + d2 = fabs( ( maxs[0] - center[0] ) * plane->normal[0] ) + + fabs( ( maxs[1] - center[1] ) * plane->normal[1] ) + + fabs( ( maxs[2] - center[2] ) * plane->normal[2] ); + + if ( d1 - d2 > epsilon ) { + return PSIDE_FRONT; + } + if ( d1 + d2 < -epsilon ) { + return PSIDE_BACK; + } + return PSIDE_FRONT | PSIDE_BACK; +} + +#endif + +#if 1 + +int BoxOnPlaneSide( vec3_t mins, vec3_t maxs, plane_t *plane ) { + int side; + int i; + vec3_t corners[2]; + vec_t dist1, dist2; + + // axial planes are easy + if ( plane->type < 3 ) { + side = 0; + if ( maxs[plane->type] > plane->dist + BRUSH_EPSILON ) { + side |= PSIDE_FRONT; + } + if ( mins[plane->type] < plane->dist - BRUSH_EPSILON ) { + side |= PSIDE_BACK; + } + return side; + } + + // create the proper leading and trailing verts for the box + + for ( i = 0; i < 3; i++ ) { + if ( plane->normal[i] < 0 ) { + corners[0][i] = mins[i]; + corners[1][i] = maxs[i]; + } else { + corners[1][i] = mins[i]; + corners[0][i] = maxs[i]; + } + } + + dist1 = DotProduct( plane->normal, corners[0] ) - plane->dist; + dist2 = DotProduct( plane->normal, corners[1] ) - plane->dist; + side = 0; + if ( dist1 >= BRUSH_EPSILON ) { + side = PSIDE_FRONT; + } + if ( dist2 < BRUSH_EPSILON ) { + side |= PSIDE_BACK; + } + + return side; +} + +#elif 1 + +__declspec( naked ) int BoxOnPlaneSide( vec3_t emins, vec3_t emaxs, plane_t *p ) { + static int bops_initialized; + static int Ljmptab[8]; + + __asm { + + push ebx + + cmp bops_initialized, 1 + je initialized + mov bops_initialized, 1 + + mov Ljmptab[0 * 4], offset Lcase0 + mov Ljmptab[1 * 4], offset Lcase1 + mov Ljmptab[2 * 4], offset Lcase2 + mov Ljmptab[3 * 4], offset Lcase3 + mov Ljmptab[4 * 4], offset Lcase4 + mov Ljmptab[5 * 4], offset Lcase5 + mov Ljmptab[6 * 4], offset Lcase6 + mov Ljmptab[7 * 4], offset Lcase7 + +initialized: + + mov edx,dword ptr[4 + 12 + esp] + mov ecx,dword ptr[4 + 4 + esp] + xor eax,eax + mov ebx,dword ptr[4 + 8 + esp] + mov al,byte ptr[17 + edx] + cmp al,8 + jge Lerror + fld dword ptr[0 + edx] + fld st( 0 ) + jmp dword ptr[Ljmptab + eax * 4] + Lcase0 : + fmul dword ptr[ebx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ebx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase1 : + fmul dword ptr[ecx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ebx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase2 : + fmul dword ptr[ebx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ecx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase3 : + fmul dword ptr[ecx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ecx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase4 : + fmul dword ptr[ebx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ebx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase5 : + fmul dword ptr[ecx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ebx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase6 : + fmul dword ptr[ebx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ecx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ecx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + jmp LSetSides + Lcase7 : + fmul dword ptr[ecx] + fld dword ptr[0 + 4 + edx] + fxch st( 2 ) + fmul dword ptr[ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[4 + ecx] + fld dword ptr[0 + 8 + edx] + fxch st( 2 ) + fmul dword ptr[4 + ebx] + fxch st( 2 ) + fld st( 0 ) + fmul dword ptr[8 + ecx] + fxch st( 5 ) + faddp st( 3 ),st( 0 ) + fmul dword ptr[8 + ebx] + fxch st( 1 ) + faddp st( 3 ),st( 0 ) + fxch st( 3 ) + faddp st( 2 ),st( 0 ) + LSetSides : + faddp st( 2 ),st( 0 ) + fcomp dword ptr[12 + edx] + xor ecx,ecx + fnstsw ax + fcomp dword ptr[12 + edx] + and ah,1 + xor ah,1 + add cl,ah + fnstsw ax + and ah,1 + add ah,ah + add cl,ah + pop ebx + mov eax,ecx + ret + Lerror : + int 3 + } +} + +#else + +int BoxOnPlaneSide( vec3_t emins, vec3_t emaxs, plane_t *p ) { + float dist1, dist2; + int sides; + + // axial planes are easy + if ( p->type < 3 ) { + sides = 0; + if ( emaxs[p->type] > p->dist + PLANESIDE_EPSILON ) { + sides |= PSIDE_FRONT; + } + if ( emins[p->type] < p->dist - PLANESIDE_EPSILON ) { + sides |= PSIDE_BACK; + } + return sides; + } //end if + +// general case + switch ( p->signbits ) + { + case 0: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 1: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 2: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 3: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 4: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 5: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 6: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + case 7: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + default: + dist1 = dist2 = 0; // shut up compiler +// assert( 0 ); + break; + } + + sides = 0; + if ( dist1 - p->dist >= PLANESIDE_EPSILON ) { + sides = PSIDE_FRONT; + } + if ( dist2 - p->dist < PLANESIDE_EPSILON ) { + sides |= PSIDE_BACK; + } + +// assert(sides != 0); + + return sides; +} +#endif +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits) +{ + int i, num; + plane_t *plane; + int s; + + *numsplits = 0; + + plane = &mapplanes[planenum]; + +#ifdef ME + //fast axial cases + if (plane->type < 3) + { + if (plane->dist + PLANESIDE_EPSILON < brush->mins[plane->type]) + return PSIDE_FRONT; + if (plane->dist - PLANESIDE_EPSILON > brush->maxs[plane->type]) + return PSIDE_BACK; + } //end if +#endif //ME + + // if the brush actually uses the planenum, + // we can tell the side for sure + for (i = 0; i < brush->numsides; i++) + { + num = brush->sides[i].planenum; + if (num >= MAX_MAPFILE_PLANES) + Error ("bad planenum"); + if (num == planenum) + return PSIDE_BACK|PSIDE_FACING; + if (num == (planenum ^ 1) ) + return PSIDE_FRONT|PSIDE_FACING; + + } + + // box on plane side + s = BoxOnPlaneSide (brush->mins, brush->maxs, plane); + + // if both sides, count the visible faces split + if (s == PSIDE_BOTH) + { + *numsplits += 3; + } + + return s; +} //end of the function QuickTestBrushToPlanenum +*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int TestBrushToPlanenum( bspbrush_t *brush, int planenum, + int *numsplits, qboolean *hintsplit, int *epsilonbrush ) { + int i, j, num; + plane_t *plane; + int s = 0; + winding_t *w; + vec_t d, d_front, d_back; + int front, back; + + *numsplits = 0; + *hintsplit = false; + + plane = &mapplanes[planenum]; + + // if the brush actually uses the planenum, we can tell the side for sure + for ( i = 0; i < brush->numsides; i++ ) + { + num = brush->sides[i].planenum; + if ( num >= MAX_MAPFILE_PLANES ) { + Error( "bad planenum" ); + } + if ( num == planenum ) { + //we don't need to test this side plane again + brush->sides[i].flags |= SFL_TESTED; + return PSIDE_BACK | PSIDE_FACING; + } //end if + if ( num == ( planenum ^ 1 ) ) { + //we don't need to test this side plane again + brush->sides[i].flags |= SFL_TESTED; + return PSIDE_FRONT | PSIDE_FACING; + } //end if + } //end for + + // box on plane side +#ifdef MRE_ET + s = MrE_BoxOnPlaneSide( brush->mins, brush->maxs, plane, BRUSH_EPSILON ); +#else + s = BoxOnPlaneSide( brush->mins, brush->maxs, plane ); +#endif + + if ( s != PSIDE_BOTH ) { + return s; + } + + // if both sides, count the visible faces split + d_front = d_back = 0.0f; + + for ( i = 0; i < brush->numsides; i++ ) + { + if ( brush->sides[i].texinfo == TEXINFO_NODE ) { + continue; // on node, don't worry about splits + } +// if (!(brush->sides[i].flags & SFL_VISIBLE)) +// continue; // we don't care about non-visible + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + front = back = 0; + for ( j = 0; j < w->numpoints; j++ ) { + d = DotProduct( w->p[j], plane->normal ) - plane->dist; + if ( d > d_front ) { + d_front = d; + } + if ( d < d_back ) { + d_back = d; + } + if ( d > BRUSH_EPSILON ) { + front = 1; + } + if ( d < -BRUSH_EPSILON ) { + back = 1; + } + } + if ( front && back ) { + if ( !( brush->sides[i].surf & SURF_SKIP ) ) { + ( *numsplits )++; + if ( brush->sides[i].surf & SURF_HINT ) { + *hintsplit = true; + } + } + } + } + + if ( ( d_front > 0.0 && d_front < 1.0 ) || ( d_back < 0.0 && d_back > -1.0 ) ) { + ( *epsilonbrush )++; + } + +#if 0 + if ( *numsplits == 0 ) { // didn't really need to be split + if ( front ) { + s = PSIDE_FRONT; + } else if ( back ) { + s = PSIDE_BACK; + } else { s = 0;} + } +#endif + + return s; +} //end of the function TestBrushToPlanenum +//=========================================================================== +// Returns true if the winding would be crunched out of +// existance by the vertex snapping. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define EDGE_LENGTH 0.2 +qboolean WindingIsTiny( winding_t *w ) { +#if 0 + if ( WindingArea( w ) < 1 ) { + return true; + } + return false; +#else + int i, j; + vec_t len; + vec3_t delta; + int edges; + + edges = 0; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + j = i == w->numpoints - 1 ? 0 : i + 1; + VectorSubtract( w->p[j], w->p[i], delta ); + len = VectorLength( delta ); + if ( len > EDGE_LENGTH ) { + if ( ++edges == 3 ) { + return false; + } + } + } + return true; +#endif +} +//=========================================================================== +// Returns true if the winding still has one of the points +// from basewinding for plane +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsHuge( winding_t *w ) { + int i, j; + + for ( i = 0 ; i < w->numpoints ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + if ( w->p[i][j] < -BOGUS_RANGE + 1 || w->p[i][j] > BOGUS_RANGE - 1 ) { + return true; + } + } + return false; +} //end of the function WindingIsHuge +//=========================================================================== +// creates a leaf out of the given nodes with the given brushes +// +// FIXME: use the origin->expansionbbox to find areas with different +// presence types +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LeafNode( node_t *node, bspbrush_t *brushes ) { + bspbrush_t *b; + int i; + + node->side = NULL; + node->planenum = PLANENUM_LEAF; + node->contents = 0; + + for ( b = brushes; b; b = b->next ) + { + // if the brush is solid and all of its sides are on nodes, + // it eats everything + if ( b->original->contents & CONTENTS_SOLID ) { + for ( i = 0 ; i < b->numsides ; i++ ) + if ( b->sides[i].texinfo != TEXINFO_NODE ) { + break; + } + if ( i == b->numsides ) { + node->contents = CONTENTS_SOLID; + break; + } //end if + } //end if + node->contents |= b->original->contents; + } //end for + + if ( create_aas ) { + node->expansionbboxes = 0; + node->contents = 0; + for ( b = brushes; b; b = b->next ) + { + node->expansionbboxes |= b->original->expansionbbox; + node->contents |= b->original->contents; + if ( b->original->modelnum ) { + node->modelnum = b->original->modelnum; + } + } //end for + if ( node->contents & CONTENTS_SOLID ) { + if ( node->expansionbboxes != cfg.allpresencetypes ) { + node->contents &= ~CONTENTS_SOLID; + } //end if + } //end if + } //end if + + if ( brushMap && ( node->contents & brushMapContents ) ) { + WriteBSPBrush( brushMap, node->volume ); + } + + node->brushlist = brushes; +} //end of the function LeafNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CheckPlaneAgainstParents( int pnum, node_t *node ) { + node_t *p; + + for ( p = node->parent; p; p = p->parent ) + { + if ( p->planenum == pnum ) { + Error( "Tried parent" ); + } + } //end for +} //end of the function CheckPlaneAgainstParants +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean CheckPlaneAgainstVolume( int pnum, node_t *node ) { + bspbrush_t *front, *back; + qboolean good; + + SplitBrush( node->volume, pnum, &front, &back ); + + good = ( front && back ); + + if ( front ) { + FreeBrush( front ); + } + if ( back ) { + FreeBrush( back ); + } + + return good; +} //end of the function CheckPlaneAgaintsVolume +//=========================================================================== +// Using a hueristic, choses one of the sides out of the brushlist +// to partition the brushes with. +// Returns NULL if there are no valid planes to split with.. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +/* + + //perfect for fact2: + + value = 5*facing - 7*splits - abs(front-back); + if (mapplanes[pnum].type < 3) + value+=3; // axial is better +*/ + +side_t *SelectSplitSide( bspbrush_t *brushes, node_t *node ) { + int value, bestvalue; + bspbrush_t *brush, *test; + side_t *side, *bestside; + int i, pass, numpasses; + //int j; + int pnum; + int s; + int front, back, both, facing, splits; + int bsplits; + int bestsplits; + int epsilonbrush; + qboolean hintsplit = false; + + bestside = NULL; + bestvalue = -9999999; + bestsplits = 0; + + // the search order goes: visible-structural, visible-detail, + // nonvisible-structural, nonvisible-detail. + // If any valid plane is available in a pass, no further + // passes will be tried. + numpasses = 2; + for ( pass = 0; pass < numpasses; pass++ ) + { + for ( brush = brushes; brush; brush = brush->next ) + { + // only check detail the second pass +// if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) ) +// continue; +// if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) ) +// continue; + for ( i = 0; i < brush->numsides; i++ ) + { + side = brush->sides + i; +// if (side->flags & SFL_BEVEL) +// continue; // never use a bevel as a spliter +#ifndef MRE_ET + if ( !side->winding ) { + continue; // nothing visible, so it can't split + } +#endif + if ( side->texinfo == TEXINFO_NODE ) { + continue; // allready a node splitter + } + if ( side->flags & SFL_TESTED ) { + continue; // we allready have metrics for this plane + } +// if (side->surf & SURF_SKIP) +// continue; // skip surfaces are never chosen +// if (!(side->flags & SFL_VISIBLE) && (pass < 2)) +// continue; // only check visible faces on first pass + if ( ( side->flags & SFL_CURVE ) && ( pass < 1 ) ) { + continue; // only check curves on the second pass + + } + pnum = side->planenum; + pnum &= ~1; // allways use positive facing plane + + CheckPlaneAgainstParents( pnum, node ); + + if ( !CheckPlaneAgainstVolume( pnum, node ) ) { + continue; // would produce a tiny volume + + } + front = 0; + back = 0; + both = 0; + facing = 0; + splits = 0; + epsilonbrush = 0; + + //inner loop: optimize + for ( test = brushes; test; test = test->next ) + { + s = TestBrushToPlanenum( test, pnum, &bsplits, &hintsplit, &epsilonbrush ); + + splits += bsplits; + test->testside = s; + if ( s & PSIDE_FACING ) { + facing++; + } + if ( s & PSIDE_FRONT ) { + front++; + } + if ( s & PSIDE_BACK ) { + back++; + } + if ( s == PSIDE_BOTH ) { + both++; + } + } + + // give a value estimate for using this plane + if ( mapplanes[pnum].type < 3 ) { + value = 20 * facing - 10 * splits - abs( front - back ) - epsilonbrush * 1000; + } else { + value = 15 * facing - 10 * splits - abs( front - back ) - epsilonbrush * 1000; + } + +#ifndef MRE_ET + // never split a hint side except with another hint + if ( hintsplit && !( side->surf & SURF_HINT ) ) { + value = -9999999; + } +#endif + + // save off the side test so we don't need + // to recalculate it when we actually seperate + // the brushes + if ( value > bestvalue ) { + bestvalue = value; + bestside = side; + bestsplits = splits; + for ( test = brushes; test ; test = test->next ) + test->side = test->testside; + } + } + } + + // if we found a good plane, don't bother trying any other passes + if ( bestside ) { + if ( pass > 1 ) { + if ( numthreads == 1 ) { + c_nonvis++; + } + } + if ( pass > 0 ) { + node->detail_seperator = true; // not needed for vis + } + break; + } + } + + // + // clear all the tested flags we set + // + for ( brush = brushes ; brush ; brush = brush->next ) + { + for ( i = 0; i < brush->numsides; i++ ) + { + brush->sides[i].flags &= ~SFL_TESTED; + } + } + + return bestside; +} //end of the function SelectSplitSide +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BrushMostlyOnSide( bspbrush_t *brush, plane_t *plane ) { + int i, j; + winding_t *w; + vec_t d, max; + int side; + + max = 0; + side = PSIDE_FRONT; + for ( i = 0 ; i < brush->numsides ; i++ ) + { + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + for ( j = 0 ; j < w->numpoints ; j++ ) + { + d = DotProduct( w->p[j], plane->normal ) - plane->dist; + if ( d > max ) { + max = d; + side = PSIDE_FRONT; + } + if ( -d > max ) { + max = -d; + side = PSIDE_BACK; + } + } + } + return side; +} //end of the function BrushMostlyOnSide +//=========================================================================== +// Generates two new brushes, leaving the original +// unchanged +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +#ifdef MRE_ET + +int SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back ) { + int res, i, j; + side_t *side, *cs; + float dist, maxBack, maxFront, *maxBackWinding, *maxFrontWinding; + winding_t *w, *mid, *frontSide, *backSide; + plane_t *plane, *splitPlane; + vec3_t normal; + + plane = &mapplanes[planenum]; + + if ( front ) { + *front = NULL; + } + if ( back ) { + *back = NULL; + } + + for ( i = 0; i < brush->numsides; i++ ) { + if ( brush->sides[i].planenum == planenum ) { + if ( back ) { + *back = CopyBrush( brush ); + } + return PSIDE_BACK; + } + if ( brush->sides[i].planenum == ( planenum ^ 1 ) ) { + if ( front ) { + *front = CopyBrush( brush ); + } + return PSIDE_FRONT; + } + } + + res = MrE_BoxOnPlaneSide( brush->mins, brush->maxs, plane, -BRUSH_EPSILON ); + if ( res == PSIDE_FRONT ) { + if ( front ) { + *front = CopyBrush( brush ); + } + return PSIDE_FRONT; + } + if ( res == PSIDE_BACK ) { + if ( back ) { + *back = CopyBrush( brush ); + } + return PSIDE_BACK; + } + + maxBackWinding = (float *) _alloca( brush->numsides * sizeof( float ) ); + maxFrontWinding = (float *) _alloca( brush->numsides * sizeof( float ) ); + + maxFront = maxBack = 0.0f; + for ( i = 0; i < brush->numsides; i++ ) { + side = &brush->sides[i]; + + w = side->winding; + + if ( !w ) { + continue; + } + + maxBackWinding[i] = 10.0f; + maxFrontWinding[i] = -10.0f; + + for ( j = 0; j < w->numpoints; j++ ) { + + dist = DotProduct( plane->normal, w->p[j] ) - plane->dist; + if ( dist > maxFrontWinding[i] ) { + maxFrontWinding[i] = dist; + } + if ( dist < maxBackWinding[i] ) { + maxBackWinding[i] = dist; + } + } + + if ( maxFrontWinding[i] > maxFront ) { + maxFront = maxFrontWinding[i]; + } + if ( maxBackWinding[i] < maxBack ) { + maxBack = maxBackWinding[i]; + } + } + + if ( maxFront < BRUSH_EPSILON ) { + if ( back ) { + *back = CopyBrush( brush ); + } + return PSIDE_BACK; + } + + if ( maxBack > -BRUSH_EPSILON ) { + if ( front ) { + *front = CopyBrush( brush ); + } + return PSIDE_FRONT; + } + + mid = BaseWindingForPlane( plane->normal, plane->dist ); + + for ( i = 0; i < brush->numsides && mid; i++ ) { + // keep the winding if on the clip plane + splitPlane = &mapplanes[brush->sides[i].planenum ^ 1]; + ChopWindingInPlace( &mid, splitPlane->normal, splitPlane->dist, BRUSH_EPSILON ); + } + + if ( mid ) { + if ( WindingIsTiny( mid ) ) { + FreeWinding( mid ); + mid = NULL; + } else if ( WindingIsHuge( mid ) ) { + // if the winding is huge the this brush is unbounded + FreeWinding( mid ); + mid = NULL; + } + } + + if ( !mid ) { + if ( maxFront > -maxBack ) { + if ( front ) { + *front = CopyBrush( brush ); + } + return PSIDE_FRONT; + } else { + if ( back ) { + *back = CopyBrush( brush ); + } + return PSIDE_BACK; + } + } + + if ( !front || !back ) { + FreeWinding( mid ); + return PSIDE_BOTH; + } + + *front = AllocBrush( brush->numsides + 1 ); + ( *front )->original = brush->original; + *back = AllocBrush( brush->numsides + 1 ); + ( *back )->original = brush->original; + + for ( i = 0; i < brush->numsides; i++ ) { + side = &brush->sides[i]; + + if ( !side->winding ) { + continue; + } + + // if completely at the front + if ( maxBackWinding[i] >= BRUSH_EPSILON ) { + cs = &( *front )->sides[( *front )->numsides]; + ( *front )->numsides++; + *cs = *side; + cs->winding = CopyWinding( side->winding ); + cs->flags &= ~SFL_TESTED; + } + // if completely at the back + else if ( maxFrontWinding[i] <= -BRUSH_EPSILON ) { + cs = &( *back )->sides[( *back )->numsides]; + ( *back )->numsides++; + *cs = *side; + cs->winding = CopyWinding( side->winding ); + cs->flags &= ~SFL_TESTED; + } else { + // split the side + ClipWindingEpsilon( side->winding, plane->normal, plane->dist, BRUSH_EPSILON, &frontSide, &backSide ); + if ( frontSide ) { + cs = &( *front )->sides[( *front )->numsides]; + ( *front )->numsides++; + *cs = *side; + cs->winding = frontSide; + cs->flags &= ~SFL_TESTED; + } else if ( maxFrontWinding[i] > -BRUSH_EPSILON ) { + // favor an over constrained brush + cs = &( *front )->sides[( *front )->numsides]; + ( *front )->numsides++; + *cs = *side; + cs->winding = CopyWinding( side->winding ); + ChopWindingInPlace( &cs->winding, plane->normal, plane->dist - ( BRUSH_EPSILON + 0.02f ), 0.01f ); + assert( cs->winding ); + cs->flags &= ~SFL_TESTED; + + } + if ( backSide ) { + cs = &( *back )->sides[( *back )->numsides]; + ( *back )->numsides++; + *cs = *side; + cs->winding = backSide; + cs->flags &= ~SFL_TESTED; + } else if ( maxBackWinding[i] < BRUSH_EPSILON ) { + // favor an over constrained brush + cs = &( *back )->sides[( *back )->numsides]; + ( *back )->numsides++; + *cs = *side; + cs->winding = CopyWinding( side->winding ); + VectorNegate( plane->normal, normal ); + ChopWindingInPlace( &cs->winding, normal, -( plane->dist + ( BRUSH_EPSILON + 0.02f ) ), 0.01f ); + assert( cs->winding ); + cs->flags &= ~SFL_TESTED; + } + } + } + + BoundBrush( ( *front ) ); + BoundBrush( ( *back ) ); + + cs = &( *front )->sides[( *front )->numsides]; + ( *front )->numsides++; + cs->planenum = planenum ^ 1; + cs->winding = ReverseWinding( mid ); + cs->texinfo = TEXINFO_NODE; // never use these sides as splitters + cs->contents = ( *front )->sides[0].contents; + cs->surf = 0; + cs->flags &= ~( SFL_VISIBLE | SFL_TESTED ); + + cs = &( *back )->sides[( *back )->numsides]; + ( *back )->numsides++; + cs->planenum = planenum; + cs->winding = mid; + cs->texinfo = TEXINFO_NODE; // never use these sides as splitters + cs->contents = ( *back )->sides[0].contents; + cs->surf = 0; + cs->flags &= ~( SFL_VISIBLE | SFL_TESTED ); + + // test for valid brushes + /* + { + float volume; + bspbrush_t *b; + + for ( i = 0; i < 2 ; i++ ) { + b = i ? *front : *back; + for ( j = 0; j < 3; j++ ) { + if ( b->mins[j] < -MAX_MAP_BOUNDS || b->maxs[j] > MAX_MAP_BOUNDS ) { + break; + } + } + volume = BrushVolume( b ); + if ( j < 3 || b->numsides < 3 || volume < 1.0f ) { + FreeBrush( b ); + if ( i ) { + *front = NULL; + } + else { + *back = NULL; + } + } + } + } + */ + + return PSIDE_BOTH; +} + +#else + +int SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back ) { + bspbrush_t *b[2]; + int i, j; + winding_t *w, *cw[2], *midwinding; + plane_t *plane, *plane2; + side_t *s, *cs; + float d, d_front, d_back; + + *front = *back = NULL; + plane = &mapplanes[planenum]; + + // check all points + d_front = d_back = 0; + for ( i = 0 ; i < brush->numsides ; i++ ) + { + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + for ( j = 0 ; j < w->numpoints ; j++ ) + { + d = DotProduct( w->p[j], plane->normal ) - plane->dist; + if ( d > 0 && d > d_front ) { + d_front = d; + } + if ( d < 0 && d < d_back ) { + d_back = d; + } + } + } + + if ( d_front < 0.2 ) { // PLANESIDE_EPSILON) + // only on back + *back = CopyBrush( brush ); + return PSIDE_BACK; + } + if ( d_back > -0.2 ) { // PLANESIDE_EPSILON) + // only on front + *front = CopyBrush( brush ); + return PSIDE_FRONT; + } + + // create a new winding from the split plane + + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( i = 0 ; i < brush->numsides && w ; i++ ) + { + plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; + ChopWindingInPlace( &w, plane2->normal, plane2->dist, 0 ); // PLANESIDE_EPSILON); + } + + if ( !w || WindingIsTiny( w ) ) { // the brush isn't really split + int side; + + //free a possible winding + if ( w ) { + FreeWinding( w ); + } + + side = BrushMostlyOnSide( brush, plane ); + if ( side == PSIDE_FRONT ) { + *front = CopyBrush( brush ); + return PSIDE_FRONT; + } + if ( side == PSIDE_BACK ) { + *back = CopyBrush( brush ); + return PSIDE_BACK; + } + } + + if ( WindingIsHuge( w ) ) { + Log_Write( "WARNING: huge winding\n" ); + } + + midwinding = w; + + // split it for real + + for ( i = 0 ; i < 2 ; i++ ) + { + b[i] = AllocBrush( brush->numsides + 1 ); + b[i]->original = brush->original; + } + + // split all the current windings + + for ( i = 0 ; i < brush->numsides ; i++ ) + { + s = &brush->sides[i]; + w = s->winding; + if ( !w ) { + continue; + } + ClipWindingEpsilon( w, plane->normal, plane->dist, + 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); + for ( j = 0 ; j < 2 ; j++ ) + { + if ( !cw[j] ) { + continue; + } +#if 0 + if ( WindingIsTiny( cw[j] ) ) { + FreeWinding( cw[j] ); + continue; + } +#endif + cs = &b[j]->sides[b[j]->numsides]; + b[j]->numsides++; + *cs = *s; +// cs->planenum = s->planenum; +// cs->texinfo = s->texinfo; +// cs->original = s->original; + cs->winding = cw[j]; + cs->flags &= ~SFL_TESTED; + } + } + + + // see if we have valid polygons on both sides + + for ( i = 0 ; i < 2 ; i++ ) + { + BoundBrush( b[i] ); + for ( j = 0 ; j < 3 ; j++ ) + { + if ( b[i]->mins[j] < -MAX_MAP_BOUNDS || b[i]->maxs[j] > MAX_MAP_BOUNDS ) { + Log_Write( "bogus brush after clip" ); + break; + } + } + + if ( b[i]->numsides < 3 || j < 3 ) { + FreeBrush( b[i] ); + b[i] = NULL; + } + } + + if ( !( b[0] && b[1] ) ) { + if ( !b[0] && !b[1] ) { + Log_Write( "split removed brush\r\n" ); + } else { + Log_Write( "split not on both sides\r\n" ); + } + if ( b[0] ) { + FreeBrush( b[0] ); + *front = CopyBrush( brush ); + return PSIDE_FRONT; + } + if ( b[1] ) { + FreeBrush( b[1] ); + *back = CopyBrush( brush ); + return PSIDE_BACK; + } + } + + // add the midwinding to both sides + for ( i = 0 ; i < 2 ; i++ ) + { + cs = &b[i]->sides[b[i]->numsides]; + b[i]->numsides++; + + cs->planenum = planenum ^ i ^ 1; + cs->texinfo = TEXINFO_NODE; //never use these sides as splitters + cs->flags &= ~SFL_VISIBLE; + cs->flags &= ~SFL_TESTED; + if ( i == 0 ) { + cs->winding = CopyWinding( midwinding ); + } else { + cs->winding = midwinding; + } + } + + { + vec_t v1; + int i; + + for ( i = 0; i < 2; i++ ) + { + v1 = BrushVolume( b[i] ); + if ( v1 < 1.0 ) { + FreeBrush( b[i] ); + b[i] = NULL; + //Log_Write("tiny volume after clip"); + } + } + if ( !b[0] && !b[1] ) { + Log_Write( "two tiny brushes\r\n" ); + } //end if + } + + *front = b[0]; + *back = b[1]; + + return PSIDE_FRONT | PSIDE_BACK; +} //end of the function SplitBrush + +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SplitBrushList( bspbrush_t *brushes, + node_t *node, bspbrush_t **front, bspbrush_t **back ) { + bspbrush_t *brush, *newbrush, *newbrush2; + side_t *side; + int sides; + int i; + + *front = *back = NULL; + + for ( brush = brushes ; brush ; brush = brush->next ) + { + sides = brush->side; + + if ( sides == PSIDE_BOTH ) { // split into two brushes + SplitBrush( brush, node->planenum, &newbrush, &newbrush2 ); + if ( newbrush ) { + newbrush->next = *front; + *front = newbrush; + } //end if + if ( newbrush2 ) { + newbrush2->next = *back; + *back = newbrush2; + } //end if + continue; + } //end if + + newbrush = CopyBrush( brush ); + + // if the planenum is actualy a part of the brush + // find the plane and flag it as used so it won't be tried + // as a splitter again + if ( sides & PSIDE_FACING ) { + for ( i = 0 ; i < newbrush->numsides ; i++ ) + { + side = newbrush->sides + i; + if ( ( side->planenum & ~1 ) == node->planenum ) { + side->texinfo = TEXINFO_NODE; + } + } //end for + } //end if + + if ( sides & PSIDE_FRONT ) { + newbrush->next = *front; + *front = newbrush; + continue; + } //end if + if ( sides & PSIDE_BACK ) { + newbrush->next = *back; + *back = newbrush; + continue; + } //end if + } //end for +} //end of the function SplitBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CheckBrushLists( bspbrush_t *brushlist1, bspbrush_t *brushlist2 ) { + bspbrush_t *brush1, *brush2; + + for ( brush1 = brushlist1; brush1; brush1 = brush1->next ) + { + for ( brush2 = brushlist2; brush2; brush2 = brush2->next ) + { + assert( brush1 != brush2 ); + } //end for + } //end for +} //end of the function CheckBrushLists +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int numrecurse = 0; + +node_t *BuildTree_r( node_t *node, bspbrush_t *brushes ) { + node_t *newnode; + side_t *bestside; + int i, totalmem; + bspbrush_t *children[2]; + + qprintf( "\r%6d", numrecurse ); + numrecurse++; + + if ( numthreads == 1 ) { + totalmem = WindingMemory() + c_nodememory + c_brushmemory; + if ( totalmem > c_peak_totalbspmemory ) { + c_peak_totalbspmemory = totalmem; + } + c_nodes++; + } //endif + + if ( drawflag ) { + DrawBrushList( brushes, node ); + } + + // find the best plane to use as a splitter + bestside = SelectSplitSide( brushes, node ); + if ( !bestside ) { + // leaf node + node->side = NULL; + node->planenum = -1; + LeafNode( node, brushes ); + if ( node->contents & CONTENTS_SOLID ) { + c_solidleafnodes++; + } + if ( create_aas ) { + //free up memory!!! + FreeBrushList( node->brushlist ); + node->brushlist = NULL; + //free the node volume brush + if ( node->volume ) { + FreeBrush( node->volume ); + node->volume = NULL; + } //end if + } //end if + return node; + } //end if + + // this is a splitplane node + node->side = bestside; + node->planenum = bestside->planenum & ~1; // always use front facing + + //split the brush list in two for both children + SplitBrushList( brushes, node, &children[0], &children[1] ); + //free the old brush list + FreeBrushList( brushes ); + + // allocate children before recursing + for ( i = 0; i < 2; i++ ) + { + newnode = AllocNode(); + newnode->parent = node; + node->children[i] = newnode; + } //end for + + //split the volume brush of the node for the children + SplitBrush( node->volume, node->planenum, &node->children[0]->volume, + &node->children[1]->volume ); + + if ( create_aas ) { + //free the volume brush + if ( node->volume ) { + FreeBrush( node->volume ); + node->volume = NULL; + } //end if + } //end if + // recursively process children + for ( i = 0; i < 2; i++ ) + { + node->children[i] = BuildTree_r( node->children[i], children[i] ); + } //end for + + return node; +} //end of the function BuildTree_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *firstnode; //first node in the list +node_t *lastnode; //last node in the list +int nodelistsize; //number of nodes in the list +int use_nodequeue = 0; //use nodequeue, otherwise a node stack is used +int numwaiting = 0; + +void ( *AddNodeToList )( node_t *node ); + +//add the node to the front of the node list +//(effectively using a node stack) +void AddNodeToStack( node_t *node ) { + ThreadLock(); + + node->next = firstnode; + firstnode = node; + if ( !lastnode ) { + lastnode = node; + } + nodelistsize++; + + ThreadUnlock(); + // + ThreadSemaphoreIncrease( 1 ); +} //end of the function AddNodeToStack +//add the node to the end of the node list +//(effectively using a node queue) +void AddNodeToQueue( node_t *node ) { + ThreadLock(); + + node->next = NULL; + if ( lastnode ) { + lastnode->next = node; + } else { firstnode = node;} + lastnode = node; + nodelistsize++; + + ThreadUnlock(); + // + ThreadSemaphoreIncrease( 1 ); +} //end of the function AddNodeToQueue +//get the first node from the front of the node list +node_t *NextNodeFromList( void ) { + node_t *node; + + ThreadLock(); + numwaiting++; + if ( !firstnode ) { + if ( numwaiting >= GetNumThreads() ) { + ThreadSemaphoreIncrease( GetNumThreads() ); + } + } //end if + ThreadUnlock(); + + ThreadSemaphoreWait(); + + ThreadLock(); + + numwaiting--; + + node = firstnode; + if ( firstnode ) { + firstnode = firstnode->next; + nodelistsize--; + } //end if + if ( !firstnode ) { + lastnode = NULL; + } + + ThreadUnlock(); + + return node; +} //end of the function NextNodeFromList +//returns the size of the node list +int NodeListSize( void ) { + int size; + + ThreadLock(); + size = nodelistsize; + ThreadUnlock(); + + return size; +} //end of the function NodeListSize +// +void IncreaseNodeCounter( void ) { + ThreadLock(); + //if (verbose) printf("\r%6d", numrecurse++); + qprintf( "\r%6d", numrecurse++ ); + //qprintf("\r%6d %d, %5d ", numrecurse++, GetNumThreads(), nodelistsize); + ThreadUnlock(); +} //end of the function IncreaseNodeCounter +//thread function, gets nodes from the nodelist and processes them +void BuildTreeThread( int threadid ) { + node_t *newnode, *node; + side_t *bestside; + int i, totalmem; + bspbrush_t *brushes; + + for ( node = NextNodeFromList(); node; ) + { + //if the nodelist isn't empty try to add another thread + //if (NodeListSize() > 10) AddThread(BuildTreeThread); + //display the number of nodes processed so far + if ( numthreads == 1 ) { + IncreaseNodeCounter(); + } + + brushes = node->brushlist; + + if ( numthreads == 1 ) { + totalmem = WindingMemory() + c_nodememory + c_brushmemory; + if ( totalmem > c_peak_totalbspmemory ) { + c_peak_totalbspmemory = totalmem; + } //end if + c_nodes++; + } //endif + + if ( drawflag ) { + DrawBrushList( brushes, node ); + } //end if + + if ( cancelconversion ) { + bestside = NULL; + } //end if + else + { + // find the best plane to use as a splitter + bestside = SelectSplitSide( brushes, node ); + } //end else + //if there's no split side left + if ( !bestside ) { + //create a leaf out of the node + LeafNode( node, brushes ); + if ( node->contents & CONTENTS_SOLID ) { + c_solidleafnodes++; + } + + if ( create_aas ) { + //free up memory!!! + FreeBrushList( node->brushlist ); + node->brushlist = NULL; + } //end if + //free the node volume brush (it is not used anymore) + if ( node->volume ) { + FreeBrush( node->volume ); + node->volume = NULL; + } //end if + node = NextNodeFromList(); + continue; + } //end if + + // this is a splitplane node + node->side = bestside; + node->planenum = bestside->planenum & ~1; //always use front facing + + //allocate children + for ( i = 0; i < 2; i++ ) + { + newnode = AllocNode(); + newnode->parent = node; + node->children[i] = newnode; + } //end for + + //split the brush list in two for both children + SplitBrushList( brushes, node, &node->children[0]->brushlist, &node->children[1]->brushlist ); + + CheckBrushLists( node->children[0]->brushlist, node->children[1]->brushlist ); + //free the old brush list + FreeBrushList( brushes ); + node->brushlist = NULL; + + //split the volume brush of the node for the children + SplitBrush( node->volume, node->planenum, &node->children[0]->volume, + &node->children[1]->volume ); + + if ( !node->children[0]->volume || !node->children[1]->volume ) { + Error( "child without volume brush" ); + } //end if + + //free the volume brush + if ( node->volume ) { + FreeBrush( node->volume ); + node->volume = NULL; + } //end if + + //add both children to the node list + //AddNodeToList(node->children[0]); + AddNodeToList( node->children[1] ); + node = node->children[0]; + } //end while + RemoveThread( threadid ); +} //end of the function BuildTreeThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BuildGrid_r( node_t *node ) { + int axis, i, splits; + float dist; + vec3_t halfSize, normal; + node_t *newnode; + bspbrush_t *brush; + + if ( !node->brushlist ) { + LeafNode( node, node->brushlist ); + return; + } + + VectorSubtract( node->volume->maxs, node->volume->mins, halfSize ); + VectorScale( halfSize, 0.5f, halfSize ); + for ( axis = 0; axis < 3; axis++ ) { + if ( halfSize[axis] > bsp_grid_size ) { + dist = bsp_grid_size * ( floor( ( node->volume->mins[axis] + halfSize[axis] ) / bsp_grid_size ) + 1 ); + } else { + dist = bsp_grid_size * ( floor( node->volume->mins[axis] / bsp_grid_size ) + 1 ); + } + + if ( dist > node->volume->mins[axis] + 1.0f && dist < node->volume->maxs[axis] - 1.0f ) { + break; + } + } + + if ( axis >= 3 ) { + AddNodeToList( node ); + return; + } + + VectorClear( normal ); + normal[axis] = 1.0f; + + node->side = NULL; + node->planenum = FindFloatPlane( normal, (int) dist, 0, NULL ) & ~1; //always use front facing + + // allocate children + for ( i = 0; i < 2; i++ ) { + newnode = AllocNode(); + newnode->parent = node; + node->children[i] = newnode; + } + + // set brush->side for all brushes + for ( brush = node->brushlist; brush; brush = brush->next ) { + splits = 0; + brush->side = TestBrushToPlanenum( brush, node->planenum, &splits, &splits, &splits ); + } + + //split the brush list in two for both children + SplitBrushList( node->brushlist, node, &node->children[0]->brushlist, &node->children[1]->brushlist ); + + CheckBrushLists( node->children[0]->brushlist, node->children[1]->brushlist ); + // free the old brush list + FreeBrushList( node->brushlist ); + node->brushlist = NULL; + + // split the volume brush of the node for the children + SplitBrush( node->volume, node->planenum, &node->children[0]->volume, &node->children[1]->volume ); + + if ( !node->children[0]->volume || !node->children[1]->volume ) { + Error( "child without volume brush" ); + } + + // free the volume brush + if ( node->volume ) { + FreeBrush( node->volume ); + node->volume = NULL; + } + + BuildGrid_r( node->children[0] ); + BuildGrid_r( node->children[1] ); +} +//=========================================================================== +// build the bsp tree using a node list +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BuildTree( tree_t *tree ) { + int i; + + firstnode = NULL; + lastnode = NULL; + //use a node queue or node stack + if ( use_nodequeue ) { + AddNodeToList = AddNodeToQueue; + } else { AddNodeToList = AddNodeToStack;} + //setup thread locking + ThreadSetupLock(); + ThreadSetupSemaphore(); + numwaiting = 0; + // + Log_Print( "%6d threads max\n", numthreads ); + if ( use_nodequeue ) { + Log_Print( "breadth first bsp building\n" ); + } else { Log_Print( "depth first bsp building\n" );} + qprintf( "%6d splits", 0 ); + +#ifdef MRE_ET + BuildGrid_r( tree->headnode ); +#else + //add the first node to the list + AddNodeToList( tree->headnode ); +#endif + + //start the threads + for ( i = 0; i < numthreads; i++ ) + AddThread( BuildTreeThread ); + //wait for all added threads to be finished + WaitForAllThreadsFinished(); + //shutdown the thread locking + ThreadShutdownLock(); + ThreadShutdownSemaphore(); +} //end of the function BuildTree +//=========================================================================== +// The incoming brush list will be freed before exiting +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tree_t *BrushBSP( bspbrush_t *brushlist, vec3_t mins, vec3_t maxs ) { + int i, c_faces, c_nonvisfaces, c_brushes; + bspbrush_t *b; + node_t *node; + tree_t *tree; +#ifndef MRE_ET + vec_t volume; +#endif + + if ( writebrushmap ) { + WriteBrushListToFile( brushlist, "BrushBSP_brushes.map" ); + } + + Log_Print( "-------- Brush BSP ---------\n" ); + + tree = Tree_Alloc(); + + c_faces = 0; + c_nonvisfaces = 0; + c_brushes = 0; + c_totalsides = 0; + for ( b = brushlist; b; b = b->next ) + { + c_brushes++; + +#ifndef MRE_ET + volume = BrushVolume( b ); + if ( volume < microvolume ) { + Log_Print( "WARNING: entity %i, brush %i: microbrush\n", + b->original->entitynum, b->original->brushnum ); + } //end if +#endif + + for ( i = 0 ; i < b->numsides ; i++ ) + { + if ( b->sides[i].flags & SFL_BEVEL ) { + continue; + } + if ( !b->sides[i].winding ) { + continue; + } + if ( b->sides[i].texinfo == TEXINFO_NODE ) { + continue; + } + if ( b->sides[i].flags & SFL_VISIBLE ) { + c_faces++; + } //end if + else + { + c_nonvisfaces++; + //if (create_aas) b->sides[i].texinfo = TEXINFO_NODE; + } //end if + } //end for + c_totalsides += b->numsides; + + AddPointToBounds( b->mins, tree->mins, tree->maxs ); + AddPointToBounds( b->maxs, tree->mins, tree->maxs ); + } //end for + + Log_Print( "%6i brushes\n", c_brushes ); + Log_Print( "%6i visible faces\n", c_faces ); + Log_Print( "%6i nonvisible faces\n", c_nonvisfaces ); + Log_Print( "%6i total sides\n", c_totalsides ); + + c_active_brushes = c_brushes; + c_nodememory = 0; + c_brushmemory = 0; + c_peak_brushmemory = 0; + + c_nodes = 0; + c_nonvis = 0; + node = AllocNode(); + + //volume of first node (head node) + node->volume = BrushFromBounds( mins, maxs ); + // + tree->headnode = node; + //just get some statistics and the mins/maxs of the node + numrecurse = 0; +// qprintf("%6d splits", numrecurse); + + tree->headnode->brushlist = brushlist; + BuildTree( tree ); + + //build the bsp tree with the start node from the brushlist +// node = BuildTree_r(node, brushlist); + + //if the conversion is cancelled + if ( cancelconversion ) { + return tree; + } + + qprintf( "\n" ); + Log_Write( "%6d splits\r\n", numrecurse ); +// Log_Print("%6i visible nodes\n", c_nodes/2 - c_nonvis); +// Log_Print("%6i nonvis nodes\n", c_nonvis); +// Log_Print("%6i leaves\n", (c_nodes+1)/2); +// Log_Print("%6i solid leaf nodes\n", c_solidleafnodes); +// Log_Print("%6i active brushes\n", c_active_brushes); + if ( numthreads == 1 ) { +// Log_Print("%6i KB of node memory\n", c_nodememory >> 10); +// Log_Print("%6i KB of brush memory\n", c_brushmemory >> 10); +// Log_Print("%6i KB of peak brush memory\n", c_peak_brushmemory >> 10); +// Log_Print("%6i KB of winding memory\n", WindingMemory() >> 10); +// Log_Print("%6i KB of peak winding memory\n", WindingPeakMemory() >> 10); + Log_Print( "%6i KB of peak total bsp memory\n", c_peak_totalbspmemory >> 10 ); + } //end if + + if ( writebrushmap ) { + CloseBSPBrushMap(); + } + + return tree; +} //end of the function BrushBSP + diff --git a/src/bspc/bspc.c b/src/bspc/bspc.c new file mode 100644 index 0000000..c13d29f --- /dev/null +++ b/src/bspc/bspc.c @@ -0,0 +1,1208 @@ +/* +=========================================================================== + +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: BSP tool +// Function: +// Programmer: id Software & Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +// Notes: Microsoft Visual C++ optimizations: +// "global optimization" or "full optimization" results +// in micro brushes?? +//=========================================================================== + +#if defined( WIN32 ) || defined( _WIN32 ) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#include "qbsp.h" +#include "l_mem.h" +//#include "l_qfiles.h" +#include "..\botlib\aasfile.h" +#include "..\botlib\be_aas_cluster.h" +#include "..\botlib\be_aas_optimize.h" +#include "aas_create.h" +#include "aas_store.h" +#include "aas_file.h" +#include "aas_cfg.h" +#include "be_aas_bspc.h" + +// Ryan, I changed this from c to d so our designers would know which .exe they had... +#define BSPC_VERSION "2.2" + +// RF +#ifndef BSPC +#define BSPC +#endif + +extern int use_nodequeue; //brushbsp.c +extern int calcgrapplereach; //be_aas_reach.c + +//float subdivide_size = 1024; +//float bsp_grid_size = 2048; +// Gordon: FIXME: test +float subdivide_size = 240; +float bsp_grid_size = 512; +char source[1024]; +char name[1024]; +vec_t microvolume = 1.0; +char outbase[32]; +int entity_num; +aas_settings_t aassettings; + +qboolean noprune; //don't prune nodes (bspc.c) +qboolean glview; //create a gl view +qboolean nodetail; //don't use detail brushes (map.c) +qboolean fulldetail; //use but don't mark detail brushes (map.c) +qboolean onlyents; //only process the entities (bspc.c) +qboolean nomerge; //don't merge bsp node faces (faces.c) +qboolean nowater; //don't use the water brushes (map.c) +qboolean nocsg; //don't carve intersecting brushes (bspc.c) +qboolean noweld; //use unique face vertexes (faces.c) +qboolean noshare; //don't share bsp edges (faces.c) +qboolean nosubdiv; //don't subdivide bsp node faces (faces.c) +qboolean notjunc; //don't create tjunctions (edge melting) (faces.c) +qboolean optimize; //enable optimisation +qboolean leaktest; //perform a leak test +qboolean verboseentities; +qboolean freetree; //free the bsp tree when not needed anymore +qboolean create_aas; //create an .AAS file +qboolean nobrushmerge; //don't merge brushes +qboolean lessbrushes; //create less brushes instead of correct texture placement +qboolean cancelconversion; //true if the conversion is being cancelled +qboolean noliquids; //no liquids when writing map file +qboolean forcesidesvisible; //force all brush sides to be visible when loaded from bsp +qboolean writeaasmap; // +qboolean groundonly; +qboolean writebrushmap; +qboolean noPatches = false; +//qboolean capsule_collision = true; //use capsule collision +qboolean capsule_collision = false; // capsule collision// Ridah, allow to specify an extension for multiple AAS files per map +int mingroundarea = 0; // areas with ground surface area less than this will be removed +qboolean reachableonly; // remove areas with no reachability + +char aas_extension[64]; +// done. + +float VectorDistance( vec3_t v1, vec3_t v2 ) { + vec3_t dir; + + VectorSubtract( v2, v1, dir ); + return VectorLength( dir ); +} + +/* +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ProcessWorldModel (void) +{ + entity_t *e; + tree_t *tree; + qboolean leaked; + int brush_start, brush_end; + + e = &entities[entity_num]; + + brush_start = e->firstbrush; + brush_end = brush_start + e->numbrushes; + leaked = false; + + //process the whole world in one time + tree = ProcessWorldBrushes(brush_start, brush_end); + //create the bsp tree portals + MakeTreePortals(tree); + //mark all leafs that can be reached by entities + if (FloodEntities(tree)) + { + FillOutside(tree->headnode); + } //end if + else + { + Log_Print("**** leaked ****\n"); + leaked = true; + LeakFile(tree); + if (leaktest) + { + Log_Print("--- MAP LEAKED ---\n"); + exit(0); + } //end if + } //end else + + MarkVisibleSides (tree, brush_start, brush_end); + + FloodAreas (tree); + +#ifndef ME + if (glview) WriteGLView(tree, source); +#endif + MakeFaces(tree->headnode); + FixTjuncs(tree->headnode); + + //NOTE: Never prune the nodes because the portals + // are screwed when prunning is done and as + // a result portal writing will crash + //if (!noprune) PruneNodes(tree->headnode); + + WriteBSP(tree->headnode); + + if (!leaked) WritePortalFile(tree); + + Tree_Free(tree); +} //end of the function ProcessWorldModel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ProcessSubModel (void) +{ + entity_t *e; + int start, end; + tree_t *tree; + bspbrush_t *list; + vec3_t mins, maxs; + + e = &entities[entity_num]; + + start = e->firstbrush; + end = start + e->numbrushes; + + mins[0] = mins[1] = mins[2] = -4096; + maxs[0] = maxs[1] = maxs[2] = 4096; + list = MakeBspBrushList(start, end, mins, maxs); + if (!nocsg) list = ChopBrushes (list); + tree = BrushBSP (list, mins, maxs); + MakeTreePortals (tree); + MarkVisibleSides (tree, start, end); + MakeFaces (tree->headnode); + FixTjuncs (tree->headnode); + WriteBSP (tree->headnode); + Tree_Free (tree); +} //end of the function ProcessSubModel +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ProcessModels (void) +{ + BeginBSPFile(); + + for (entity_num = 0; entity_num < num_entities; entity_num++) + { + if (!entities[entity_num].numbrushes) + continue; + + Log_Print("############### model %i ###############\n", nummodels); + BeginModel(); + if (entity_num == 0) ProcessWorldModel(); + else ProcessSubModel(); + EndModel(); + + if (!verboseentities) + verbose = false; // don't bother printing submodels + } //end for + EndBSPFile(); +} //end of the function ProcessModels +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Win_Map2Bsp(char *bspfilename) +{ + double start, end; + char path[1024]; + + start = I_FloatTime(); + + ThreadSetDefault(); + //yeah sure Carmack + //numthreads = 1; // multiple threads aren't helping... + + strcpy(source, ExpandArg(bspfilename)); + StripExtension(source); + + //delete portal and line files + sprintf(path, "%s.prt", source); + remove(path); + sprintf(path, "%s.lin", source); + remove(path); + + strcpy(name, ExpandArg(bspfilename)); + DefaultExtension(name, ".map"); // might be .reg + + Q2_AllocMaxBSP(); + // + SetModelNumbers(); + SetLightStyles(); + ProcessModels(); + //write the BSP + Q2_WriteBSPFile(bspfilename); + + Q2_FreeMaxBSP(); + + end = I_FloatTime(); + Log_Print("%5.0f seconds elapsed\n", end-start); +} //end of the function Win_Map2Bsp +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Map2Bsp(char *mapfilename, char *outputfilename) +{ + double start, end; + char path[1024]; + + start = I_FloatTime (); + + ThreadSetDefault (); + //yeah sure Carmack + //numthreads = 1; //multiple threads aren't helping... + //SetQdirFromPath(bspfilename); + + strcpy(source, ExpandArg(mapfilename)); + StripExtension(source); + + // delete portal and line files + sprintf(path, "%s.prt", source); + remove(path); + sprintf(path, "%s.lin", source); + remove(path); + + strcpy(name, ExpandArg(mapfilename)); + DefaultExtension(name, ".map"); // might be .reg + + // + // if onlyents, just grab the entites and resave + // + if (onlyents) + { + char out[1024]; + + Q2_AllocMaxBSP(); + sprintf (out, "%s.bsp", source); + Q2_LoadBSPFile(out, 0, 0); + num_entities = 0; + + Q2_LoadMapFile(name); + SetModelNumbers(); + SetLightStyles(); + + Q2_UnparseEntities(); + + Q2_WriteBSPFile(out); + // + Q2_FreeMaxBSP(); + } //end if + else + { + // + // start from scratch + // + Q2_AllocMaxBSP(); + //load the map + Q2_LoadMapFile(name); + //create the .bsp file + SetModelNumbers(); + SetLightStyles(); + ProcessModels(); + //write the BSP + Q2_WriteBSPFile(outputfilename); + // + Q2_FreeMaxBSP(); + } //end else + + end = I_FloatTime(); + Log_Print("%5.0f seconds elapsed\n", end-start); +} //end of the function Map2Bsp +*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AASOuputFile( quakefile_t *qf, char *outputpath, char *filename ) { + char ext[MAX_PATH]; + + // + if ( strlen( outputpath ) ) { + strcpy( filename, outputpath ); + //append the bsp file base + AppendPathSeperator( filename, MAX_PATH ); + ExtractFileBase( qf->origname, &filename[strlen( filename )] ); + + // Ridah, add extension + strcat( filename, aas_extension ); + // done. + + //append .aas + strcat( filename, ".aas" ); + return; + } //end if + // + ExtractFileExtension( qf->filename, ext ); + if ( !stricmp( ext, "pk3" ) || !stricmp( ext, "pak" ) || !stricmp( ext, "sin" ) ) { + strcpy( filename, qf->filename ); + while ( strlen( filename ) && + filename[strlen( filename ) - 1] != '\\' && + filename[strlen( filename ) - 1] != '/' ) + { + filename[strlen( filename ) - 1] = '\0'; + } //end while + strcat( filename, "maps" ); + if ( access( filename, 0x04 ) ) { + CreatePath( filename ); + } + //append the bsp file base + AppendPathSeperator( filename, MAX_PATH ); + ExtractFileBase( qf->origname, &filename[strlen( filename )] ); + + // Ridah, add extension + strcat( filename, aas_extension ); + // done. + + //append .aas + strcat( filename, ".aas" ); + } //end if + else + { + strcpy( filename, qf->filename ); + while ( strlen( filename ) && + filename[strlen( filename ) - 1] != '.' ) + { + filename[strlen( filename ) - 1] = '\0'; + } //end while + + // Ridah, add extension + strcat( filename, aas_extension ); + // done. + + strcat( filename, "aas" ); + } //end else +} //end of the function AASOutputFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CreateAASFilesForAllBSPFiles( char *quakepath ) { +#if defined( WIN32 ) | defined( _WIN32 ) + WIN32_FIND_DATA filedata; + HWND handle; + struct _stat statbuf; +#else + glob_t globbuf; + struct stat statbuf; + int j; +#endif + int done; + char filter[_MAX_PATH], bspfilter[_MAX_PATH], aasfilter[_MAX_PATH]; + char aasfile[_MAX_PATH], buf[_MAX_PATH], foldername[_MAX_PATH]; + quakefile_t *qf, *qf2, *files, *bspfiles, *aasfiles; + + strcpy( filter, quakepath ); + AppendPathSeperator( filter, sizeof( filter ) ); + strcat( filter, "*" ); + +#if defined( WIN32 ) | defined( _WIN32 ) + handle = FindFirstFile( filter, &filedata ); + done = ( handle == INVALID_HANDLE_VALUE ); + while ( !done ) + { + _splitpath( filter, foldername, NULL, NULL, NULL ); + _splitpath( filter, NULL, &foldername[strlen( foldername )], NULL, NULL ); + AppendPathSeperator( foldername, _MAX_PATH ); + strcat( foldername, filedata.cFileName ); + _stat( foldername, &statbuf ); +#else + glob( filter, 0, NULL, &globbuf ); + for ( j = 0; j < globbuf.gl_pathc; j++ ) + { + strcpy( foldername, globbuf.gl_pathv[j] ); + stat( foldername, &statbuf ); +#endif + //if it is a folder + if ( statbuf.st_mode & S_IFDIR ) { + // + AppendPathSeperator( foldername, sizeof( foldername ) ); + //get all the bsp files + strcpy( bspfilter, foldername ); + strcat( bspfilter, "maps/*.bsp" ); + files = FindQuakeFiles( bspfilter ); + strcpy( bspfilter, foldername ); + strcat( bspfilter, "*.pk3/maps/*.bsp" ); + bspfiles = FindQuakeFiles( bspfilter ); + for ( qf = bspfiles; qf; qf = qf->next ) if ( !qf->next ) { + break; + } + if ( qf ) { + qf->next = files; + } else { bspfiles = files;} + //get all the aas files + strcpy( aasfilter, foldername ); + strcat( aasfilter, "maps/*.aas" ); + files = FindQuakeFiles( aasfilter ); + strcpy( aasfilter, foldername ); + strcat( aasfilter, "*.pk3/maps/*.aas" ); + aasfiles = FindQuakeFiles( aasfilter ); + for ( qf = aasfiles; qf; qf = qf->next ) if ( !qf->next ) { + break; + } + if ( qf ) { + qf->next = files; + } else { aasfiles = files;} + // + for ( qf = bspfiles; qf; qf = qf->next ) + { + sprintf( aasfile, "%s/%s", qf->pakfile, qf->origname ); + Log_Print( "found %s\n", aasfile ); + strcpy( &aasfile[strlen( aasfile ) - strlen( ".bsp" )], ".aas" ); + for ( qf2 = aasfiles; qf2; qf2 = qf2->next ) + { + sprintf( buf, "%s/%s", qf2->pakfile, qf2->origname ); + if ( !stricmp( aasfile, buf ) ) { + Log_Print( "found %s\n", buf ); + break; + } //end if + } //end for + } //end for + } //end if +#if defined( WIN32 ) | defined( _WIN32 ) + //find the next file + done = !FindNextFile( handle, &filedata ); + } //end while +#else + } //end for + globfree( &globbuf ); +#endif +} //end of the function CreateAASFilesForAllBSPFiles +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +quakefile_t *GetArgumentFiles( int argc, char *argv[], int *i, char *ext ) { + quakefile_t *qfiles, *lastqf, *qf; + int j; + char buf[1024]; + + qfiles = NULL; + lastqf = NULL; + for (; ( *i ) + 1 < argc && argv[( *i ) + 1][0] != '-'; ( *i )++ ) + { + strcpy( buf, argv[( *i ) + 1] ); + for ( j = strlen( buf ) - 1; j >= strlen( buf ) - 4; j-- ) + if ( buf[j] == '.' ) { + break; + } + if ( j >= strlen( buf ) - 4 ) { + strcpy( &buf[j + 1], ext ); + } + qf = FindQuakeFiles( buf ); + if ( !qf ) { + continue; + } + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; + while ( lastqf->next ) lastqf = lastqf->next; + } //end for + return qfiles; +} //end of the function GetArgumentFiles +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +#define COMP_BSP2MAP 1 +#define COMP_BSP2AAS 2 +#define COMP_REACH 3 +#define COMP_CLUSTER 4 +#define COMP_AASOPTIMIZE 5 +#define COMP_AASINFO 6 +#define COMP_TETRA 7 +#define COMP_AASREMOVENONREACHABLE 8 + +void AAS_InitBotImport( void ); +void AAS_InitClustering( void ); +void AAS_ShowTotals( void ); +void AAS_InitBSP( void ); + +int main( int argc, char **argv ) { + int i, comp = 0; + char outputpath[MAX_PATH] = ""; + char filename[MAX_PATH] = "unknown"; + quakefile_t *qfiles, *qf; + + // Ridah, allow to specify an extension for multiple AAS files per map + int has_ext = 0; + // done. + + // Gordon: make sure to memset bspworld, fixes crash after CM_LoadMap + AAS_InitBSP(); + + // Ridah, set the world pointer up for reachabilities + aasworld = aasworlds; + AAS_SetWorldPointer( &( *aasworld ) ); + // done. + + myargc = argc; + myargv = argv; + + Log_Open( "bspc.log" ); //open a log file + Log_Print( "BSPC version " BSPC_VERSION ", %s %s by Mr Elusive\n", __DATE__, __TIME__ ); + + // Ryan, I added this so our designers would know which .exe they had... + Log_Print( "Last worked on by Ryan Feltrin\n" ); + + DefaultCfg(); + for ( i = 1; i < argc; i++ ) + { + if ( !stricmp( argv[i],"-threads" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + numthreads = atoi( argv[++i] ); + Log_Print( "threads = %d\n", numthreads ); + } //end if + else if ( !stricmp( argv[i], "-noverbose" ) ) { + Log_Print( "verbose = false\n" ); + verbose = false; + } //end else if + else if ( !stricmp( argv[i], "-nocsg" ) ) { + Log_Print( "nocsg = true\n" ); + nocsg = true; + } //end else if + else if ( !stricmp( argv[i], "-optimize" ) ) { + Log_Print( "optimize = true\n" ); + optimize = true; + } //end else if + /* + else if (!stricmp(argv[i],"-glview")) + { + glview = true; + } //end else if + else if (!stricmp(argv[i], "-draw")) + { + Log_Print("drawflag = true\n"); + drawflag = true; + } //end else if + else if (!stricmp(argv[i], "-noweld")) + { + Log_Print("noweld = true\n"); + noweld = true; + } //end else if + else if (!stricmp(argv[i], "-noshare")) + { + Log_Print("noshare = true\n"); + noshare = true; + } //end else if + else if (!stricmp(argv[i], "-notjunc")) + { + Log_Print("notjunc = true\n"); + notjunc = true; + } //end else if + else if (!stricmp(argv[i], "-nowater")) + { + Log_Print("nowater = true\n"); + nowater = true; + } //end else if + else if (!stricmp(argv[i], "-noprune")) + { + Log_Print("noprune = true\n"); + noprune = true; + } //end else if + else if (!stricmp(argv[i], "-nomerge")) + { + Log_Print("nomerge = true\n"); + nomerge = true; + } //end else if + else if (!stricmp(argv[i], "-nosubdiv")) + { + Log_Print("nosubdiv = true\n"); + nosubdiv = true; + } //end else if + else if (!stricmp(argv[i], "-nodetail")) + { + Log_Print("nodetail = true\n"); + nodetail = true; + } //end else if + else if (!stricmp(argv[i], "-fulldetail")) + { + Log_Print("fulldetail = true\n"); + fulldetail = true; + } //end else if + else if (!stricmp(argv[i], "-onlyents")) + { + Log_Print("onlyents = true\n"); + onlyents = true; + } //end else if + else if (!stricmp(argv[i], "-micro")) + { + if (i + 1 >= argc) {i = 0; break;} + microvolume = atof(argv[++i]); + Log_Print("microvolume = %f\n", microvolume); + } //end else if + else if (!stricmp(argv[i], "-leaktest")) + { + Log_Print("leaktest = true\n"); + leaktest = true; + } //end else if + else if (!stricmp(argv[i], "-verboseentities")) + { + Log_Print("verboseentities = true\n"); + verboseentities = true; + } //end else if + else if (!stricmp(argv[i], "-chop")) + { + if (i + 1 >= argc) {i = 0; break;} + subdivide_size = atof(argv[++i]); + Log_Print("subdivide_size = %f\n", subdivide_size); + } //end else if + else if (!stricmp (argv[i], "-tmpout")) + { + strcpy (outbase, "/tmp"); + Log_Print("temp output\n"); + } //end else if + */ +#ifdef ME + else if ( !stricmp( argv[i], "-freetree" ) ) { + freetree = true; + Log_Print( "freetree = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-grapplereach" ) ) { + calcgrapplereach = true; + Log_Print( "grapplereach = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-nobrushmerge" ) ) { + nobrushmerge = true; + Log_Print( "nobrushmerge = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-noliquids" ) ) { + noliquids = true; + Log_Print( "noliquids = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-forcesidesvisible" ) ) { + forcesidesvisible = true; + Log_Print( "forcesidesvisible = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-output" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + if ( access( argv[i + 1], 0x04 ) ) { + Warning( "the folder %s does not exist", argv[i + 1] ); + } + strcpy( outputpath, argv[++i] ); + } //end else if + else if ( !stricmp( argv[i], "-breadthfirst" ) ) { + use_nodequeue = true; + Log_Print( "breadthfirst = true\n" ); + } //end else if + else if ( !stricmp( argv[i], "-cfg" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + if ( !LoadCfgFile( argv[++i] ) ) { + exit( 0 ); + } + } //end else if + else if ( !stricmp( argv[i], "-bsp2map" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_BSP2MAP; + qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); + } //end else if + else if ( !stricmp( argv[i], "-bsp2aas" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_BSP2AAS; + qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); + } //end else if + else if ( !stricmp( argv[i], "-aasall" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + CreateAASFilesForAllBSPFiles( argv[++i] ); + } //end else if + else if ( !stricmp( argv[i], "-reach" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_REACH; + qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); + } //end else if + else if ( !stricmp( argv[i], "-cluster" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_CLUSTER; + qfiles = GetArgumentFiles( argc, argv, &i, "bsp" ); + } //end else if + else if ( !stricmp( argv[i], "-aasinfo" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_AASINFO; + qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); + } //end else if + else if ( !stricmp( argv[i], "-aasopt" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_AASOPTIMIZE; + qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); + } //end else if + else if ( !stricmp( argv[i], "-aasreachonly" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_AASREMOVENONREACHABLE; + qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); + } //end else if + else if ( !stricmp( argv[i], "-tetra" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + comp = COMP_TETRA; + qfiles = GetArgumentFiles( argc, argv, &i, "aas" ); + } //end else if + else if ( !stricmp( argv[i], "-writeaasmap" ) ) { + writeaasmap = true; + Log_Print( "writeaasmap = true\n" ); + } else if ( !stricmp( argv[i], "-groundonly" ) ) { + groundonly = true; + Log_Print( "groundonly = true\n" ); + } else if ( !stricmp( argv[i], "-reachableonly" ) ) { + reachableonly = true; + Log_Print( "reachableonly = true\n" ); + } else if ( !stricmp( argv[i], "-mingroundarea" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + mingroundarea = atoi( argv[++i] ); + Log_Print( "mingroundvolume = %i\n", mingroundarea ); + } else if ( !stricmp( argv[i], "-writebrushmap" ) ) { + writebrushmap = true; + Log_Print( "writebrushmap = %i\n", writebrushmap ); + } else if ( !stricmp( argv[i], "-noPatches" ) ) { + noPatches = true; + Log_Print( "noPatches = %i\n", noPatches ); + } + // Ridah, allow to specify an extension for multiple AAS files per map + else if ( !stricmp( argv[i], "-ext" ) ) { + if ( i + 1 >= argc ) { + i = 0; break; + } + strcpy( aas_extension, argv[++i] ); + has_ext = 1; + + } //end else if + // done. + +#endif //ME + else + { + Log_Print( "unknown parameter %s\n", argv[i] ); + break; + } //end else + } //end for + + //if there are parameters and there's no mismatch in one of the parameters + if ( argc > 1 && i == argc ) { + switch ( comp ) + { + case COMP_BSP2MAP: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + //copy the output path + strcpy( filename, outputpath ); + //append the bsp file base + AppendPathSeperator( filename, MAX_PATH ); + ExtractFileBase( qf->origname, &filename[strlen( filename )] ); + //append .map + strcat( filename, ".map" ); + // + Log_Print( "bsp2map: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_BSP ) { + Warning( "%s is probably not a BSP file\n", qf->origname ); + } + // + LoadMapFromBSP( qf ); + //write the map file + WriteMapFile( filename ); + } //end for + break; + } //end case + case COMP_BSP2AAS: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "bsp2aas: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_BSP ) { + Warning( "%s is probably not a BSP file\n", qf->origname ); + } + //set before map loading + create_aas = 1; + LoadMapFromBSP( qf ); + //create the AAS file + AAS_Create( filename ); + //if it's a Quake3 map calculate the reachabilities and clusters + if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + AAS_CalcReachAndClusters( qf ); + } + // + if ( groundonly ) { + AAS_RemoveNonGrounded(); + } + if ( reachableonly ) { + AAS_RemoveNonReachability(); + } + if ( optimize ) { + AAS_Optimize(); + } + // + //write out the stored AAS file + if ( !AAS_WriteAASFile( filename ) ) { + Error( "error writing %s\n", filename ); + } //end if + //deallocate memory + AAS_FreeMaxAAS(); + } //end for + break; + } //end case + case COMP_REACH: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "reach: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_BSP ) { + Warning( "%s is probably not a BSP file\n", qf->origname ); + } + //if the AAS file exists in the output directory + if ( !access( filename, 0x04 ) ) { + if ( !AAS_LoadAASFile( filename, 0, 0 ) ) { + Error( "error loading aas file %s\n", filename ); + } //end if + //assume it's a Quake3 BSP file + loadedmaptype = MAPTYPE_QUAKE3; + } //end if + else + { + Warning( "AAS file %s not found in output folder\n", filename ); + Log_Print( "creating %s...\n", filename ); + //set before map loading + create_aas = 1; + LoadMapFromBSP( qf ); + //create the AAS file + AAS_Create( filename ); + } //end else + //if it's a Quake3 map calculate the reachabilities and clusters + if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + AAS_CalcReachAndClusters( qf ); + } //end if + // + if ( groundonly ) { + AAS_RemoveNonGrounded(); + } + if ( reachableonly ) { + AAS_RemoveNonReachability(); + } + if ( optimize ) { + AAS_Optimize(); + } + //write out the stored AAS file + if ( !AAS_WriteAASFile( filename ) ) { + Error( "error writing %s\n", filename ); + } //end if + //deallocate memory + AAS_FreeMaxAAS(); + } //end for + break; + } //end case + case COMP_CLUSTER: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "cluster: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_BSP ) { + Warning( "%s is probably not a BSP file\n", qf->origname ); + } + //if the AAS file exists in the output directory + if ( !access( filename, 0x04 ) ) { + if ( !AAS_LoadAASFile( filename, 0, 0 ) ) { + Error( "error loading aas file %s\n", filename ); + } //end if + //assume it's a Quake3 BSP file + loadedmaptype = MAPTYPE_QUAKE3; + //if it's a Quake3 map calculate the clusters + if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + ( *aasworld ).numclusters = 0; + AAS_InitBotImport(); + AAS_InitClustering(); + } //end if + } //end if + else + { + Warning( "AAS file %s not found in output folder\n", filename ); + Log_Print( "creating %s...\n", filename ); + //set before map loading + create_aas = 1; + LoadMapFromBSP( qf ); + //create the AAS file + AAS_Create( filename ); + //if it's a Quake3 map calculate the reachabilities and clusters + if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + AAS_CalcReachAndClusters( qf ); + } + } //end else + // + if ( optimize ) { + AAS_Optimize(); + } + //write out the stored AAS file + if ( !AAS_WriteAASFile( filename ) ) { + Error( "error writing %s\n", filename ); + } //end if + //deallocate memory + AAS_FreeMaxAAS(); + } //end for + break; + } //end case + case COMP_AASOPTIMIZE: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "optimizing: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_AAS ) { + Warning( "%s is probably not a AAS file\n", qf->origname ); + } + // + AAS_InitBotImport(); + // + if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) { + Error( "error loading aas file %s\n", qf->filename ); + } //end if + if ( groundonly ) { + AAS_RemoveNonGrounded(); + } + if ( reachableonly ) { + AAS_RemoveNonReachability(); + } + AAS_Optimize(); + //write out the stored AAS file + if ( !AAS_WriteAASFile( filename ) ) { + Error( "error writing %s\n", filename ); + } //end if + //deallocate memory + AAS_FreeMaxAAS(); + } //end for + break; + } //end case + case COMP_AASREMOVENONREACHABLE: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "removing non-reachability areas: %s to %s\n", qf->origname, filename ); + if ( qf->type != QFILETYPE_AAS ) { + Warning( "%s is probably not a AAS file\n", qf->origname ); + } + // + AAS_InitBotImport(); + // + if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) { + Error( "error loading aas file %s\n", qf->filename ); + } //end if + AAS_RemoveNonReachability(); + if ( optimize ) { + AAS_Optimize(); + } + //write out the stored AAS file + if ( !AAS_WriteAASFile( filename ) ) { + Error( "error writing %s\n", filename ); + } //end if + //deallocate memory + AAS_FreeMaxAAS(); + } //end for + break; + } //end case + case COMP_AASINFO: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + AASOuputFile( qf, outputpath, filename ); + // + Log_Print( "aas info for: %s\n", filename ); + if ( qf->type != QFILETYPE_AAS ) { + Warning( "%s is probably not a AAS file\n", qf->origname ); + } + // + AAS_InitBotImport(); + // + if ( !AAS_LoadAASFile( qf->filename, qf->offset, qf->length ) ) { + Error( "error loading aas file %s\n", qf->filename ); + } //end if + AAS_ShowTotals(); + } //end for + } //end case + case COMP_TETRA: + { + if ( !qfiles ) { + Log_Print( "no files found\n" ); + } + for ( qf = qfiles; qf; qf = qf->next ) + { + //TH_AASToTetrahedrons(qf->filename); + } //end for + break; + } //end case + default: + { + Log_Print( "don't know what to do\n" ); + break; + } //end default + } //end switch + } //end if + else + { + Log_Print( "Usage: bspc [- [- ...]]\n" +#if defined( WIN32 ) || defined( _WIN32 ) + "Example 1: bspc -bsp2aas d:\\quake3\\baseq3\\maps\\mymap?.bsp\n" + "Example 2: bspc -bsp2aas d:\\quake3\\baseq3\\pak0.pk3\\maps/q3dm*.bsp\n" +#else + "Example 1: bspc -bsp2aas /quake3/baseq3/maps/mymap?.bsp\n" + "Example 2: bspc -bsp2aas /quake3/baseq3/pak0.pk3/maps/q3dm*.bsp\n" +#endif + "\n" + "Switches:\n" + //" bsp2map <[pakfilter/]filter.bsp> = convert BSP to MAP\n" + " bsp2aas <[pakfilter/]filter.bsp> = convert BSP to AAS\n" + //" aasall = create AAS files for all BSPs\n" + " reach = compute reachability & clusters\n" + " cluster = compute clusters\n" + " aasopt = optimize aas file\n" + //" tetra = tetrahedral decomposition\n" + " output = set output path\n" + " threads = set number of threads to X\n" + " cfg = use this cfg file\n" + " optimize = enable optimization\n" + " noverbose = disable verbose output\n" + " breadthfirst = breadth first bsp building\n" + " nobrushmerge = don't merge brushes\n" + " noliquids = don't write liquids to map\n" + " freetree = free the bsp tree\n" + " nocsg = disables brush chopping\n" + " forcesidesvisible = force all sides to be visible\n" + " grapplereach = calculate grapple reachabilities\n" + " writeaasmap = write the map the AI sees\n" + +/* " glview = output a GL view\n" + " draw = enables drawing\n" + " noweld = disables weld\n" + " noshare = disables sharing\n" + " notjunc = disables juncs\n" + " nowater = disables water brushes\n" + " noprune = disables node prunes\n" + " nomerge = disables face merging\n" + " nosubdiv = disables subdeviding\n" + " nodetail = disables detail brushes\n" + " fulldetail = enables full detail\n" + " onlyents = only compile entities with bsp\n" + " micro \n" + " = sets the micro volume to the given float\n" + " leaktest = perform a leak test\n" + " verboseentities\n" + " = enable entity verbose mode\n" + " chop \n" + " = sets the subdivide size to the given float\n"*/ + "\n" ); + } //end else + Log_Close(); //close the log file + return 0; +} //end of the function main + + diff --git a/src/bspc/bspc.vcproj b/src/bspc/bspc.vcproj new file mode 100644 index 0000000..5b22fae --- /dev/null +++ b/src/bspc/bspc.vcproj @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bspc/csg.c b/src/bspc/csg.c new file mode 100644 index 0000000..8fa3bc3 --- /dev/null +++ b/src/bspc/csg.c @@ -0,0 +1,1061 @@ +/* +=========================================================================== + +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: Brush CSG +// Function: +// Programmer(s): id Software & Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +// Notes: Microsoft Visual C++ optimizations: +// "global optimization" or "full optimization" results +// in micro brushes?? +//=========================================================================== + +#include "qbsp.h" +#include "../game/surfaceflags.h" + +/* + +tag all brushes with original contents +brushes may contain multiple contents +there will be no brush overlap after csg phase + +*/ + +int minplanenums[3]; +int maxplanenums[3]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CheckBSPBrush( bspbrush_t *brush ) { + int i, j; + plane_t *plane1, *plane2; + + //check if the brush is convex... flipped planes make a brush non-convex + for ( i = 0; i < brush->numsides; i++ ) + { + for ( j = 0; j < brush->numsides; j++ ) + { + if ( i == j ) { + continue; + } + plane1 = &mapplanes[brush->sides[i].planenum]; + plane2 = &mapplanes[brush->sides[j].planenum]; + // + if ( WindingsNonConvex( brush->sides[i].winding, + brush->sides[j].winding, + plane1->normal, plane2->normal, + plane1->dist, plane2->dist ) ) { + Log_Print( "non convex brush" ); + break; + } //end if + } //end for + } //end for + BoundBrush( brush ); + //check for out of bound brushes + for ( i = 0; i < 3; i++ ) + { + if ( brush->mins[i] < -MAX_MAP_BOUNDS || brush->maxs[i] > MAX_MAP_BOUNDS ) { + Log_Print( "brush: bounds out of range\n" ); + Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i] ); + break; + } //end if + if ( brush->mins[i] > MAX_MAP_BOUNDS || brush->maxs[i] < -MAX_MAP_BOUNDS ) { + Log_Print( "brush: no visible sides on brush\n" ); + Log_Print( "ob->mins[%d] = %f, ob->maxs[%d] = %f\n", i, brush->mins[i], i, brush->maxs[i] ); + break; + } //end if + } //end for +} //end of the function CheckBSPBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void BSPBrushWindings( bspbrush_t *brush ) { + int i, j; + winding_t *w; + plane_t *plane; + + for ( i = 0; i < brush->numsides; i++ ) + { + plane = &mapplanes[brush->sides[i].planenum]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( j = 0; j < brush->numsides && w; j++ ) + { + if ( i == j ) { + continue; + } + plane = &mapplanes[brush->sides[j].planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } //end for + brush->sides[i].winding = w; + } //end for +} //end of the function BSPBrushWindings +//=========================================================================== +// NOTE: can't keep brush->original intact +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *TryMergeBrushes( bspbrush_t *brush1, bspbrush_t *brush2 ) { + int i, j, k, n, shared; + side_t *side1, *side2, *cs; + plane_t *plane1, *plane2; + bspbrush_t *newbrush; + + //check for bounding box overlapp + for ( i = 0; i < 3; i++ ) + { + if ( brush1->mins[i] > brush2->maxs[i] + 2 + || brush1->maxs[i] < brush2->mins[i] - 2 ) { + return NULL; + } //end if + } //end for + // + shared = 0; + //check if the brush is convex... flipped planes make a brush non-convex + for ( i = 0; i < brush1->numsides; i++ ) + { + side1 = &brush1->sides[i]; + //don't check the "shared" sides + for ( k = 0; k < brush2->numsides; k++ ) + { + side2 = &brush2->sides[k]; + if ( side1->planenum == ( side2->planenum ^ 1 ) ) { + shared++; + //there may only be ONE shared side + if ( shared > 1 ) { + return NULL; + } + break; + } //end if + } //end for + if ( k < brush2->numsides ) { + continue; + } + // + for ( j = 0; j < brush2->numsides; j++ ) + { + side2 = &brush2->sides[j]; + //don't check the "shared" sides + for ( n = 0; n < brush1->numsides; n++ ) + { + side1 = &brush1->sides[n]; + if ( side1->planenum == ( side2->planenum ^ 1 ) ) { + break; + } + } //end for + if ( n < brush1->numsides ) { + continue; + } + // + side1 = &brush1->sides[i]; + //if the side is in the same plane + //* + if ( side1->planenum == side2->planenum ) { + if ( side1->texinfo != TEXINFO_NODE && + side2->texinfo != TEXINFO_NODE && + side1->texinfo != side2->texinfo ) { + return NULL; + } + continue; + } //end if + // + plane1 = &mapplanes[side1->planenum]; + plane2 = &mapplanes[side2->planenum]; + // + if ( WindingsNonConvex( side1->winding, side2->winding, + plane1->normal, plane2->normal, + plane1->dist, plane2->dist ) ) { + return NULL; + } //end if + } //end for + } //end for + newbrush = AllocBrush( brush1->numsides + brush2->numsides ); + newbrush->original = brush1->original; + newbrush->numsides = 0; + //newbrush->side = brush1->side; //brush contents + //fix texinfos for sides lying in the same plane + for ( i = 0; i < brush1->numsides; i++ ) + { + side1 = &brush1->sides[i]; + // + for ( n = 0; n < brush2->numsides; n++ ) + { + side2 = &brush2->sides[n]; + //if both sides are in the same plane get the texinfo right + if ( side1->planenum == side2->planenum ) { + if ( side1->texinfo == TEXINFO_NODE ) { + side1->texinfo = side2->texinfo; + } + if ( side2->texinfo == TEXINFO_NODE ) { + side2->texinfo = side1->texinfo; + } + } //end if + } //end for + } //end for + // + for ( i = 0; i < brush1->numsides; i++ ) + { + side1 = &brush1->sides[i]; + //don't add the "shared" sides + for ( n = 0; n < brush2->numsides; n++ ) + { + side2 = &brush2->sides[n]; + if ( side1->planenum == ( side2->planenum ^ 1 ) ) { + break; + } + } //end for + if ( n < brush2->numsides ) { + continue; + } + // + for ( n = 0; n < newbrush->numsides; n++ ) + { + cs = &newbrush->sides[n]; + if ( cs->planenum == side1->planenum ) { + Log_Print( "brush duplicate plane\n" ); + break; + } //end if + } //end if + if ( n < newbrush->numsides ) { + continue; + } + //add this side + cs = &newbrush->sides[newbrush->numsides]; + newbrush->numsides++; + *cs = *side1; + } //end for + for ( j = 0; j < brush2->numsides; j++ ) + { + side2 = &brush2->sides[j]; + for ( n = 0; n < brush1->numsides; n++ ) + { + side1 = &brush1->sides[n]; + //if the side is in the same plane + if ( side2->planenum == side1->planenum ) { + break; + } + //don't add the "shared" sides + if ( side2->planenum == ( side1->planenum ^ 1 ) ) { + break; + } + } //end for + if ( n < brush1->numsides ) { + continue; + } + // + for ( n = 0; n < newbrush->numsides; n++ ) + { + cs = &newbrush->sides[n]; + if ( cs->planenum == side2->planenum ) { + Log_Print( "brush duplicate plane\n" ); + break; + } //end if + } //end if + if ( n < newbrush->numsides ) { + continue; + } + //add this side + cs = &newbrush->sides[newbrush->numsides]; + newbrush->numsides++; + *cs = *side2; + } //end for + BSPBrushWindings( newbrush ); + BoundBrush( newbrush ); + CheckBSPBrush( newbrush ); + return newbrush; +} //end of the function TryMergeBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *MergeBrushes( bspbrush_t *brushlist ) { + int nummerges, merged; + bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; + bspbrush_t *lastb2; + + if ( !brushlist ) { + return NULL; + } + + qprintf( "%5d brushes merged", nummerges = 0 ); + do + { + for ( tail = brushlist; tail; tail = tail->next ) + { + if ( !tail->next ) { + break; + } + } //end for + merged = 0; + newbrushlist = NULL; + for ( b1 = brushlist; b1; b1 = brushlist ) + { + lastb2 = b1; + for ( b2 = b1->next; b2; b2 = b2->next ) + { + //if the brushes don't have the same contents + if ( b1->original->contents != b2->original->contents || + b1->original->expansionbbox != b2->original->expansionbbox ) { + newbrush = NULL; + } else { newbrush = TryMergeBrushes( b1, b2 );} + if ( newbrush ) { + tail->next = newbrush; + lastb2->next = b2->next; + brushlist = brushlist->next; + FreeBrush( b1 ); + FreeBrush( b2 ); + for ( tail = brushlist; tail; tail = tail->next ) + { + if ( !tail->next ) { + break; + } + } //end for + merged++; + qprintf( "\r%5d", nummerges++ ); + break; + } //end if + lastb2 = b2; + } //end for + //if b1 can't be merged with any of the other brushes + if ( !b2 ) { + brushlist = brushlist->next; + //keep b1 + b1->next = newbrushlist; + newbrushlist = b1; + } //end else + } //end for + brushlist = newbrushlist; + } while ( merged ); + qprintf( "\n" ); + return newbrushlist; +} //end of the function MergeBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SplitBrush2( bspbrush_t *brush, int planenum, + bspbrush_t **front, bspbrush_t **back ) { + SplitBrush( brush, planenum, front, back ); +#if 0 + if ( *front && ( *front )->sides[( *front )->numsides - 1].texinfo == -1 ) { + ( *front )->sides[( *front )->numsides - 1].texinfo = ( *front )->sides[0].texinfo; // not -1 + } + if ( *back && ( *back )->sides[( *back )->numsides - 1].texinfo == -1 ) { + ( *back )->sides[( *back )->numsides - 1].texinfo = ( *back )->sides[0].texinfo; // not -1 + } +#endif +} //end of the function SplitBrush2 +//=========================================================================== +// Returns a list of brushes that remain after B is subtracted from A. +// May by empty if A is contained inside B. +// The originals are undisturbed. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *SubtractBrush( bspbrush_t *a, bspbrush_t *b ) { // a - b = out (list) + int i; + bspbrush_t *front, *back; + bspbrush_t *out, *in; + + in = a; + out = NULL; + for ( i = 0; i < b->numsides && in; i++ ) + { + SplitBrush( in, b->sides[i].planenum, &front, &back ); +// SplitBrush2(in, b->sides[i].planenum, &front, &back); + if ( in != a ) { + FreeBrush( in ); + } + if ( front ) { // add to list + front->next = out; + out = front; + } //end if + in = back; + } //end for + if ( in ) { + FreeBrush( in ); + } //end if + else + { // didn't really intersect + FreeBrushList( out ); + return a; + } //end else + return out; +} //end of the function SubtractBrush +//=========================================================================== +// Returns a single brush made up by the intersection of the +// two provided brushes, or NULL if they are disjoint. +// +// The originals are undisturbed. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *IntersectBrush( bspbrush_t *a, bspbrush_t *b ) { + int i; + bspbrush_t *front, *back; + bspbrush_t *in; + + in = a; + for ( i = 0 ; i < b->numsides && in ; i++ ) + { + SplitBrush( in, b->sides[i].planenum, &front, &back ); +// SplitBrush2(in, b->sides[i].planenum, &front, &back); + if ( in != a ) { + FreeBrush( in ); + } + if ( front ) { + FreeBrush( front ); + } + in = back; + } //end for + + if ( in == a ) { + return NULL; + } + + in->next = NULL; + return in; +} //end of the function IntersectBrush + +//=========================================================================== +// Returns true if the two brushes definately do not intersect. +// There will be false negatives for some non-axial combinations. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean BrushesDisjoint( bspbrush_t *a, bspbrush_t *b ) { + int i, j; + + // check bounding boxes + for ( i = 0 ; i < 3 ; i++ ) + if ( a->mins[i] >= b->maxs[i] + || a->maxs[i] <= b->mins[i] ) { + return true; + } // bounding boxes don't overlap + + // check for opposing planes + for ( i = 0 ; i < a->numsides ; i++ ) + { + for ( j = 0 ; j < b->numsides ; j++ ) + { + if ( a->sides[i].planenum == + ( b->sides[j].planenum ^ 1 ) ) { + return true; // opposite planes, so not touching + } + } + } + + return false; // might intersect +} //end of the function BrushesDisjoint +//=========================================================================== +// Returns a content word for the intersection of two brushes. +// Some combinations will generate a combination (water + clip), +// but most will be the stronger of the two contents. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int IntersectionContents( int c1, int c2 ) { + int out; + + out = c1 | c2; + + if ( out & CONTENTS_SOLID ) { + out = CONTENTS_SOLID; + } + + return out; +} //end of the function IntersectionContents +//=========================================================================== +// Any planes shared with the box edge will be set to no texinfo +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *ClipBrushToBox( bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs ) { + int i, j; + bspbrush_t *front, *back; + int p; + + for ( j = 0 ; j < 2 ; j++ ) + { + if ( brush->maxs[j] > clipmaxs[j] ) { + SplitBrush( brush, maxplanenums[j], &front, &back ); + if ( front ) { + FreeBrush( front ); + } + brush = back; + if ( !brush ) { + return NULL; + } + } + if ( brush->mins[j] < clipmins[j] ) { + SplitBrush( brush, minplanenums[j], &front, &back ); + if ( back ) { + FreeBrush( back ); + } + brush = front; + if ( !brush ) { + return NULL; + } + } + } + + // remove any colinear faces + + for ( i = 0 ; i < brush->numsides ; i++ ) + { + p = brush->sides[i].planenum & ~1; + if ( p == maxplanenums[0] || p == maxplanenums[1] + || p == minplanenums[0] || p == minplanenums[1] ) { + brush->sides[i].texinfo = TEXINFO_NODE; + brush->sides[i].flags &= ~SFL_VISIBLE; + } + } + return brush; +} //end of the function ClipBrushToBox +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *MakeBspBrushList( int startbrush, int endbrush, + vec3_t clipmins, vec3_t clipmaxs ) { + mapbrush_t *mb; + bspbrush_t *brushlist, *newbrush; + int i, j; + int c_faces; + int c_brushes; + int numsides; + int vis; + vec3_t normal; + float dist; + + for ( i = 0 ; i < 2 ; i++ ) + { + VectorClear( normal ); + normal[i] = 1; + dist = clipmaxs[i]; + maxplanenums[i] = FindFloatPlane( normal, dist, 0, NULL ); + + dist = clipmins[i]; + minplanenums[i] = FindFloatPlane( normal, dist, 0, NULL ); + } + + brushlist = NULL; + c_faces = 0; + c_brushes = 0; + + for ( i = startbrush ; i < endbrush ; i++ ) + { + mb = &mapbrushes[i]; + + numsides = mb->numsides; + if ( !numsides ) { + continue; + } + + // make sure the brush has at least one face showing + vis = 0; + for ( j = 0 ; j < numsides ; j++ ) + if ( ( mb->original_sides[j].flags & SFL_VISIBLE ) && mb->original_sides[j].winding ) { + vis++; + } +#if 0 + if ( !vis ) { + continue; // no faces at all + } +#endif + // if the brush is outside the clip area, skip it + for ( j = 0 ; j < 3 ; j++ ) + if ( mb->mins[j] >= clipmaxs[j] + || mb->maxs[j] <= clipmins[j] ) { + break; + } + if ( j != 3 ) { + continue; + } + + // + // make a copy of the brush + // + newbrush = AllocBrush( mb->numsides ); + newbrush->original = mb; + newbrush->numsides = mb->numsides; + memcpy( newbrush->sides, mb->original_sides, numsides * sizeof( side_t ) ); + for ( j = 0 ; j < numsides ; j++ ) + { + if ( newbrush->sides[j].winding ) { + newbrush->sides[j].winding = CopyWinding( newbrush->sides[j].winding ); + } + if ( newbrush->sides[j].surf & SURF_HINT ) { + newbrush->sides[j].flags |= SFL_VISIBLE; // hints are always visible + } + } + VectorCopy( mb->mins, newbrush->mins ); + VectorCopy( mb->maxs, newbrush->maxs ); + + // + // carve off anything outside the clip box + // + newbrush = ClipBrushToBox( newbrush, clipmins, clipmaxs ); + if ( !newbrush ) { + continue; + } + + c_faces += vis; + c_brushes++; + + newbrush->next = brushlist; + brushlist = newbrush; + } + + return brushlist; +} //end of the function MakeBspBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *AddBrushListToTail( bspbrush_t *list, bspbrush_t *tail ) { + bspbrush_t *walk, *next; + + for ( walk = list ; walk ; walk = next ) + { // add to end of list + next = walk->next; + walk->next = NULL; + tail->next = walk; + tail = walk; + } //end for + return tail; +} //end of the function AddBrushListToTail +//=========================================================================== +// Builds a new list that doesn't hold the given brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *CullList( bspbrush_t *list, bspbrush_t *skip1 ) { + bspbrush_t *newlist; + bspbrush_t *next; + + newlist = NULL; + + for ( ; list ; list = next ) + { + next = list->next; + if ( list == skip1 ) { + FreeBrush( list ); + continue; + } + list->next = newlist; + newlist = list; + } + return newlist; +} //end of the function CullList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void WriteBrushMap(char *name, bspbrush_t *list) +{ + FILE *f; + side_t *s; + int i; + winding_t *w; + + Log_Print("writing %s\n", name); + f = fopen (name, "wb"); + if (!f) + Error ("Can't write %s\b", name); + + fprintf (f, "{\n\"classname\" \"worldspawn\"\n"); + + for ( ; list ; list=list->next ) + { + fprintf (f, "{\n"); + for (i=0,s=list->sides ; inumsides ; i++,s++) + { + w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist); + + fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]); + fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]); + fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]); + + fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture); + FreeWinding (w); + } + fprintf (f, "}\n"); + } + fprintf (f, "}\n"); + + fclose (f); +} //end of the function WriteBrushMap +*/ +//=========================================================================== +// Returns true if b1 is allowed to bite b2 +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +__inline qboolean BrushGE( bspbrush_t *b1, bspbrush_t *b2 ) { + // RF, let mover brushes bite other mover brushes + if ( b1->original->contents == CONTENTS_MOVER && b2->original->contents == CONTENTS_MOVER ) { + return true; + } +#ifdef ME + if ( create_aas ) { + if ( b1->original->expansionbbox != b2->original->expansionbbox ) { + return false; + } //end if + //never have something else bite a ladder brush + //never have a ladder brush bite something else + if ( ( b1->original->contents & CONTENTS_LADDER ) + && !( b2->original->contents & CONTENTS_LADDER ) ) { + return false; + } //end if + } //end if +#endif //ME + // detail brushes never bite structural brushes + if ( ( b1->original->contents & CONTENTS_DETAIL ) + && !( b2->original->contents & CONTENTS_DETAIL ) ) { + return false; + } //end if + if ( !( b1->original->contents & CONTENTS_SOLID ) ) { + return false; + } //end if + return true; +} //end of the function BrushGE +//=========================================================================== +// Carves any intersecting solid brushes into the minimum number +// of non-intersecting brushes. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *ChopBrushes( bspbrush_t *head ) { + bspbrush_t *b1, *b2, *next; + bspbrush_t *tail; + bspbrush_t *keep; + bspbrush_t *sub, *sub2; + int c1, c2; + int num_csg_iterations, total_processed; + + Log_Print( "-------- Brush CSG ---------\n" ); + Log_Print( "%6d original brushes\n", CountBrushList( head ) ); + + num_csg_iterations = 0; + total_processed = 0; + qprintf( "%6d output brushes", num_csg_iterations, total_processed ); + +#if 0 + if ( startbrush == 0 ) { + WriteBrushList( "before.gl", head, false ); + } +#endif + keep = NULL; + +newlist: + // find tail + if ( !head ) { + return NULL; + } + + for ( tail = head; tail->next; tail = tail->next ) + ; + + for ( b1 = head ; b1 ; b1 = next ) + { + next = b1->next; + total_processed++; + + //if the conversion is cancelled + if ( cancelconversion ) { + b1->next = keep; + keep = b1; + continue; + } //end if + + for ( b2 = b1->next; b2; b2 = b2->next ) + { + if ( BrushesDisjoint( b1, b2 ) ) { + continue; + } + + sub = NULL; + sub2 = NULL; + c1 = 999999; + c2 = 999999; + + if ( BrushGE( b2, b1 ) ) { + sub = SubtractBrush( b1, b2 ); + if ( sub == b1 ) { + continue; // didn't really intersect + } //end if + if ( !sub ) { // b1 is swallowed by b2 + head = CullList( b1, b1 ); + goto newlist; + } + c1 = CountBrushList( sub ); + } + + if ( BrushGE( b1, b2 ) ) { + sub2 = SubtractBrush( b2, b1 ); + if ( sub2 == b2 ) { + continue; // didn't really intersect + } + if ( !sub2 ) { // b2 is swallowed by b1 + FreeBrushList( sub ); + head = CullList( b1, b2 ); + goto newlist; + } + c2 = CountBrushList( sub2 ); + } + + if ( !sub && !sub2 ) { + continue; // neither one can bite + + } + // only accept if it didn't fragment + // (commenting this out allows full fragmentation) + if ( c1 > 1 && c2 > 1 ) { + if ( sub2 ) { + FreeBrushList( sub2 ); + } + if ( sub ) { + FreeBrushList( sub ); + } + continue; + } + + if ( c1 < c2 ) { + if ( sub2 ) { + FreeBrushList( sub2 ); + } + tail = AddBrushListToTail( sub, tail ); + head = CullList( b1, b1 ); + goto newlist; + } //end if + else + { + if ( sub ) { + FreeBrushList( sub ); + } + tail = AddBrushListToTail( sub2, tail ); + head = CullList( b1, b2 ); + goto newlist; + } //end else + } //end for + + if ( !b2 ) { // b1 is no longer intersecting anything, so keep it + b1->next = keep; + keep = b1; + } //end if + num_csg_iterations++; + qprintf( "\r%6d", num_csg_iterations, total_processed ); + } //end for + + if ( cancelconversion ) { + return keep; + } + // + qprintf( "\n" ); + Log_Write( "%6d output brushes\r\n", num_csg_iterations ); + +#if 0 + { + WriteBrushList( "after.gl", keep, false ); + WriteBrushMap( "after.map", keep ); + } +#endif + + return keep; +} //end of the function ChopBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *InitialBrushList( bspbrush_t *list ) { + bspbrush_t *b; + bspbrush_t *out, *newb; + int i; + + // only return brushes that have visible faces + out = NULL; + for ( b = list ; b ; b = b->next ) + { +#if 0 + for ( i = 0 ; i < b->numsides ; i++ ) + if ( b->sides[i].flags & SFL_VISIBLE ) { + break; + } + if ( i == b->numsides ) { + continue; + } +#endif + newb = CopyBrush( b ); + newb->next = out; + out = newb; + + // clear visible, so it must be set by MarkVisibleFaces_r + // to be used in the optimized list + for ( i = 0 ; i < b->numsides ; i++ ) + { + newb->sides[i].original = &b->sides[i]; +// newb->sides[i].visible = true; + b->sides[i].flags &= ~SFL_VISIBLE; + } + } + + return out; +} //end of the function InitialBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *OptimizedBrushList( bspbrush_t *list ) { + bspbrush_t *b; + bspbrush_t *out, *newb; + int i; + + // only return brushes that have visible faces + out = NULL; + for ( b = list ; b ; b = b->next ) + { + for ( i = 0 ; i < b->numsides ; i++ ) + if ( b->sides[i].flags & SFL_VISIBLE ) { + break; + } + if ( i == b->numsides ) { + continue; + } + newb = CopyBrush( b ); + newb->next = out; + out = newb; + } //end for + +// WriteBrushList ("vis.gl", out, true); + return out; +} //end of the function OptimizeBrushList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tree_t *ProcessWorldBrushes( int brush_start, int brush_end ) { + bspbrush_t *brushes; + tree_t *tree; + node_t *node; + vec3_t mins, maxs; + + //take the whole world + mins[0] = map_mins[0] - 8; + mins[1] = map_mins[1] - 8; + mins[2] = map_mins[2] - 8; + + maxs[0] = map_maxs[0] + 8; + maxs[1] = map_maxs[1] + 8; + maxs[2] = map_maxs[2] + 8; + + // the makelist and chopbrushes could be cached between the passes... + + //create a list with brushes that are within the given mins/maxs + //some brushes will be cut and only the part that falls within the + //mins/maxs will be in the bush list + brushes = MakeBspBrushList( brush_start, brush_end, mins, maxs ); + // + + if ( !brushes ) { + node = AllocNode(); + node->planenum = PLANENUM_LEAF; + node->contents = CONTENTS_SOLID; + + tree = Tree_Alloc(); + tree->headnode = node; + VectorCopy( mins, tree->mins ); + VectorCopy( maxs, tree->maxs ); + } //end if + else + { + //Carves any intersecting solid brushes into the minimum number + //of non-intersecting brushes. + if ( !nocsg ) { + brushes = ChopBrushes( brushes ); + /* + if (create_aas) + { + brushes = MergeBrushes(brushes); + } //end if*/ + } //end if + //if the conversion is cancelled + if ( cancelconversion ) { + FreeBrushList( brushes ); + return NULL; + } //end if + //create the actual bsp tree + tree = BrushBSP( brushes, mins, maxs ); + } //end else + //return the tree + return tree; +} //end of the function ProcessWorldBrushes diff --git a/src/bspc/faces.c b/src/bspc/faces.c new file mode 100644 index 0000000..8ca1868 --- /dev/null +++ b/src/bspc/faces.c @@ -0,0 +1,1002 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// NO LONGER USED +// faces.c +#if 0 +#include "qbsp.h" +#include "l_mem.h" + + +extern dvertex_t *dvertexes; +extern int numvertexes; +extern int numedges; +extern dedge_t *dedges; +extern texinfo_t texinfo[MAX_MAP_TEXINFO]; + +/* + + some faces will be removed before saving, but still form nodes: + + the insides of sky volumes + meeting planes of different water current volumes + +*/ + +// undefine for dumb linear searches +#define USE_HASHING + +#define INTEGRAL_EPSILON 0.01 +#define POINT_EPSILON 0.5 +#define OFF_EPSILON 0.5 + +int c_merge; +int c_subdivide; + +int c_totalverts; +int c_uniqueverts; +int c_degenerate; +int c_tjunctions; +int c_faceoverflows; +int c_facecollapse; +int c_badstartverts; + +#define MAX_SUPERVERTS 512 +int superverts[MAX_SUPERVERTS]; +int numsuperverts; + +face_t *edgefaces[MAX_MAP_EDGES][2]; +int firstmodeledge = 1; +int firstmodelface; + +int c_tryedges; + +vec3_t edge_dir; +vec3_t edge_start; +vec_t edge_len; + +int num_edge_verts; +int edge_verts[MAX_MAP_VERTS]; + + +face_t *NewFaceFromFace( face_t *f ); + +//=========================================================================== + +typedef struct hashvert_s +{ + struct hashvert_s *next; + int num; +} hashvert_t; + + +#define HASH_SIZE 64 + + +int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain +int hashverts[HASH_SIZE * HASH_SIZE]; // a vertex number, or 0 for no verts + +face_t *edgefaces[MAX_MAP_EDGES][2]; + +//============================================================================ + + +unsigned HashVec( vec3_t vec ) { + int x, y; + + x = ( 4096 + (int)( vec[0] + 0.5 ) ) >> 7; + y = ( 4096 + (int)( vec[1] + 0.5 ) ) >> 7; + + if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE ) { + Error( "HashVec: point outside valid range" ); + } + + return y * HASH_SIZE + x; +} + +#ifdef USE_HASHING +/* +============= +GetVertex + +Uses hashing +============= +*/ +int GetVertexnum( vec3_t in ) { + int h; + int i; + float *p; + vec3_t vert; + int vnum; + + c_totalverts++; + + for ( i = 0 ; i < 3 ; i++ ) + { + if ( fabs( in[i] - Q_rint( in[i] ) ) < INTEGRAL_EPSILON ) { + vert[i] = Q_rint( in[i] ); + } else { + vert[i] = in[i]; + } + } + + h = HashVec( vert ); + + for ( vnum = hashverts[h] ; vnum ; vnum = vertexchain[vnum] ) + { + p = dvertexes[vnum].point; + if ( fabs( p[0] - vert[0] ) < POINT_EPSILON + && fabs( p[1] - vert[1] ) < POINT_EPSILON + && fabs( p[2] - vert[2] ) < POINT_EPSILON ) { + return vnum; + } + } + +// emit a vertex + if ( numvertexes == MAX_MAP_VERTS ) { + Error( "numvertexes == MAX_MAP_VERTS" ); + } + + dvertexes[numvertexes].point[0] = vert[0]; + dvertexes[numvertexes].point[1] = vert[1]; + dvertexes[numvertexes].point[2] = vert[2]; + + vertexchain[numvertexes] = hashverts[h]; + hashverts[h] = numvertexes; + + c_uniqueverts++; + + numvertexes++; + + return numvertexes - 1; +} +#else +/* +================== +GetVertexnum + +Dumb linear search +================== +*/ +int GetVertexnum( vec3_t v ) { + int i, j; + dvertex_t *dv; + vec_t d; + + c_totalverts++; + + // make really close values exactly integral + for ( i = 0 ; i < 3 ; i++ ) + { + if ( fabs( v[i] - (int)( v[i] + 0.5 ) ) < INTEGRAL_EPSILON ) { + v[i] = (int)( v[i] + 0.5 ); + } + if ( v[i] < -4096 || v[i] > 4096 ) { + Error( "GetVertexnum: outside +/- 4096" ); + } + } + + // search for an existing vertex match + for ( i = 0, dv = dvertexes ; i < numvertexes ; i++, dv++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + { + d = v[j] - dv->point[j]; + if ( d > POINT_EPSILON || d < -POINT_EPSILON ) { + break; + } + } + if ( j == 3 ) { + return i; // a match + } + } + + // new point + if ( numvertexes == MAX_MAP_VERTS ) { + Error( "MAX_MAP_VERTS" ); + } + VectorCopy( v, dv->point ); + numvertexes++; + c_uniqueverts++; + + return numvertexes - 1; +} +#endif + + +/* +================== +FaceFromSuperverts + +The faces vertexes have been added to the superverts[] array, +and there may be more there than can be held in a face (MAXEDGES). + +If less, the faces vertexnums[] will be filled in, otherwise +face will reference a tree of split[] faces until all of the +vertexnums can be added. + +superverts[base] will become face->vertexnums[0], and the others +will be circularly filled in. +================== +*/ +void FaceFromSuperverts( node_t *node, face_t *f, int base ) { + face_t *newf; + int remaining; + int i; + + remaining = numsuperverts; + while ( remaining > MAXEDGES ) + { // must split into two faces, because of vertex overload + c_faceoverflows++; + + newf = f->split[0] = NewFaceFromFace( f ); + newf = f->split[0]; + newf->next = node->faces; + node->faces = newf; + + newf->numpoints = MAXEDGES; + for ( i = 0 ; i < MAXEDGES ; i++ ) + newf->vertexnums[i] = superverts[( i + base ) % numsuperverts]; + + f->split[1] = NewFaceFromFace( f ); + f = f->split[1]; + f->next = node->faces; + node->faces = f; + + remaining -= ( MAXEDGES - 2 ); + base = ( base + MAXEDGES - 1 ) % numsuperverts; + } + + // copy the vertexes back to the face + f->numpoints = remaining; + for ( i = 0 ; i < remaining ; i++ ) + f->vertexnums[i] = superverts[( i + base ) % numsuperverts]; +} + + +/* +================== +EmitFaceVertexes +================== +*/ +void EmitFaceVertexes( node_t *node, face_t *f ) { + winding_t *w; + int i; + + if ( f->merged || f->split[0] || f->split[1] ) { + return; + } + + w = f->w; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + if ( noweld ) { // make every point unique + if ( numvertexes == MAX_MAP_VERTS ) { + Error( "MAX_MAP_VERTS" ); + } + superverts[i] = numvertexes; + VectorCopy( w->p[i], dvertexes[numvertexes].point ); + numvertexes++; + c_uniqueverts++; + c_totalverts++; + } else { + superverts[i] = GetVertexnum( w->p[i] ); + } + } + numsuperverts = w->numpoints; + + // this may fragment the face if > MAXEDGES + FaceFromSuperverts( node, f, 0 ); +} + +/* +================== +EmitVertexes_r +================== +*/ +void EmitVertexes_r( node_t *node ) { + int i; + face_t *f; + + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + for ( f = node->faces ; f ; f = f->next ) + { + EmitFaceVertexes( node, f ); + } + + for ( i = 0 ; i < 2 ; i++ ) + EmitVertexes_r( node->children[i] ); +} + + +#ifdef USE_HASHING +/* +========== +FindEdgeVerts + +Uses the hash tables to cut down to a small number +========== +*/ +void FindEdgeVerts( vec3_t v1, vec3_t v2 ) { + int x1, x2, y1, y2, t; + int x, y; + int vnum; + +#if 0 + { + int i; + num_edge_verts = numvertexes - 1; + for ( i = 0 ; i < numvertexes - 1 ; i++ ) + edge_verts[i] = i + 1; + } +#endif + + x1 = ( 4096 + (int)( v1[0] + 0.5 ) ) >> 7; + y1 = ( 4096 + (int)( v1[1] + 0.5 ) ) >> 7; + x2 = ( 4096 + (int)( v2[0] + 0.5 ) ) >> 7; + y2 = ( 4096 + (int)( v2[1] + 0.5 ) ) >> 7; + + if ( x1 > x2 ) { + t = x1; + x1 = x2; + x2 = t; + } + if ( y1 > y2 ) { + t = y1; + y1 = y2; + y2 = t; + } +#if 0 + x1--; + x2++; + y1--; + y2++; + if ( x1 < 0 ) { + x1 = 0; + } + if ( x2 >= HASH_SIZE ) { + x2 = HASH_SIZE; + } + if ( y1 < 0 ) { + y1 = 0; + } + if ( y2 >= HASH_SIZE ) { + y2 = HASH_SIZE; + } +#endif + num_edge_verts = 0; + for ( x = x1 ; x <= x2 ; x++ ) + { + for ( y = y1 ; y <= y2 ; y++ ) + { + for ( vnum = hashverts[y * HASH_SIZE + x] ; vnum ; vnum = vertexchain[vnum] ) + { + edge_verts[num_edge_verts++] = vnum; + } + } + } +} + +#else +/* +========== +FindEdgeVerts + +Forced a dumb check of everything +========== +*/ +void FindEdgeVerts( vec3_t v1, vec3_t v2 ) { + int i; + + num_edge_verts = numvertexes - 1; + for ( i = 0 ; i < num_edge_verts ; i++ ) + edge_verts[i] = i + 1; +} +#endif + +/* +========== +TestEdge + +Can be recursively reentered +========== +*/ +void TestEdge( vec_t start, vec_t end, int p1, int p2, int startvert ) { + int j, k; + vec_t dist; + vec3_t delta; + vec3_t exact; + vec3_t off; + vec_t error; + vec3_t p; + + if ( p1 == p2 ) { + c_degenerate++; + return; // degenerate edge + } + + for ( k = startvert ; k < num_edge_verts ; k++ ) + { + j = edge_verts[k]; + if ( j == p1 || j == p2 ) { + continue; + } + + VectorCopy( dvertexes[j].point, p ); + + VectorSubtract( p, edge_start, delta ); + dist = DotProduct( delta, edge_dir ); + if ( dist <= start || dist >= end ) { + continue; // off an end + } + VectorMA( edge_start, dist, edge_dir, exact ); + VectorSubtract( p, exact, off ); + error = VectorLength( off ); + + if ( fabs( error ) > OFF_EPSILON ) { + continue; // not on the edge + + } + // break the edge + c_tjunctions++; + TestEdge( start, dist, p1, j, k + 1 ); + TestEdge( dist, end, j, p2, k + 1 ); + return; + } + + // the edge p1 to p2 is now free of tjunctions + if ( numsuperverts >= MAX_SUPERVERTS ) { + Error( "MAX_SUPERVERTS" ); + } + superverts[numsuperverts] = p1; + numsuperverts++; +} + +/* +================== +FixFaceEdges + +================== +*/ +void FixFaceEdges( node_t *node, face_t *f ) { + int p1, p2; + int i; + vec3_t e2; + vec_t len; + int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS]; + int base; + + if ( f->merged || f->split[0] || f->split[1] ) { + return; + } + + numsuperverts = 0; + + for ( i = 0 ; i < f->numpoints ; i++ ) + { + p1 = f->vertexnums[i]; + p2 = f->vertexnums[( i + 1 ) % f->numpoints]; + + VectorCopy( dvertexes[p1].point, edge_start ); + VectorCopy( dvertexes[p2].point, e2 ); + + FindEdgeVerts( edge_start, e2 ); + + VectorSubtract( e2, edge_start, edge_dir ); + len = VectorNormalize( edge_dir ); + + start[i] = numsuperverts; + TestEdge( 0, len, p1, p2, 0 ); + + count[i] = numsuperverts - start[i]; + } + + if ( numsuperverts < 3 ) { // entire face collapsed + f->numpoints = 0; + c_facecollapse++; + return; + } + + // we want to pick a vertex that doesn't have tjunctions + // on either side, which can cause artifacts on trifans, + // especially underwater + for ( i = 0 ; i < f->numpoints ; i++ ) + { + if ( count[i] == 1 && count[( i + f->numpoints - 1 ) % f->numpoints] == 1 ) { + break; + } + } + if ( i == f->numpoints ) { + f->badstartvert = true; + c_badstartverts++; + base = 0; + } else + { // rotate the vertex order + base = start[i]; + } + + // this may fragment the face if > MAXEDGES + FaceFromSuperverts( node, f, base ); +} + +/* +================== +FixEdges_r +================== +*/ +void FixEdges_r( node_t *node ) { + int i; + face_t *f; + + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + for ( f = node->faces ; f ; f = f->next ) + FixFaceEdges( node, f ); + + for ( i = 0 ; i < 2 ; i++ ) + FixEdges_r( node->children[i] ); +} + +/* +=========== +FixTjuncs + +=========== +*/ +void FixTjuncs( node_t *headnode ) { + // snap and merge all vertexes + qprintf( "---- snap verts ----\n" ); + memset( hashverts, 0, sizeof( hashverts ) ); + c_totalverts = 0; + c_uniqueverts = 0; + c_faceoverflows = 0; + EmitVertexes_r( headnode ); + qprintf( "%i unique from %i\n", c_uniqueverts, c_totalverts ); + + // break edges on tjunctions + qprintf( "---- tjunc ----\n" ); + c_tryedges = 0; + c_degenerate = 0; + c_facecollapse = 0; + c_tjunctions = 0; + if ( !notjunc ) { + FixEdges_r( headnode ); + } + qprintf( "%5i edges degenerated\n", c_degenerate ); + qprintf( "%5i faces degenerated\n", c_facecollapse ); + qprintf( "%5i edges added by tjunctions\n", c_tjunctions ); + qprintf( "%5i faces added by tjunctions\n", c_faceoverflows ); + qprintf( "%5i bad start verts\n", c_badstartverts ); +} + + +//======================================================== + +int c_faces; + +face_t *AllocFace( void ) { + face_t *f; + + f = GetMemory( sizeof( *f ) ); + memset( f, 0, sizeof( *f ) ); + c_faces++; + + return f; +} + +face_t *NewFaceFromFace( face_t *f ) { + face_t *newf; + + newf = AllocFace(); + *newf = *f; + newf->merged = NULL; + newf->split[0] = newf->split[1] = NULL; + newf->w = NULL; + return newf; +} + +void FreeFace( face_t *f ) { + if ( f->w ) { + FreeWinding( f->w ); + } + FreeMemory( f ); + c_faces--; +} + +//======================================================== + +/* +================== +GetEdge + +Called by writebsp. +Don't allow four way edges +================== +*/ +int GetEdge2( int v1, int v2, face_t *f ) { + dedge_t *edge; + int i; + + c_tryedges++; + + if ( !noshare ) { + for ( i = firstmodeledge ; i < numedges ; i++ ) + { + edge = &dedges[i]; + if ( v1 == edge->v[1] && v2 == edge->v[0] + && edgefaces[i][0]->contents == f->contents ) { + if ( edgefaces[i][1] ) { + // printf ("WARNING: multiple backward edge\n"); + continue; + } + edgefaces[i][1] = f; + return -i; + } + #if 0 + if ( v1 == edge->v[0] && v2 == edge->v[1] ) { + printf( "WARNING: multiple forward edge\n" ); + return i; + } + #endif + } + } + +// emit an edge + if ( numedges >= MAX_MAP_EDGES ) { + Error( "numedges == MAX_MAP_EDGES" ); + } + edge = &dedges[numedges]; + numedges++; + edge->v[0] = v1; + edge->v[1] = v2; + edgefaces[numedges - 1][0] = f; + + return numedges - 1; +} + +/* +=========================================================================== + +FACE MERGING + +=========================================================================== +*/ + +/* +============= +TryMerge + +If two polygons share a common edge and the edges that meet at the +common points are both inside the other polygons, merge them + +Returns NULL if the faces couldn't be merged, or the new face. +The originals will NOT be freed. +============= +*/ +face_t *TryMerge( face_t *f1, face_t *f2, vec3_t planenormal ) { + face_t *newf; + winding_t *nw; + + if ( !f1->w || !f2->w ) { + return NULL; + } + if ( f1->texinfo != f2->texinfo ) { + return NULL; + } + if ( f1->planenum != f2->planenum ) { // on front and back sides + return NULL; + } + if ( f1->contents != f2->contents ) { + return NULL; + } + + + nw = TryMergeWinding( f1->w, f2->w, planenormal ); + if ( !nw ) { + return NULL; + } + + c_merge++; + newf = NewFaceFromFace( f1 ); + newf->w = nw; + + f1->merged = newf; + f2->merged = newf; + + return newf; +} + +/* +=============== +MergeNodeFaces +=============== +*/ +void MergeNodeFaces( node_t *node ) { + face_t *f1, *f2, *end; + face_t *merged; + plane_t *plane; + + plane = &mapplanes[node->planenum]; + merged = NULL; + + for ( f1 = node->faces ; f1 ; f1 = f1->next ) + { + if ( f1->merged || f1->split[0] || f1->split[1] ) { + continue; + } + + for ( f2 = node->faces ; f2 != f1 ; f2 = f2->next ) + { + if ( f2->merged || f2->split[0] || f2->split[1] ) { + continue; + } + + //IDBUG: always passes the face's node's normal to TryMerge() + //regardless of which side the face is on. Approximately 50% of + //the time the face will be on the other side of node, and thus + //the result of the convex/concave test in TryMergeWinding(), + //which depends on the normal, is flipped. This causes faces + //that shouldn't be merged to be merged and faces that + //should be merged to not be merged. + //the following added line fixes this bug + //thanks to: Alexander Malmberg + plane = &mapplanes[f1->planenum]; + // + merged = TryMerge( f1, f2, plane->normal ); + if ( !merged ) { + continue; + } + + // add merged to the end of the node face list + // so it will be checked against all the faces again + for ( end = node->faces ; end->next ; end = end->next ) + ; + merged->next = NULL; + end->next = merged; + break; + } + } +} + +//===================================================================== + +/* +=============== +SubdivideFace + +Chop up faces that are larger than we want in the surface cache +=============== +*/ +void SubdivideFace( node_t *node, face_t *f ) { + float mins, maxs; + vec_t v; + int axis, i; + texinfo_t *tex; + vec3_t temp; + vec_t dist; + winding_t *w, *frontw, *backw; + + if ( f->merged ) { + return; + } + +// special (non-surface cached) faces don't need subdivision + tex = &texinfo[f->texinfo]; + + if ( tex->flags & ( SURF_WARP | SURF_SKY ) ) { + return; + } + + for ( axis = 0 ; axis < 2 ; axis++ ) + { + while ( 1 ) + { + mins = 999999; + maxs = -999999; + + VectorCopy( tex->vecs[axis], temp ); + w = f->w; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + v = DotProduct( w->p[i], temp ); + if ( v < mins ) { + mins = v; + } + if ( v > maxs ) { + maxs = v; + } + } +#if 0 + if ( maxs - mins <= 0 ) { + Error( "zero extents" ); + } +#endif + if ( axis == 2 ) { // allow double high walls + if ( maxs - mins <= subdivide_size /* *2 */ ) { + break; + } + } else if ( maxs - mins <= subdivide_size ) { + break; + } + + // split it + c_subdivide++; + + v = VectorNormalize( temp ); + + dist = ( mins + subdivide_size - 16 ) / v; + + ClipWindingEpsilon( w, temp, dist, ON_EPSILON, &frontw, &backw ); + if ( !frontw || !backw ) { + Error( "SubdivideFace: didn't split the polygon" ); + } + + f->split[0] = NewFaceFromFace( f ); + f->split[0]->w = frontw; + f->split[0]->next = node->faces; + node->faces = f->split[0]; + + f->split[1] = NewFaceFromFace( f ); + f->split[1]->w = backw; + f->split[1]->next = node->faces; + node->faces = f->split[1]; + + SubdivideFace( node, f->split[0] ); + SubdivideFace( node, f->split[1] ); + return; + } + } +} + +void SubdivideNodeFaces( node_t *node ) { + face_t *f; + + for ( f = node->faces ; f ; f = f->next ) + { + SubdivideFace( node, f ); + } +} + +//=========================================================================== + +int c_nodefaces; + + +/* +============ +FaceFromPortal + +============ +*/ +face_t *FaceFromPortal( portal_t *p, int pside ) { + face_t *f; + side_t *side; + + side = p->side; + if ( !side ) { + return NULL; // portal does not bridge different visible contents + + } + f = AllocFace(); + + f->texinfo = side->texinfo; + f->planenum = ( side->planenum & ~1 ) | pside; + f->portal = p; + + if ( ( p->nodes[pside]->contents & CONTENTS_WINDOW ) + && VisibleContents( p->nodes[!pside]->contents ^ p->nodes[pside]->contents ) == CONTENTS_WINDOW ) { + return NULL; // don't show insides of windows + + } + if ( pside ) { + f->w = ReverseWinding( p->winding ); + f->contents = p->nodes[1]->contents; + } else + { + f->w = CopyWinding( p->winding ); + f->contents = p->nodes[0]->contents; + } + return f; +} + + +/* +=============== +MakeFaces_r + +If a portal will make a visible face, +mark the side that originally created it + + solid / empty : solid + solid / water : solid + water / empty : water + water / water : none +=============== +*/ +void MakeFaces_r( node_t *node ) { + portal_t *p; + int s; + + // recurse down to leafs + if ( node->planenum != PLANENUM_LEAF ) { + MakeFaces_r( node->children[0] ); + MakeFaces_r( node->children[1] ); + + // merge together all visible faces on the node + if ( !nomerge ) { + MergeNodeFaces( node ); + } + if ( !nosubdiv ) { + SubdivideNodeFaces( node ); + } + + return; + } + + // solid leafs never have visible faces + if ( node->contents & CONTENTS_SOLID ) { + return; + } + + // see which portals are valid + for ( p = node->portals ; p ; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); + + p->face[s] = FaceFromPortal( p, s ); + if ( p->face[s] ) { + c_nodefaces++; + p->face[s]->next = p->onnode->faces; + p->onnode->faces = p->face[s]; + } + } +} + +/* +============ +MakeFaces +============ +*/ +void MakeFaces( node_t *node ) { + qprintf( "--- MakeFaces ---\n" ); + c_merge = 0; + c_subdivide = 0; + c_nodefaces = 0; + + MakeFaces_r( node ); + + qprintf( "%5i makefaces\n", c_nodefaces ); + qprintf( "%5i merged\n", c_merge ); + qprintf( "%5i subdivided\n", c_subdivide ); +} +#endif diff --git a/src/bspc/glfile.c b/src/bspc/glfile.c new file mode 100644 index 0000000..d3b94cf --- /dev/null +++ b/src/bspc/glfile.c @@ -0,0 +1,157 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "qbsp.h" + +int c_glfaces; + +int PortalVisibleSides( portal_t *p ) { + int fcon, bcon; + + if ( !p->onnode ) { + return 0; // outside + + } + fcon = p->nodes[0]->contents; + bcon = p->nodes[1]->contents; + + // same contents never create a face + if ( fcon == bcon ) { + return 0; + } + + // FIXME: is this correct now? + if ( !fcon ) { + return 1; + } + if ( !bcon ) { + return 2; + } + return 0; +} + +void OutputWinding( winding_t *w, FILE *glview ) { + static int level = 128; + vec_t light; + int i; + + fprintf( glview, "%i\n", w->numpoints ); + level += 28; + light = ( level & 255 ) / 255.0; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + fprintf( glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n", + w->p[i][0], + w->p[i][1], + w->p[i][2], + light, + light, + light ); + } + fprintf( glview, "\n" ); +} + +/* +============= +OutputPortal +============= +*/ +void OutputPortal( portal_t *p, FILE *glview ) { + winding_t *w; + int sides; + + sides = PortalVisibleSides( p ); + if ( !sides ) { + return; + } + + c_glfaces++; + + w = p->winding; + + if ( sides == 2 ) { // back side + w = ReverseWinding( w ); + } + + OutputWinding( w, glview ); + + if ( sides == 2 ) { + FreeWinding( w ); + } +} + +/* +============= +WriteGLView_r +============= +*/ +void WriteGLView_r( node_t *node, FILE *glview ) { + portal_t *p, *nextp; + + if ( node->planenum != PLANENUM_LEAF ) { + WriteGLView_r( node->children[0], glview ); + WriteGLView_r( node->children[1], glview ); + return; + } + + // write all the portals + for ( p = node->portals ; p ; p = nextp ) + { + if ( p->nodes[0] == node ) { + OutputPortal( p, glview ); + nextp = p->next[0]; + } else { + nextp = p->next[1]; + } + } +} + +/* +============= +WriteGLView +============= +*/ +void WriteGLView( tree_t *tree, char *source ) { + char name[1024]; + FILE *glview; + + c_glfaces = 0; + sprintf( name, "%s%s.gl",outbase, source ); + printf( "Writing %s\n", name ); + + glview = fopen( name, "w" ); + if ( !glview ) { + Error( "Couldn't open %s", name ); + } + WriteGLView_r( tree->headnode, glview ); + fclose( glview ); + + printf( "%5i c_glfaces\n", c_glfaces ); +} + diff --git a/src/bspc/l_bsp_ent.c b/src/bspc/l_bsp_ent.c new file mode 100644 index 0000000..542a7a6 --- /dev/null +++ b/src/bspc/l_bsp_ent.c @@ -0,0 +1,195 @@ +/* +=========================================================================== + +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_bsp_ent.c +// Function: bsp entity parsing +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1999-04-05 +// Tab Size: 3 +// Notes: - +//=========================================================================== + +#include "l_cmd.h" +#include "l_math.h" +#include "l_mem.h" +#include "l_log.h" +#include "../botlib/l_script.h" +#include "l_bsp_ent.h" + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +int num_entities; +entity_t entities[MAX_MAP_ENTITIES]; + +void StripTrailing( char *e ) { + char *s; + + s = e + strlen( e ) - 1; + while ( s >= e && *s <= 32 ) + { + *s = 0; + s--; + } +} + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair( script_t *script ) { + epair_t *e; + token_t token; + + e = GetMemory( sizeof( epair_t ) ); + memset( e, 0, sizeof( epair_t ) ); + + PS_ExpectAnyToken( script, &token ); + StripDoubleQuotes( token.string ); + if ( strlen( token.string ) >= MAX_KEY - 1 ) { + Error( "ParseEpair: token %s too long", token.string ); + } + e->key = copystring( token.string ); + PS_ExpectAnyToken( script, &token ); + StripDoubleQuotes( token.string ); + if ( strlen( token.string ) >= MAX_VALUE - 1 ) { + Error( "ParseEpair: token %s too long", token.string ); + } + e->value = copystring( token.string ); + + // strip trailing spaces + StripTrailing( e->key ); + StripTrailing( e->value ); + + return e; +} //end of the function ParseEpair + + +/* +================ +ParseEntity +================ +*/ +qboolean ParseEntity( script_t *script ) { + epair_t *e; + entity_t *mapent; + token_t token; + + if ( !PS_ReadToken( script, &token ) ) { + return false; + } + + if ( strcmp( token.string, "{" ) ) { + Error( "ParseEntity: { not found" ); + } + + if ( num_entities == MAX_MAP_ENTITIES ) { + Error( "num_entities == MAX_MAP_ENTITIES" ); + } + + mapent = &entities[num_entities]; + num_entities++; + + do + { + if ( !PS_ReadToken( script, &token ) ) { + Error( "ParseEntity: EOF without closing brace" ); + } + if ( !strcmp( token.string, "}" ) ) { + break; + } + PS_UnreadLastToken( script ); + e = ParseEpair( script ); + e->next = mapent->epairs; + mapent->epairs = e; + } while ( 1 ); + + return true; +} //end of the function ParseEntity + +void PrintEntity( entity_t *ent ) { + epair_t *ep; + + printf( "------- entity %p -------\n", ent ); + for ( ep = ent->epairs ; ep ; ep = ep->next ) + { + printf( "%s = %s\n", ep->key, ep->value ); + } + +} + +void SetKeyValue( entity_t *ent, char *key, char *value ) { + epair_t *ep; + + for ( ep = ent->epairs ; ep ; ep = ep->next ) + if ( !strcmp( ep->key, key ) ) { + FreeMemory( ep->value ); + ep->value = copystring( value ); + return; + } + ep = GetMemory( sizeof( *ep ) ); + ep->next = ent->epairs; + ent->epairs = ep; + ep->key = copystring( key ); + ep->value = copystring( value ); +} + +char *ValueForKey( entity_t *ent, char *key ) { + epair_t *ep; + + for ( ep = ent->epairs ; ep ; ep = ep->next ) + if ( !strcmp( ep->key, key ) ) { + return ep->value; + } + return ""; +} + +vec_t FloatForKey( entity_t *ent, char *key ) { + char *k; + + k = ValueForKey( ent, key ); + return atof( k ); +} + +void GetVectorForKey( entity_t *ent, char *key, vec3_t vec ) { + char *k; + double v1, v2, v3; + + k = ValueForKey( ent, key ); +// scanf into doubles, then assign, so it is vec_t size independent + v1 = v2 = v3 = 0; + sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 ); + vec[0] = v1; + vec[1] = v2; + vec[2] = v3; +} + + diff --git a/src/bspc/l_bsp_ent.h b/src/bspc/l_bsp_ent.h new file mode 100644 index 0000000..5e28d76 --- /dev/null +++ b/src/bspc/l_bsp_ent.h @@ -0,0 +1,65 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#ifndef MAX_MAP_ENTITIES +#define MAX_MAP_ENTITIES 4096 +#endif + +typedef struct epair_s +{ + struct epair_s *next; + char *key; + char *value; +} epair_t; + +typedef struct +{ + vec3_t origin; + int firstbrush; + int numbrushes; + epair_t *epairs; + // only valid for func_areaportals + int areaportalnum; + int portalareas[2]; + int modelnum; //for bsp 2 map conversion + qboolean wasdetail; //for SIN +} entity_t; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +void StripTrailing( char *e ); +void SetKeyValue( entity_t *ent, char *key, char *value ); +char *ValueForKey( entity_t *ent, char *key ); // will return "" if not present +vec_t FloatForKey( entity_t *ent, char *key ); +void GetVectorForKey( entity_t *ent, char *key, vec3_t vec ); +qboolean ParseEntity( script_t *script ); +epair_t *ParseEpair( script_t *script ); +void PrintEntity( entity_t *ent ); + diff --git a/src/bspc/l_bsp_q1.c b/src/bspc/l_bsp_q1.c new file mode 100644 index 0000000..5f2e632 --- /dev/null +++ b/src/bspc/l_bsp_q1.c @@ -0,0 +1,608 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "l_cmd.h" +#include "l_math.h" +#include "l_mem.h" +#include "l_log.h" +#include "../botlib/l_script.h" +#include "l_bsp_q1.h" +#include "l_bsp_ent.h" + +//============================================================================= + +int q1_nummodels; +q1_dmodel_t *q1_dmodels; //[MAX_MAP_MODELS]; + +int q1_visdatasize; +byte *q1_dvisdata; //[MAX_MAP_VISIBILITY]; + +int q1_lightdatasize; +byte *q1_dlightdata; //[MAX_MAP_LIGHTING]; + +int q1_texdatasize; +byte *q1_dtexdata; //[MAX_MAP_MIPTEX]; // (dmiptexlump_t) + +int q1_entdatasize; +char *q1_dentdata; //[MAX_MAP_ENTSTRING]; + +int q1_numleafs; +q1_dleaf_t *q1_dleafs; //[MAX_MAP_LEAFS]; + +int q1_numplanes; +q1_dplane_t *q1_dplanes; //[MAX_MAP_PLANES]; + +int q1_numvertexes; +q1_dvertex_t *q1_dvertexes; //[MAX_MAP_VERTS]; + +int q1_numnodes; +q1_dnode_t *q1_dnodes; //[MAX_MAP_NODES]; + +int q1_numtexinfo; +q1_texinfo_t *q1_texinfo; //[MAX_MAP_TEXINFO]; + +int q1_numfaces; +q1_dface_t *q1_dfaces; //[MAX_MAP_FACES]; + +int q1_numclipnodes; +q1_dclipnode_t *q1_dclipnodes; //[MAX_MAP_CLIPNODES]; + +int q1_numedges; +q1_dedge_t *q1_dedges; //[MAX_MAP_EDGES]; + +int q1_nummarksurfaces; +unsigned short *q1_dmarksurfaces; //[MAX_MAP_MARKSURFACES]; + +int q1_numsurfedges; +int *q1_dsurfedges; //[MAX_MAP_SURFEDGES]; + +//============================================================================= + +int q1_bspallocated = false; +int q1_allocatedbspmem = 0; + +void Q1_AllocMaxBSP( void ) { + //models + q1_nummodels = 0; + q1_dmodels = (q1_dmodel_t *) GetMemory( Q1_MAX_MAP_MODELS * sizeof( q1_dmodel_t ) ); + q1_allocatedbspmem = Q1_MAX_MAP_MODELS * sizeof( q1_dmodel_t ); + //visibility + q1_visdatasize = 0; + q1_dvisdata = (byte *) GetMemory( Q1_MAX_MAP_VISIBILITY * sizeof( byte ) ); + q1_allocatedbspmem += Q1_MAX_MAP_VISIBILITY * sizeof( byte ); + //light data + q1_lightdatasize = 0; + q1_dlightdata = (byte *) GetMemory( Q1_MAX_MAP_LIGHTING * sizeof( byte ) ); + q1_allocatedbspmem += Q1_MAX_MAP_LIGHTING * sizeof( byte ); + //texture data + q1_texdatasize = 0; + q1_dtexdata = (byte *) GetMemory( Q1_MAX_MAP_MIPTEX * sizeof( byte ) ); // (dmiptexlump_t) + q1_allocatedbspmem += Q1_MAX_MAP_MIPTEX * sizeof( byte ); + //entities + q1_entdatasize = 0; + q1_dentdata = (char *) GetMemory( Q1_MAX_MAP_ENTSTRING * sizeof( char ) ); + q1_allocatedbspmem += Q1_MAX_MAP_ENTSTRING * sizeof( char ); + //leaves + q1_numleafs = 0; + q1_dleafs = (q1_dleaf_t *) GetMemory( Q1_MAX_MAP_LEAFS * sizeof( q1_dleaf_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_LEAFS * sizeof( q1_dleaf_t ); + //planes + q1_numplanes = 0; + q1_dplanes = (q1_dplane_t *) GetMemory( Q1_MAX_MAP_PLANES * sizeof( q1_dplane_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_PLANES * sizeof( q1_dplane_t ); + //vertexes + q1_numvertexes = 0; + q1_dvertexes = (q1_dvertex_t *) GetMemory( Q1_MAX_MAP_VERTS * sizeof( q1_dvertex_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_VERTS * sizeof( q1_dvertex_t ); + //nodes + q1_numnodes = 0; + q1_dnodes = (q1_dnode_t *) GetMemory( Q1_MAX_MAP_NODES * sizeof( q1_dnode_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_NODES * sizeof( q1_dnode_t ); + //texture info + q1_numtexinfo = 0; + q1_texinfo = (q1_texinfo_t *) GetMemory( Q1_MAX_MAP_TEXINFO * sizeof( q1_texinfo_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_TEXINFO * sizeof( q1_texinfo_t ); + //faces + q1_numfaces = 0; + q1_dfaces = (q1_dface_t *) GetMemory( Q1_MAX_MAP_FACES * sizeof( q1_dface_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_FACES * sizeof( q1_dface_t ); + //clip nodes + q1_numclipnodes = 0; + q1_dclipnodes = (q1_dclipnode_t *) GetMemory( Q1_MAX_MAP_CLIPNODES * sizeof( q1_dclipnode_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_CLIPNODES * sizeof( q1_dclipnode_t ); + //edges + q1_numedges = 0; + q1_dedges = (q1_dedge_t *) GetMemory( Q1_MAX_MAP_EDGES * sizeof( q1_dedge_t ) ); + q1_allocatedbspmem += Q1_MAX_MAP_EDGES, sizeof( q1_dedge_t ); + //mark surfaces + q1_nummarksurfaces = 0; + q1_dmarksurfaces = (unsigned short *) GetMemory( Q1_MAX_MAP_MARKSURFACES * sizeof( unsigned short ) ); + q1_allocatedbspmem += Q1_MAX_MAP_MARKSURFACES * sizeof( unsigned short ); + //surface edges + q1_numsurfedges = 0; + q1_dsurfedges = (int *) GetMemory( Q1_MAX_MAP_SURFEDGES * sizeof( int ) ); + q1_allocatedbspmem += Q1_MAX_MAP_SURFEDGES * sizeof( int ); + //print allocated memory + Log_Print( "allocated " ); + PrintMemorySize( q1_allocatedbspmem ); + Log_Print( " of BSP memory\n" ); +} //end of the function Q1_AllocMaxBSP + +void Q1_FreeMaxBSP( void ) { + //models + q1_nummodels = 0; + FreeMemory( q1_dmodels ); + q1_dmodels = NULL; + //visibility + q1_visdatasize = 0; + FreeMemory( q1_dvisdata ); + q1_dvisdata = NULL; + //light data + q1_lightdatasize = 0; + FreeMemory( q1_dlightdata ); + q1_dlightdata = NULL; + //texture data + q1_texdatasize = 0; + FreeMemory( q1_dtexdata ); + q1_dtexdata = NULL; + //entities + q1_entdatasize = 0; + FreeMemory( q1_dentdata ); + q1_dentdata = NULL; + //leaves + q1_numleafs = 0; + FreeMemory( q1_dleafs ); + q1_dleafs = NULL; + //planes + q1_numplanes = 0; + FreeMemory( q1_dplanes ); + q1_dplanes = NULL; + //vertexes + q1_numvertexes = 0; + FreeMemory( q1_dvertexes ); + q1_dvertexes = NULL; + //nodes + q1_numnodes = 0; + FreeMemory( q1_dnodes ); + q1_dnodes = NULL; + //texture info + q1_numtexinfo = 0; + FreeMemory( q1_texinfo ); + q1_texinfo = NULL; + //faces + q1_numfaces = 0; + FreeMemory( q1_dfaces ); + q1_dfaces = NULL; + //clip nodes + q1_numclipnodes = 0; + FreeMemory( q1_dclipnodes ); + q1_dclipnodes = NULL; + //edges + q1_numedges = 0; + FreeMemory( q1_dedges ); + q1_dedges = NULL; + //mark surfaces + q1_nummarksurfaces = 0; + FreeMemory( q1_dmarksurfaces ); + q1_dmarksurfaces = NULL; + //surface edges + q1_numsurfedges = 0; + FreeMemory( q1_dsurfedges ); + q1_dsurfedges = NULL; + // + Log_Print( "freed " ); + PrintMemorySize( q1_allocatedbspmem ); + Log_Print( " of BSP memory\n" ); + q1_allocatedbspmem = 0; +} //end of the function Q1_FreeMaxBSP +//#endif //ME + +/* +============= +Q1_SwapBSPFile + +Byte swaps all data in a bsp file. +============= +*/ +void Q1_SwapBSPFile( qboolean todisk ) { + int i, j, c; + q1_dmodel_t *d; + q1_dmiptexlump_t *mtl; + + +// models + for ( i = 0 ; i < q1_nummodels ; i++ ) + { + d = &q1_dmodels[i]; + + for ( j = 0 ; j < Q1_MAX_MAP_HULLS ; j++ ) + d->headnode[j] = LittleLong( d->headnode[j] ); + + d->visleafs = LittleLong( d->visleafs ); + d->firstface = LittleLong( d->firstface ); + d->numfaces = LittleLong( d->numfaces ); + + for ( j = 0 ; j < 3 ; j++ ) + { + d->mins[j] = LittleFloat( d->mins[j] ); + d->maxs[j] = LittleFloat( d->maxs[j] ); + d->origin[j] = LittleFloat( d->origin[j] ); + } + } + +// +// vertexes +// + for ( i = 0 ; i < q1_numvertexes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + q1_dvertexes[i].point[j] = LittleFloat( q1_dvertexes[i].point[j] ); + } + +// +// planes +// + for ( i = 0 ; i < q1_numplanes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + q1_dplanes[i].normal[j] = LittleFloat( q1_dplanes[i].normal[j] ); + q1_dplanes[i].dist = LittleFloat( q1_dplanes[i].dist ); + q1_dplanes[i].type = LittleLong( q1_dplanes[i].type ); + } + +// +// texinfos +// + for ( i = 0 ; i < q1_numtexinfo ; i++ ) + { + for ( j = 0 ; j < 8 ; j++ ) + q1_texinfo[i].vecs[0][j] = LittleFloat( q1_texinfo[i].vecs[0][j] ); + q1_texinfo[i].miptex = LittleLong( q1_texinfo[i].miptex ); + q1_texinfo[i].flags = LittleLong( q1_texinfo[i].flags ); + } + +// +// faces +// + for ( i = 0 ; i < q1_numfaces ; i++ ) + { + q1_dfaces[i].texinfo = LittleShort( q1_dfaces[i].texinfo ); + q1_dfaces[i].planenum = LittleShort( q1_dfaces[i].planenum ); + q1_dfaces[i].side = LittleShort( q1_dfaces[i].side ); + q1_dfaces[i].lightofs = LittleLong( q1_dfaces[i].lightofs ); + q1_dfaces[i].firstedge = LittleLong( q1_dfaces[i].firstedge ); + q1_dfaces[i].numedges = LittleShort( q1_dfaces[i].numedges ); + } + +// +// nodes +// + for ( i = 0 ; i < q1_numnodes ; i++ ) + { + q1_dnodes[i].planenum = LittleLong( q1_dnodes[i].planenum ); + for ( j = 0 ; j < 3 ; j++ ) + { + q1_dnodes[i].mins[j] = LittleShort( q1_dnodes[i].mins[j] ); + q1_dnodes[i].maxs[j] = LittleShort( q1_dnodes[i].maxs[j] ); + } + q1_dnodes[i].children[0] = LittleShort( q1_dnodes[i].children[0] ); + q1_dnodes[i].children[1] = LittleShort( q1_dnodes[i].children[1] ); + q1_dnodes[i].firstface = LittleShort( q1_dnodes[i].firstface ); + q1_dnodes[i].numfaces = LittleShort( q1_dnodes[i].numfaces ); + } + +// +// leafs +// + for ( i = 0 ; i < q1_numleafs ; i++ ) + { + q1_dleafs[i].contents = LittleLong( q1_dleafs[i].contents ); + for ( j = 0 ; j < 3 ; j++ ) + { + q1_dleafs[i].mins[j] = LittleShort( q1_dleafs[i].mins[j] ); + q1_dleafs[i].maxs[j] = LittleShort( q1_dleafs[i].maxs[j] ); + } + + q1_dleafs[i].firstmarksurface = LittleShort( q1_dleafs[i].firstmarksurface ); + q1_dleafs[i].nummarksurfaces = LittleShort( q1_dleafs[i].nummarksurfaces ); + q1_dleafs[i].visofs = LittleLong( q1_dleafs[i].visofs ); + } + +// +// clipnodes +// + for ( i = 0 ; i < q1_numclipnodes ; i++ ) + { + q1_dclipnodes[i].planenum = LittleLong( q1_dclipnodes[i].planenum ); + q1_dclipnodes[i].children[0] = LittleShort( q1_dclipnodes[i].children[0] ); + q1_dclipnodes[i].children[1] = LittleShort( q1_dclipnodes[i].children[1] ); + } + +// +// miptex +// + if ( q1_texdatasize ) { + mtl = (q1_dmiptexlump_t *)q1_dtexdata; + if ( todisk ) { + c = mtl->nummiptex; + } else { + c = LittleLong( mtl->nummiptex ); + } + mtl->nummiptex = LittleLong( mtl->nummiptex ); + for ( i = 0 ; i < c ; i++ ) + mtl->dataofs[i] = LittleLong( mtl->dataofs[i] ); + } + +// +// marksurfaces +// + for ( i = 0 ; i < q1_nummarksurfaces ; i++ ) + q1_dmarksurfaces[i] = LittleShort( q1_dmarksurfaces[i] ); + +// +// surfedges +// + for ( i = 0 ; i < q1_numsurfedges ; i++ ) + q1_dsurfedges[i] = LittleLong( q1_dsurfedges[i] ); + +// +// edges +// + for ( i = 0 ; i < q1_numedges ; i++ ) + { + q1_dedges[i].v[0] = LittleShort( q1_dedges[i].v[0] ); + q1_dedges[i].v[1] = LittleShort( q1_dedges[i].v[1] ); + } +} + + +q1_dheader_t *q1_header; + +int Q1_CopyLump( int lump, void *dest, int size ) { + int length, ofs; + + length = q1_header->lumps[lump].filelen; + ofs = q1_header->lumps[lump].fileofs; + + if ( length % size ) { + Error( "Q1_LoadBSPFile: odd lump size" ); + } + + memcpy( dest, (byte *)q1_header + ofs, length ); + + return length / size; +} + +/* +============= +Q1_LoadBSPFile +============= +*/ +void Q1_LoadBSPFile( char *filename, int offset, int length ) { + int i; + +// +// load the file header +// + LoadFile( filename, (void **)&q1_header, offset, length ); + +// swap the header + for ( i = 0 ; i < sizeof( q1_dheader_t ) / 4 ; i++ ) + ( (int *)q1_header )[i] = LittleLong( ( (int *)q1_header )[i] ); + + if ( q1_header->version != Q1_BSPVERSION ) { + Error( "%s is version %i, not %i", filename, i, Q1_BSPVERSION ); + } + + q1_nummodels = Q1_CopyLump( Q1_LUMP_MODELS, q1_dmodels, sizeof( q1_dmodel_t ) ); + q1_numvertexes = Q1_CopyLump( Q1_LUMP_VERTEXES, q1_dvertexes, sizeof( q1_dvertex_t ) ); + q1_numplanes = Q1_CopyLump( Q1_LUMP_PLANES, q1_dplanes, sizeof( q1_dplane_t ) ); + q1_numleafs = Q1_CopyLump( Q1_LUMP_LEAFS, q1_dleafs, sizeof( q1_dleaf_t ) ); + q1_numnodes = Q1_CopyLump( Q1_LUMP_NODES, q1_dnodes, sizeof( q1_dnode_t ) ); + q1_numtexinfo = Q1_CopyLump( Q1_LUMP_TEXINFO, q1_texinfo, sizeof( q1_texinfo_t ) ); + q1_numclipnodes = Q1_CopyLump( Q1_LUMP_CLIPNODES, q1_dclipnodes, sizeof( q1_dclipnode_t ) ); + q1_numfaces = Q1_CopyLump( Q1_LUMP_FACES, q1_dfaces, sizeof( q1_dface_t ) ); + q1_nummarksurfaces = Q1_CopyLump( Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, sizeof( q1_dmarksurfaces[0] ) ); + q1_numsurfedges = Q1_CopyLump( Q1_LUMP_SURFEDGES, q1_dsurfedges, sizeof( q1_dsurfedges[0] ) ); + q1_numedges = Q1_CopyLump( Q1_LUMP_EDGES, q1_dedges, sizeof( q1_dedge_t ) ); + + q1_texdatasize = Q1_CopyLump( Q1_LUMP_TEXTURES, q1_dtexdata, 1 ); + q1_visdatasize = Q1_CopyLump( Q1_LUMP_VISIBILITY, q1_dvisdata, 1 ); + q1_lightdatasize = Q1_CopyLump( Q1_LUMP_LIGHTING, q1_dlightdata, 1 ); + q1_entdatasize = Q1_CopyLump( Q1_LUMP_ENTITIES, q1_dentdata, 1 ); + + FreeMemory( q1_header ); // everything has been copied out + +// +// swap everything +// + Q1_SwapBSPFile( false ); +} + +//============================================================================ + +FILE *q1_wadfile; +q1_dheader_t q1_outheader; + +void Q1_AddLump( int lumpnum, void *data, int len ) { + q1_lump_t *lump; + + lump = &q1_header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( q1_wadfile ) ); + lump->filelen = LittleLong( len ); + SafeWrite( q1_wadfile, data, ( len + 3 ) & ~3 ); +} + +/* +============= +Q1_WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void Q1_WriteBSPFile( char *filename ) { + q1_header = &q1_outheader; + memset( q1_header, 0, sizeof( q1_dheader_t ) ); + + Q1_SwapBSPFile( true ); + + q1_header->version = LittleLong( Q1_BSPVERSION ); + + q1_wadfile = SafeOpenWrite( filename ); + SafeWrite( q1_wadfile, q1_header, sizeof( q1_dheader_t ) ); // overwritten later + + Q1_AddLump( Q1_LUMP_PLANES, q1_dplanes, q1_numplanes * sizeof( q1_dplane_t ) ); + Q1_AddLump( Q1_LUMP_LEAFS, q1_dleafs, q1_numleafs * sizeof( q1_dleaf_t ) ); + Q1_AddLump( Q1_LUMP_VERTEXES, q1_dvertexes, q1_numvertexes * sizeof( q1_dvertex_t ) ); + Q1_AddLump( Q1_LUMP_NODES, q1_dnodes, q1_numnodes * sizeof( q1_dnode_t ) ); + Q1_AddLump( Q1_LUMP_TEXINFO, q1_texinfo, q1_numtexinfo * sizeof( q1_texinfo_t ) ); + Q1_AddLump( Q1_LUMP_FACES, q1_dfaces, q1_numfaces * sizeof( q1_dface_t ) ); + Q1_AddLump( Q1_LUMP_CLIPNODES, q1_dclipnodes, q1_numclipnodes * sizeof( q1_dclipnode_t ) ); + Q1_AddLump( Q1_LUMP_MARKSURFACES, q1_dmarksurfaces, q1_nummarksurfaces * sizeof( q1_dmarksurfaces[0] ) ); + Q1_AddLump( Q1_LUMP_SURFEDGES, q1_dsurfedges, q1_numsurfedges * sizeof( q1_dsurfedges[0] ) ); + Q1_AddLump( Q1_LUMP_EDGES, q1_dedges, q1_numedges * sizeof( q1_dedge_t ) ); + Q1_AddLump( Q1_LUMP_MODELS, q1_dmodels, q1_nummodels * sizeof( q1_dmodel_t ) ); + + Q1_AddLump( Q1_LUMP_LIGHTING, q1_dlightdata, q1_lightdatasize ); + Q1_AddLump( Q1_LUMP_VISIBILITY, q1_dvisdata, q1_visdatasize ); + Q1_AddLump( Q1_LUMP_ENTITIES, q1_dentdata, q1_entdatasize ); + Q1_AddLump( Q1_LUMP_TEXTURES, q1_dtexdata, q1_texdatasize ); + + fseek( q1_wadfile, 0, SEEK_SET ); + SafeWrite( q1_wadfile, q1_header, sizeof( q1_dheader_t ) ); + fclose( q1_wadfile ); +} + +//============================================================================ + +/* +============= +Q1_PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void Q1_PrintBSPFileSizes( void ) { + printf( "%5i planes %6i\n" + ,q1_numplanes, (int)( q1_numplanes * sizeof( q1_dplane_t ) ) ); + printf( "%5i vertexes %6i\n" + ,q1_numvertexes, (int)( q1_numvertexes * sizeof( q1_dvertex_t ) ) ); + printf( "%5i nodes %6i\n" + ,q1_numnodes, (int)( q1_numnodes * sizeof( q1_dnode_t ) ) ); + printf( "%5i texinfo %6i\n" + ,q1_numtexinfo, (int)( q1_numtexinfo * sizeof( q1_texinfo_t ) ) ); + printf( "%5i faces %6i\n" + ,q1_numfaces, (int)( q1_numfaces * sizeof( q1_dface_t ) ) ); + printf( "%5i clipnodes %6i\n" + ,q1_numclipnodes, (int)( q1_numclipnodes * sizeof( q1_dclipnode_t ) ) ); + printf( "%5i leafs %6i\n" + ,q1_numleafs, (int)( q1_numleafs * sizeof( q1_dleaf_t ) ) ); + printf( "%5i marksurfaces %6i\n" + ,q1_nummarksurfaces, (int)( q1_nummarksurfaces * sizeof( q1_dmarksurfaces[0] ) ) ); + printf( "%5i surfedges %6i\n" + ,q1_numsurfedges, (int)( q1_numsurfedges * sizeof( q1_dmarksurfaces[0] ) ) ); + printf( "%5i edges %6i\n" + ,q1_numedges, (int)( q1_numedges * sizeof( q1_dedge_t ) ) ); + if ( !q1_texdatasize ) { + printf( " 0 textures 0\n" ); + } else { + printf( "%5i textures %6i\n",( (q1_dmiptexlump_t*)q1_dtexdata )->nummiptex, q1_texdatasize ); + } + printf( " lightdata %6i\n", q1_lightdatasize ); + printf( " visdata %6i\n", q1_visdatasize ); + printf( " entdata %6i\n", q1_entdatasize ); +} //end of the function Q1_PrintBSPFileSizes + + +/* +================ +Q1_ParseEntities + +Parses the dentdata string into entities +================ +*/ +void Q1_ParseEntities( void ) { + script_t *script; + + num_entities = 0; + script = LoadScriptMemory( q1_dentdata, q1_entdatasize, "*Quake1 bsp file" ); + SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | + SCFL_NOSTRINGESCAPECHARS ); + + while ( ParseEntity( script ) ) + { + } //end while + + FreeScript( script ); +} //end of the function Q1_ParseEntities + + +/* +================ +Q1_UnparseEntities + +Generates the dentdata string from all the entities +================ +*/ +void Q1_UnparseEntities( void ) { + char *buf, *end; + epair_t *ep; + char line[2048]; + int i; + + buf = q1_dentdata; + end = buf; + *end = 0; + + for ( i = 0 ; i < num_entities ; i++ ) + { + ep = entities[i].epairs; + if ( !ep ) { + continue; // ent got removed + + } + strcat( end,"{\n" ); + end += 2; + + for ( ep = entities[i].epairs ; ep ; ep = ep->next ) + { + sprintf( line, "\"%s\" \"%s\"\n", ep->key, ep->value ); + strcat( end, line ); + end += strlen( line ); + } + strcat( end,"}\n" ); + end += 2; + + if ( end > buf + Q1_MAX_MAP_ENTSTRING ) { + Error( "Entity text too long" ); + } + } + q1_entdatasize = end - buf + 1; +} //end of the function Q1_UnparseEntities diff --git a/src/bspc/l_bsp_q1.h b/src/bspc/l_bsp_q1.h new file mode 100644 index 0000000..ee73d85 --- /dev/null +++ b/src/bspc/l_bsp_q1.h @@ -0,0 +1,282 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +// upper design bounds + +#define Q1_MAX_MAP_HULLS 4 + +#define Q1_MAX_MAP_MODELS 256 +#define Q1_MAX_MAP_BRUSHES 4096 +#define Q1_MAX_MAP_ENTITIES 4096 +#define Q1_MAX_MAP_ENTSTRING ( 128 * Q1_MAX_MAP_ENTITIES ) + +#define Q1_MAX_MAP_PLANES 8192 +#define Q1_MAX_MAP_NODES 32767 // because negative shorts are contents +#define Q1_MAX_MAP_CLIPNODES 32767 // +#define Q1_MAX_MAP_LEAFS 32767 // +#define Q1_MAX_MAP_VERTS 65535 +#define Q1_MAX_MAP_FACES 65535 +#define Q1_MAX_MAP_MARKSURFACES 65535 +#define Q1_MAX_MAP_TEXINFO 4096 +#define Q1_MAX_MAP_EDGES 256000 +#define Q1_MAX_MAP_SURFEDGES 512000 +#define Q1_MAX_MAP_MIPTEX 0x200000 +#define Q1_MAX_MAP_LIGHTING 0x100000 +#define Q1_MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + + +#define Q1_BSPVERSION 29 + +typedef struct +{ + int fileofs, filelen; +} q1_lump_t; + +#define Q1_LUMP_ENTITIES 0 +#define Q1_LUMP_PLANES 1 +#define Q1_LUMP_TEXTURES 2 +#define Q1_LUMP_VERTEXES 3 +#define Q1_LUMP_VISIBILITY 4 +#define Q1_LUMP_NODES 5 +#define Q1_LUMP_TEXINFO 6 +#define Q1_LUMP_FACES 7 +#define Q1_LUMP_LIGHTING 8 +#define Q1_LUMP_CLIPNODES 9 +#define Q1_LUMP_LEAFS 10 +#define Q1_LUMP_MARKSURFACES 11 +#define Q1_LUMP_EDGES 12 +#define Q1_LUMP_SURFEDGES 13 +#define Q1_LUMP_MODELS 14 + +#define Q1_HEADER_LUMPS 15 + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[Q1_MAX_MAP_HULLS]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} q1_dmodel_t; + +typedef struct +{ + int version; + q1_lump_t lumps[Q1_HEADER_LUMPS]; +} q1_dheader_t; + +typedef struct +{ + int nummiptex; + int dataofs[4]; // [nummiptex] +} q1_dmiptexlump_t; + +#define MIPLEVELS 4 +typedef struct q1_miptex_s +{ + char name[16]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored +} q1_miptex_t; + + +typedef struct +{ + float point[3]; +} q1_dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} q1_dplane_t; + + + +#define Q1_CONTENTS_EMPTY -1 +#define Q1_CONTENTS_SOLID -2 +#define Q1_CONTENTS_WATER -3 +#define Q1_CONTENTS_SLIME -4 +#define Q1_CONTENTS_LAVA -5 +#define Q1_CONTENTS_SKY -6 + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + int planenum; + short children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for sphere culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} q1_dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} q1_dclipnode_t; + + +typedef struct q1_texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int miptex; + int flags; +} q1_texinfo_t; +#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} q1_dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} q1_dface_t; + + + +#define AMBIENT_WATER 0 +#define AMBIENT_SKY 1 +#define AMBIENT_SLIME 2 +#define AMBIENT_LAVA 3 + +#define NUM_AMBIENTS 4 // automatic ambient sounds + +// leaf 0 is the generic Q1_CONTENTS_SOLID leaf, used for all solid areas +// all other leafs need visibility info +typedef struct +{ + int contents; + int visofs; // -1 = no visibility info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstmarksurface; + unsigned short nummarksurfaces; + + byte ambient_level[NUM_AMBIENTS]; +} q1_dleaf_t; + +//============================================================================ + +#ifndef QUAKE_GAME + +// the utilities get to be lazy and just use large static arrays + +extern int q1_nummodels; +extern q1_dmodel_t *q1_dmodels; //[MAX_MAP_MODELS]; + +extern int q1_visdatasize; +extern byte *q1_dvisdata; //[MAX_MAP_VISIBILITY]; + +extern int q1_lightdatasize; +extern byte *q1_dlightdata; //[MAX_MAP_LIGHTING]; + +extern int q1_texdatasize; +extern byte *q1_dtexdata; //[MAX_MAP_MIPTEX]; // (dmiptexlump_t) + +extern int q1_entdatasize; +extern char *q1_dentdata; //[MAX_MAP_ENTSTRING]; + +extern int q1_numleafs; +extern q1_dleaf_t *q1_dleafs; //[MAX_MAP_LEAFS]; + +extern int q1_numplanes; +extern q1_dplane_t *q1_dplanes; //[MAX_MAP_PLANES]; + +extern int q1_numvertexes; +extern q1_dvertex_t *q1_dvertexes; //[MAX_MAP_VERTS]; + +extern int q1_numnodes; +extern q1_dnode_t *q1_dnodes; //[MAX_MAP_NODES]; + +extern int q1_numtexinfo; +extern q1_texinfo_t *q1_texinfo; //[MAX_MAP_TEXINFO]; + +extern int q1_numfaces; +extern q1_dface_t *q1_dfaces; //[MAX_MAP_FACES]; + +extern int q1_numclipnodes; +extern q1_dclipnode_t *q1_dclipnodes; //[MAX_MAP_CLIPNODES]; + +extern int q1_numedges; +extern q1_dedge_t *q1_dedges; //[MAX_MAP_EDGES]; + +extern int q1_nummarksurfaces; +extern unsigned short *q1_dmarksurfaces; //[MAX_MAP_MARKSURFACES]; + +extern int q1_numsurfedges; +extern int *q1_dsurfedges; //[MAX_MAP_SURFEDGES]; + + +void Q1_AllocMaxBSP( void ); +void Q1_FreeMaxBSP( void ); +void Q1_LoadBSPFile( char *filename, int offset, int length ); +void Q1_WriteBSPFile( char *filename ); +void Q1_PrintBSPFileSizes( void ); +void Q1_ParseEntities( void ); +void Q1_UnparseEntities( void ); + +#endif diff --git a/src/bspc/l_bsp_q2.c b/src/bspc/l_bsp_q2.c new file mode 100644 index 0000000..ce3e55a --- /dev/null +++ b/src/bspc/l_bsp_q2.c @@ -0,0 +1,1138 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "l_cmd.h" +#include "l_math.h" +#include "l_mem.h" +#include "l_log.h" +#include "l_poly.h" +#include "../botlib/l_script.h" +#include "q2files.h" +#include "l_bsp_q2.h" +#include "l_bsp_ent.h" + +#define q2_dmodel_t dmodel_t +#define q2_lump_t lump_t +#define q2_dheader_t dheader_t +#define q2_dmodel_t dmodel_t +#define q2_dvertex_t dvertex_t +#define q2_dplane_t dplane_t +#define q2_dnode_t dnode_t +#define q2_texinfo_t texinfo_t +#define q2_dedge_t dedge_t +#define q2_dface_t dface_t +#define q2_dleaf_t dleaf_t +#define q2_dbrushside_t dbrushside_t +#define q2_dbrush_t dbrush_t +#define q2_dvis_t dvis_t +#define q2_dareaportal_t dareaportal_t +#define q2_darea_t darea_t + +#define q2_nummodels nummodels +#define q2_dmodels dmodels +#define q2_numleafs numleafs +#define q2_dleafs dleafs +#define q2_numplanes numplanes +#define q2_dplanes dplanes +#define q2_numvertexes numvertexes +#define q2_dvertexes dvertexes +#define q2_numnodes numnodes +#define q2_dnodes dnodes +#define q2_numtexinfo numtexinfo +#define q2_texinfo texinfo +#define q2_numfaces numfaces +#define q2_dfaces dfaces +#define q2_numedges numedges +#define q2_dedges dedges +#define q2_numleaffaces numleaffaces +#define q2_dleaffaces dleaffaces +#define q2_numleafbrushes numleafbrushes +#define q2_dleafbrushes dleafbrushes +#define q2_dsurfedges dsurfedges +#define q2_numbrushes numbrushes +#define q2_dbrushes dbrushes +#define q2_numbrushsides numbrushsides +#define q2_dbrushsides dbrushsides +#define q2_numareas numareas +#define q2_dareas dareas +#define q2_numareaportals numareaportals +#define q2_dareaportals dareaportals + +void GetLeafNums( void ); + +//============================================================================= + +int nummodels; +dmodel_t *dmodels; //[MAX_MAP_MODELS]; + +int visdatasize; +byte *dvisdata; //[MAX_MAP_VISIBILITY]; +dvis_t *dvis; // = (dvis_t *)dvisdata; + +int lightdatasize; +byte *dlightdata; //[MAX_MAP_LIGHTING]; + +int entdatasize; +char *dentdata; //[MAX_MAP_ENTSTRING]; + +int numleafs; +dleaf_t *dleafs; //[MAX_MAP_LEAFS]; + +int numplanes; +dplane_t *dplanes; //[MAX_MAP_PLANES]; + +int numvertexes; +dvertex_t *dvertexes; //[MAX_MAP_VERTS]; + +int numnodes; +dnode_t *dnodes; //[MAX_MAP_NODES]; + +//NOTE: must be static for q2 .map to q2 .bsp +int numtexinfo; +texinfo_t texinfo[MAX_MAP_TEXINFO]; + +int numfaces; +dface_t *dfaces; //[MAX_MAP_FACES]; + +int numedges; +dedge_t *dedges; //[MAX_MAP_EDGES]; + +int numleaffaces; +unsigned short *dleaffaces; //[MAX_MAP_LEAFFACES]; + +int numleafbrushes; +unsigned short *dleafbrushes; //[MAX_MAP_LEAFBRUSHES]; + +int numsurfedges; +int *dsurfedges; //[MAX_MAP_SURFEDGES]; + +int numbrushes; +dbrush_t *dbrushes; //[MAX_MAP_BRUSHES]; + +int numbrushsides; +dbrushside_t *dbrushsides; //[MAX_MAP_BRUSHSIDES]; + +int numareas; +darea_t *dareas; //[MAX_MAP_AREAS]; + +int numareaportals; +dareaportal_t *dareaportals; //[MAX_MAP_AREAPORTALS]; + +#define MAX_MAP_DPOP 256 +byte dpop[MAX_MAP_DPOP]; + +// +char brushsidetextured[MAX_MAP_BRUSHSIDES]; + +//#ifdef ME + +int bspallocated = false; +int allocatedbspmem = 0; + +void Q2_AllocMaxBSP( void ) { + //models + nummodels = 0; + dmodels = (dmodel_t *) GetClearedMemory( MAX_MAP_MODELS * sizeof( dmodel_t ) ); + allocatedbspmem += MAX_MAP_MODELS * sizeof( dmodel_t ); + //vis data + visdatasize = 0; + dvisdata = (byte *) GetClearedMemory( MAX_MAP_VISIBILITY * sizeof( byte ) ); + dvis = (dvis_t *) dvisdata; + allocatedbspmem += MAX_MAP_VISIBILITY * sizeof( byte ); + //light data + lightdatasize = 0; + dlightdata = (byte *) GetClearedMemory( MAX_MAP_LIGHTING * sizeof( byte ) ); + allocatedbspmem += MAX_MAP_LIGHTING * sizeof( byte ); + //entity data + entdatasize = 0; + dentdata = (char *) GetClearedMemory( MAX_MAP_ENTSTRING * sizeof( char ) ); + allocatedbspmem += MAX_MAP_ENTSTRING * sizeof( char ); + //leafs + numleafs = 0; + dleafs = (dleaf_t *) GetClearedMemory( MAX_MAP_LEAFS * sizeof( dleaf_t ) ); + allocatedbspmem += MAX_MAP_LEAFS * sizeof( dleaf_t ); + //planes + numplanes = 0; + dplanes = (dplane_t *) GetClearedMemory( MAX_MAP_PLANES * sizeof( dplane_t ) ); + allocatedbspmem += MAX_MAP_PLANES * sizeof( dplane_t ); + //vertexes + numvertexes = 0; + dvertexes = (dvertex_t *) GetClearedMemory( MAX_MAP_VERTS * sizeof( dvertex_t ) ); + allocatedbspmem += MAX_MAP_VERTS * sizeof( dvertex_t ); + //nodes + numnodes = 0; + dnodes = (dnode_t *) GetClearedMemory( MAX_MAP_NODES * sizeof( dnode_t ) ); + allocatedbspmem += MAX_MAP_NODES * sizeof( dnode_t ); + /* + //texture info + numtexinfo = 0; + texinfo = (texinfo_t *) GetClearedMemory(MAX_MAP_TEXINFO * sizeof(texinfo_t)); + allocatedbspmem += MAX_MAP_TEXINFO * sizeof(texinfo_t); + //*/ + //faces + numfaces = 0; + dfaces = (dface_t *) GetClearedMemory( MAX_MAP_FACES * sizeof( dface_t ) ); + allocatedbspmem += MAX_MAP_FACES * sizeof( dface_t ); + //edges + numedges = 0; + dedges = (dedge_t *) GetClearedMemory( MAX_MAP_EDGES * sizeof( dedge_t ) ); + allocatedbspmem += MAX_MAP_EDGES * sizeof( dedge_t ); + //leaf faces + numleaffaces = 0; + dleaffaces = (unsigned short *) GetClearedMemory( MAX_MAP_LEAFFACES * sizeof( unsigned short ) ); + allocatedbspmem += MAX_MAP_LEAFFACES * sizeof( unsigned short ); + //leaf brushes + numleafbrushes = 0; + dleafbrushes = (unsigned short *) GetClearedMemory( MAX_MAP_LEAFBRUSHES * sizeof( unsigned short ) ); + allocatedbspmem += MAX_MAP_LEAFBRUSHES * sizeof( unsigned short ); + //surface edges + numsurfedges = 0; + dsurfedges = (int *) GetClearedMemory( MAX_MAP_SURFEDGES * sizeof( int ) ); + allocatedbspmem += MAX_MAP_SURFEDGES * sizeof( int ); + //brushes + numbrushes = 0; + dbrushes = (dbrush_t *) GetClearedMemory( MAX_MAP_BRUSHES * sizeof( dbrush_t ) ); + allocatedbspmem += MAX_MAP_BRUSHES * sizeof( dbrush_t ); + //brushsides + numbrushsides = 0; + dbrushsides = (dbrushside_t *) GetClearedMemory( MAX_MAP_BRUSHSIDES * sizeof( dbrushside_t ) ); + allocatedbspmem += MAX_MAP_BRUSHSIDES * sizeof( dbrushside_t ); + //areas + numareas = 0; + dareas = (darea_t *) GetClearedMemory( MAX_MAP_AREAS * sizeof( darea_t ) ); + allocatedbspmem += MAX_MAP_AREAS * sizeof( darea_t ); + //area portals + numareaportals = 0; + dareaportals = (dareaportal_t *) GetClearedMemory( MAX_MAP_AREAPORTALS * sizeof( dareaportal_t ) ); + allocatedbspmem += MAX_MAP_AREAPORTALS * sizeof( dareaportal_t ); + //print allocated memory + Log_Print( "allocated " ); + PrintMemorySize( allocatedbspmem ); + Log_Print( " of BSP memory\n" ); +} //end of the function Q2_AllocMaxBSP + +void Q2_FreeMaxBSP( void ) { + //models + nummodels = 0; + FreeMemory( dmodels ); + dmodels = NULL; + //vis data + visdatasize = 0; + FreeMemory( dvisdata ); + dvisdata = NULL; + dvis = NULL; + //light data + lightdatasize = 0; + FreeMemory( dlightdata ); + dlightdata = NULL; + //entity data + entdatasize = 0; + FreeMemory( dentdata ); + dentdata = NULL; + //leafs + numleafs = 0; + FreeMemory( dleafs ); + dleafs = NULL; + //planes + numplanes = 0; + FreeMemory( dplanes ); + dplanes = NULL; + //vertexes + numvertexes = 0; + FreeMemory( dvertexes ); + dvertexes = NULL; + //nodes + numnodes = 0; + FreeMemory( dnodes ); + dnodes = NULL; + /* + //texture info + numtexinfo = 0; + FreeMemory(texinfo); + texinfo = NULL; + //*/ + //faces + numfaces = 0; + FreeMemory( dfaces ); + dfaces = NULL; + //edges + numedges = 0; + FreeMemory( dedges ); + dedges = NULL; + //leaf faces + numleaffaces = 0; + FreeMemory( dleaffaces ); + dleaffaces = NULL; + //leaf brushes + numleafbrushes = 0; + FreeMemory( dleafbrushes ); + dleafbrushes = NULL; + //surface edges + numsurfedges = 0; + FreeMemory( dsurfedges ); + dsurfedges = NULL; + //brushes + numbrushes = 0; + FreeMemory( dbrushes ); + dbrushes = NULL; + //brushsides + numbrushsides = 0; + FreeMemory( dbrushsides ); + dbrushsides = NULL; + //areas + numareas = 0; + FreeMemory( dareas ); + dareas = NULL; + //area portals + numareaportals = 0; + FreeMemory( dareaportals ); + dareaportals = NULL; + // + Log_Print( "freed " ); + PrintMemorySize( allocatedbspmem ); + Log_Print( " of BSP memory\n" ); + allocatedbspmem = 0; +} //end of the function Q2_FreeMaxBSP + +#define WCONVEX_EPSILON 0.5 + +int InsideWinding( winding_t *w, vec3_t point, int planenum ) { + int i; + float dist; + vec_t *v1, *v2; + vec3_t normal, edgevec; + dplane_t *plane; + + for ( i = 1; i <= w->numpoints; i++ ) + { + v1 = w->p[i % w->numpoints]; + v2 = w->p[( i + 1 ) % w->numpoints]; + + VectorSubtract( v2, v1, edgevec ); + plane = &dplanes[planenum]; + CrossProduct( plane->normal, edgevec, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + if ( DotProduct( normal, point ) - dist > WCONVEX_EPSILON ) { + return false; + } + } //end for + return true; +} //end of the function InsideWinding + +int InsideFace( dface_t *face, vec3_t point ) { + int i, edgenum, side; + float dist; + vec_t *v1, *v2; + vec3_t normal, edgevec; + dplane_t *plane; + + for ( i = 0; i < face->numedges; i++ ) + { + //get the first and second vertex of the edge + edgenum = dsurfedges[face->firstedge + i]; + side = edgenum < 0; + v1 = dvertexes[dedges[abs( edgenum )].v[side]].point; + v2 = dvertexes[dedges[abs( edgenum )].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract( v1, v2, edgevec ); + plane = &dplanes[face->planenum]; + CrossProduct( plane->normal, edgevec, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + if ( DotProduct( normal, point ) - dist > WCONVEX_EPSILON ) { + return false; + } + } //end for + return true; +} //end of the function InsideFace +//=========================================================================== +// returns the amount the face and the winding overlap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q2_FaceOnWinding( q2_dface_t *face, winding_t *winding ) { + int i, edgenum, side; + float dist, area; + q2_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + winding_t *w; + + // + w = CopyWinding( winding ); + memcpy( &plane, &q2_dplanes[face->planenum], sizeof( q2_dplane_t ) ); + //check on which side of the plane the face is + if ( face->side ) { + VectorNegate( plane.normal, plane.normal ); + plane.dist = -plane.dist; + } //end if + for ( i = 0; i < face->numedges && w; i++ ) + { + //get the first and second vertex of the edge + edgenum = q2_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = q2_dvertexes[q2_dedges[abs( edgenum )].v[side]].point; + v2 = q2_dvertexes[q2_dedges[abs( edgenum )].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing inward + VectorSubtract( v1, v2, edgevec ); + CrossProduct( edgevec, plane.normal, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + ChopWindingInPlace( &w, normal, dist, -0.1 ); //CLIP_EPSILON + } //end for + if ( w ) { + area = WindingArea( w ); + FreeWinding( w ); + return area; + } //end if + return 0; +} //end of the function Q2_FaceOnWinding +//=========================================================================== +// creates a winding for the given brush side on the given brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +winding_t *Q2_BrushSideWinding( dbrush_t *brush, dbrushside_t *baseside ) { + int i; + dplane_t *baseplane, *plane; + winding_t *w; + dbrushside_t *side; + + //create a winding for the brush side with the given planenumber + baseplane = &dplanes[baseside->planenum]; + w = BaseWindingForPlane( baseplane->normal, baseplane->dist ); + for ( i = 0; i < brush->numsides && w; i++ ) + { + side = &dbrushsides[brush->firstside + i]; + //don't chop with the base plane + if ( side->planenum == baseside->planenum ) { + continue; + } + //also don't use planes that are almost equal + plane = &dplanes[side->planenum]; + if ( DotProduct( baseplane->normal, plane->normal ) > 0.999 + && fabs( baseplane->dist - plane->dist ) < 0.01 ) { + continue; + } + // + plane = &dplanes[side->planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, -0.1 ); //CLIP_EPSILON); + } //end for + return w; +} //end of the function Q2_BrushSideWinding +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q2_HintSkipBrush( dbrush_t *brush ) { + int j; + dbrushside_t *brushside; + + for ( j = 0; j < brush->numsides; j++ ) + { + brushside = &dbrushsides[brush->firstside + j]; + if ( brushside->texinfo > 0 ) { +// if (texinfo[brushside->texinfo].flags & (SURF_SKIP|SURF_HINT)) +// { +// return true; +// } //end if + } //end if + } //end for + return false; +} //end of the function Q2_HintSkipBrush +//=========================================================================== +// fix screwed brush texture references +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsTiny( winding_t *w ); + +void Q2_FixTextureReferences( void ) { + int i, j, k, we; + dbrushside_t *brushside; + dbrush_t *brush; + dface_t *face; + winding_t *w; + + memset( brushsidetextured, false, MAX_MAP_BRUSHSIDES ); + //go over all the brushes + for ( i = 0; i < numbrushes; i++ ) + { + brush = &dbrushes[i]; + //hint brushes are not textured + if ( Q2_HintSkipBrush( brush ) ) { + continue; + } + //go over all the sides of the brush + for ( j = 0; j < brush->numsides; j++ ) + { + brushside = &dbrushsides[brush->firstside + j]; + // + w = Q2_BrushSideWinding( brush, brushside ); + if ( !w ) { + brushsidetextured[brush->firstside + j] = true; + continue; + } //end if + else + { + //RemoveEqualPoints(w, 0.2); + if ( WindingIsTiny( w ) ) { + FreeWinding( w ); + brushsidetextured[brush->firstside + j] = true; + continue; + } //end if + else + { + we = WindingError( w ); + if ( we == WE_NOTENOUGHPOINTS + || we == WE_SMALLAREA + || we == WE_POINTBOGUSRANGE +// || we == WE_NONCONVEX + ) { + FreeWinding( w ); + brushsidetextured[brush->firstside + j] = true; + continue; + } //end if + } //end else + } //end else + if ( WindingArea( w ) < 20 ) { + brushsidetextured[brush->firstside + j] = true; + } //end if + //find a face for texturing this brush + for ( k = 0; k < numfaces; k++ ) + { + face = &dfaces[k]; + //if the face is in the same plane as the brush side + if ( ( face->planenum & ~1 ) != ( brushside->planenum & ~1 ) ) { + continue; + } + //if the face is partly or totally on the brush side + if ( Q2_FaceOnWinding( face, w ) ) { + brushside->texinfo = face->texinfo; + brushsidetextured[brush->firstside + j] = true; + break; + } //end if + } //end for + FreeWinding( w ); + } //end for + } //end for +} //end of the function Q2_FixTextureReferences*/ + +//#endif //ME + + +/* +=============== +CompressVis + +=============== +*/ +int Q2_CompressVis( byte *vis, byte *dest ) { + int j; + int rep; + int visrow; + byte *dest_p; + + dest_p = dest; +// visrow = (r_numvisleafs + 7)>>3; + visrow = ( dvis->numclusters + 7 ) >> 3; + + for ( j = 0 ; j < visrow ; j++ ) + { + *dest_p++ = vis[j]; + if ( vis[j] ) { + continue; + } + + rep = 1; + for ( j++; j < visrow ; j++ ) + if ( vis[j] || rep == 255 ) { + break; + } else { + rep++; + } + *dest_p++ = rep; + j--; + } + + return dest_p - dest; +} + + +/* +=================== +DecompressVis +=================== +*/ +void Q2_DecompressVis( byte *in, byte *decompressed ) { + int c; + byte *out; + int row; + +// row = (r_numvisleafs+7)>>3; + row = ( dvis->numclusters + 7 ) >> 3; + out = decompressed; + + do + { + if ( *in ) { + *out++ = *in++; + continue; + } + + c = in[1]; + if ( !c ) { + Error( "DecompressVis: 0 repeat" ); + } + in += 2; + while ( c ) + { + *out++ = 0; + c--; + } + } while ( out - decompressed < row ); +} + +//============================================================================= + +/* +============= +SwapBSPFile + +Byte swaps all data in a bsp file. +============= +*/ +void Q2_SwapBSPFile( qboolean todisk ) { + int i, j; + dmodel_t *d; + + +// models + for ( i = 0 ; i < nummodels ; i++ ) + { + d = &dmodels[i]; + + d->firstface = LittleLong( d->firstface ); + d->numfaces = LittleLong( d->numfaces ); + d->headnode = LittleLong( d->headnode ); + + for ( j = 0 ; j < 3 ; j++ ) + { + d->mins[j] = LittleFloat( d->mins[j] ); + d->maxs[j] = LittleFloat( d->maxs[j] ); + d->origin[j] = LittleFloat( d->origin[j] ); + } + } + +// +// vertexes +// + for ( i = 0 ; i < numvertexes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + dvertexes[i].point[j] = LittleFloat( dvertexes[i].point[j] ); + } + +// +// planes +// + for ( i = 0 ; i < numplanes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + dplanes[i].normal[j] = LittleFloat( dplanes[i].normal[j] ); + dplanes[i].dist = LittleFloat( dplanes[i].dist ); + dplanes[i].type = LittleLong( dplanes[i].type ); + } + +// +// texinfos +// + for ( i = 0 ; i < numtexinfo ; i++ ) + { + for ( j = 0 ; j < 8 ; j++ ) + texinfo[i].vecs[0][j] = LittleFloat( texinfo[i].vecs[0][j] ); + texinfo[i].flags = LittleLong( texinfo[i].flags ); + texinfo[i].value = LittleLong( texinfo[i].value ); + texinfo[i].nexttexinfo = LittleLong( texinfo[i].nexttexinfo ); + } + +// +// faces +// + for ( i = 0 ; i < numfaces ; i++ ) + { + dfaces[i].texinfo = LittleShort( dfaces[i].texinfo ); + dfaces[i].planenum = LittleShort( dfaces[i].planenum ); + dfaces[i].side = LittleShort( dfaces[i].side ); + dfaces[i].lightofs = LittleLong( dfaces[i].lightofs ); + dfaces[i].firstedge = LittleLong( dfaces[i].firstedge ); + dfaces[i].numedges = LittleShort( dfaces[i].numedges ); + } + +// +// nodes +// + for ( i = 0 ; i < numnodes ; i++ ) + { + dnodes[i].planenum = LittleLong( dnodes[i].planenum ); + for ( j = 0 ; j < 3 ; j++ ) + { + dnodes[i].mins[j] = LittleShort( dnodes[i].mins[j] ); + dnodes[i].maxs[j] = LittleShort( dnodes[i].maxs[j] ); + } + dnodes[i].children[0] = LittleLong( dnodes[i].children[0] ); + dnodes[i].children[1] = LittleLong( dnodes[i].children[1] ); + dnodes[i].firstface = LittleShort( dnodes[i].firstface ); + dnodes[i].numfaces = LittleShort( dnodes[i].numfaces ); + } + +// +// leafs +// + for ( i = 0 ; i < numleafs ; i++ ) + { + dleafs[i].contents = LittleLong( dleafs[i].contents ); + dleafs[i].cluster = LittleShort( dleafs[i].cluster ); + dleafs[i].area = LittleShort( dleafs[i].area ); + for ( j = 0 ; j < 3 ; j++ ) + { + dleafs[i].mins[j] = LittleShort( dleafs[i].mins[j] ); + dleafs[i].maxs[j] = LittleShort( dleafs[i].maxs[j] ); + } + + dleafs[i].firstleafface = LittleShort( dleafs[i].firstleafface ); + dleafs[i].numleaffaces = LittleShort( dleafs[i].numleaffaces ); + dleafs[i].firstleafbrush = LittleShort( dleafs[i].firstleafbrush ); + dleafs[i].numleafbrushes = LittleShort( dleafs[i].numleafbrushes ); + } + +// +// leaffaces +// + for ( i = 0 ; i < numleaffaces ; i++ ) + dleaffaces[i] = LittleShort( dleaffaces[i] ); + +// +// leafbrushes +// + for ( i = 0 ; i < numleafbrushes ; i++ ) + dleafbrushes[i] = LittleShort( dleafbrushes[i] ); + +// +// surfedges +// + for ( i = 0 ; i < numsurfedges ; i++ ) + dsurfedges[i] = LittleLong( dsurfedges[i] ); + +// +// edges +// + for ( i = 0 ; i < numedges ; i++ ) + { + dedges[i].v[0] = LittleShort( dedges[i].v[0] ); + dedges[i].v[1] = LittleShort( dedges[i].v[1] ); + } + +// +// brushes +// + for ( i = 0 ; i < numbrushes ; i++ ) + { + dbrushes[i].firstside = LittleLong( dbrushes[i].firstside ); + dbrushes[i].numsides = LittleLong( dbrushes[i].numsides ); + dbrushes[i].contents = LittleLong( dbrushes[i].contents ); + } + +// +// areas +// + for ( i = 0 ; i < numareas ; i++ ) + { + dareas[i].numareaportals = LittleLong( dareas[i].numareaportals ); + dareas[i].firstareaportal = LittleLong( dareas[i].firstareaportal ); + } + +// +// areasportals +// + for ( i = 0 ; i < numareaportals ; i++ ) + { + dareaportals[i].portalnum = LittleLong( dareaportals[i].portalnum ); + dareaportals[i].otherarea = LittleLong( dareaportals[i].otherarea ); + } + +// +// brushsides +// + for ( i = 0 ; i < numbrushsides ; i++ ) + { + dbrushsides[i].planenum = LittleShort( dbrushsides[i].planenum ); + dbrushsides[i].texinfo = LittleShort( dbrushsides[i].texinfo ); + } + +// +// visibility +// + if ( todisk ) { + j = dvis->numclusters; + } else { + j = LittleLong( dvis->numclusters ); + } + dvis->numclusters = LittleLong( dvis->numclusters ); + for ( i = 0 ; i < j ; i++ ) + { + dvis->bitofs[i][0] = LittleLong( dvis->bitofs[i][0] ); + dvis->bitofs[i][1] = LittleLong( dvis->bitofs[i][1] ); + } +} //end of the function Q2_SwapBSPFile + + +dheader_t *header; + +int Q2_CopyLump( int lump, void *dest, int size, int maxsize ) { + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if ( length % size ) { + Error( "LoadBSPFile: odd lump size" ); + } + + if ( ( length / size ) > maxsize ) { + Error( "Q2_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, ( length / size ), maxsize ); + } + + memcpy( dest, (byte *)header + ofs, length ); + + return length / size; +} //end of the function Q2_CopyLump + +/* +============= +LoadBSPFile +============= +*/ +void Q2_LoadBSPFile( char *filename, int offset, int length ) { + int i; + +// +// load the file header +// + LoadFile( filename, (void **)&header, offset, length ); + +// swap the header + for ( i = 0 ; i < sizeof( dheader_t ) / 4 ; i++ ) + ( (int *)header )[i] = LittleLong( ( (int *)header )[i] ); + + if ( header->ident != IDBSPHEADER ) { + Error( "%s is not a IBSP file", filename ); + } + if ( header->version != BSPVERSION ) { + Error( "%s is version %i, not %i", filename, header->version, BSPVERSION ); + } + + nummodels = Q2_CopyLump( LUMP_MODELS, dmodels, sizeof( dmodel_t ), MAX_MAP_MODELS ); + numvertexes = Q2_CopyLump( LUMP_VERTEXES, dvertexes, sizeof( dvertex_t ), MAX_MAP_VERTS ); + numplanes = Q2_CopyLump( LUMP_PLANES, dplanes, sizeof( dplane_t ), MAX_MAP_PLANES ); + numleafs = Q2_CopyLump( LUMP_LEAFS, dleafs, sizeof( dleaf_t ), MAX_MAP_LEAFS ); + numnodes = Q2_CopyLump( LUMP_NODES, dnodes, sizeof( dnode_t ), MAX_MAP_NODES ); + numtexinfo = Q2_CopyLump( LUMP_TEXINFO, texinfo, sizeof( texinfo_t ), MAX_MAP_TEXINFO ); + numfaces = Q2_CopyLump( LUMP_FACES, dfaces, sizeof( dface_t ), MAX_MAP_FACES ); + numleaffaces = Q2_CopyLump( LUMP_LEAFFACES, dleaffaces, sizeof( dleaffaces[0] ), MAX_MAP_LEAFFACES ); + numleafbrushes = Q2_CopyLump( LUMP_LEAFBRUSHES, dleafbrushes, sizeof( dleafbrushes[0] ), MAX_MAP_LEAFBRUSHES ); + numsurfedges = Q2_CopyLump( LUMP_SURFEDGES, dsurfedges, sizeof( dsurfedges[0] ), MAX_MAP_SURFEDGES ); + numedges = Q2_CopyLump( LUMP_EDGES, dedges, sizeof( dedge_t ), MAX_MAP_EDGES ); + numbrushes = Q2_CopyLump( LUMP_BRUSHES, dbrushes, sizeof( dbrush_t ), MAX_MAP_BRUSHES ); + numbrushsides = Q2_CopyLump( LUMP_BRUSHSIDES, dbrushsides, sizeof( dbrushside_t ), MAX_MAP_BRUSHSIDES ); + numareas = Q2_CopyLump( LUMP_AREAS, dareas, sizeof( darea_t ), MAX_MAP_AREAS ); + numareaportals = Q2_CopyLump( LUMP_AREAPORTALS, dareaportals, sizeof( dareaportal_t ), MAX_MAP_AREAPORTALS ); + + visdatasize = Q2_CopyLump( LUMP_VISIBILITY, dvisdata, 1, MAX_MAP_VISIBILITY ); + lightdatasize = Q2_CopyLump( LUMP_LIGHTING, dlightdata, 1, MAX_MAP_LIGHTING ); + entdatasize = Q2_CopyLump( LUMP_ENTITIES, dentdata, 1, MAX_MAP_ENTSTRING ); + + Q2_CopyLump( LUMP_POP, dpop, 1, MAX_MAP_DPOP ); + + FreeMemory( header ); // everything has been copied out + +// +// swap everything +// + Q2_SwapBSPFile( false ); + + Q2_FixTextureReferences(); +} //end of the function Q2_LoadBSPFile + + +/* +============= +LoadBSPFileTexinfo + +Only loads the texinfo lump, so qdata can scan for textures +============= +*/ +void Q2_LoadBSPFileTexinfo( char *filename ) { + int i; + FILE *f; + int length, ofs; + + header = GetMemory( sizeof( dheader_t ) ); + + f = fopen( filename, "rb" ); + fread( header, sizeof( dheader_t ), 1, f ); + +// swap the header + for ( i = 0 ; i < sizeof( dheader_t ) / 4 ; i++ ) + ( (int *)header )[i] = LittleLong( ( (int *)header )[i] ); + + if ( header->ident != IDBSPHEADER ) { + Error( "%s is not a IBSP file", filename ); + } + if ( header->version != BSPVERSION ) { + Error( "%s is version %i, not %i", filename, header->version, BSPVERSION ); + } + + + length = header->lumps[LUMP_TEXINFO].filelen; + ofs = header->lumps[LUMP_TEXINFO].fileofs; + + fseek( f, ofs, SEEK_SET ); + fread( texinfo, length, 1, f ); + fclose( f ); + + numtexinfo = length / sizeof( texinfo_t ); + + FreeMemory( header ); // everything has been copied out + + Q2_SwapBSPFile( false ); +} //end of the function Q2_LoadBSPFileTexinfo + + +//============================================================================ + +FILE *wadfile; +dheader_t outheader; + +void Q2_AddLump( int lumpnum, void *data, int len ) { + lump_t *lump; + + lump = &header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( wadfile ) ); + lump->filelen = LittleLong( len ); + SafeWrite( wadfile, data, ( len + 3 ) & ~3 ); +} //end of the function Q2_AddLump + +/* +============= +WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void Q2_WriteBSPFile( char *filename ) { + header = &outheader; + memset( header, 0, sizeof( dheader_t ) ); + + Q2_SwapBSPFile( true ); + + header->ident = LittleLong( IDBSPHEADER ); + header->version = LittleLong( BSPVERSION ); + + wadfile = SafeOpenWrite( filename ); + SafeWrite( wadfile, header, sizeof( dheader_t ) ); // overwritten later + + Q2_AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplane_t ) ); + Q2_AddLump( LUMP_LEAFS, dleafs, numleafs * sizeof( dleaf_t ) ); + Q2_AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertex_t ) ); + Q2_AddLump( LUMP_NODES, dnodes, numnodes * sizeof( dnode_t ) ); + Q2_AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo_t ) ); + Q2_AddLump( LUMP_FACES, dfaces, numfaces * sizeof( dface_t ) ); + Q2_AddLump( LUMP_BRUSHES, dbrushes, numbrushes * sizeof( dbrush_t ) ); + Q2_AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides * sizeof( dbrushside_t ) ); + Q2_AddLump( LUMP_LEAFFACES, dleaffaces, numleaffaces * sizeof( dleaffaces[0] ) ); + Q2_AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ) ); + Q2_AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[0] ) ); + Q2_AddLump( LUMP_EDGES, dedges, numedges * sizeof( dedge_t ) ); + Q2_AddLump( LUMP_MODELS, dmodels, nummodels * sizeof( dmodel_t ) ); + Q2_AddLump( LUMP_AREAS, dareas, numareas * sizeof( darea_t ) ); + Q2_AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals * sizeof( dareaportal_t ) ); + + Q2_AddLump( LUMP_LIGHTING, dlightdata, lightdatasize ); + Q2_AddLump( LUMP_VISIBILITY, dvisdata, visdatasize ); + Q2_AddLump( LUMP_ENTITIES, dentdata, entdatasize ); + Q2_AddLump( LUMP_POP, dpop, sizeof( dpop ) ); + + fseek( wadfile, 0, SEEK_SET ); + SafeWrite( wadfile, header, sizeof( dheader_t ) ); + fclose( wadfile ); +} //end of the function Q2_WriteBSPFile + +//============================================================================ + +/* +============= +PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void Q2_PrintBSPFileSizes( void ) { + if ( !num_entities ) { + Q2_ParseEntities(); + } + + printf( "%6i models %7i\n" + ,nummodels, (int)( nummodels * sizeof( dmodel_t ) ) ); + printf( "%6i brushes %7i\n" + ,numbrushes, (int)( numbrushes * sizeof( dbrush_t ) ) ); + printf( "%6i brushsides %7i\n" + ,numbrushsides, (int)( numbrushsides * sizeof( dbrushside_t ) ) ); + printf( "%6i planes %7i\n" + ,numplanes, (int)( numplanes * sizeof( dplane_t ) ) ); + printf( "%6i texinfo %7i\n" + ,numtexinfo, (int)( numtexinfo * sizeof( texinfo_t ) ) ); + printf( "%6i entdata %7i\n", num_entities, entdatasize ); + + printf( "\n" ); + + printf( "%6i vertexes %7i\n" + ,numvertexes, (int)( numvertexes * sizeof( dvertex_t ) ) ); + printf( "%6i nodes %7i\n" + ,numnodes, (int)( numnodes * sizeof( dnode_t ) ) ); + printf( "%6i faces %7i\n" + ,numfaces, (int)( numfaces * sizeof( dface_t ) ) ); + printf( "%6i leafs %7i\n" + ,numleafs, (int)( numleafs * sizeof( dleaf_t ) ) ); + printf( "%6i leaffaces %7i\n" + ,numleaffaces, (int)( numleaffaces * sizeof( dleaffaces[0] ) ) ); + printf( "%6i leafbrushes %7i\n" + ,numleafbrushes, (int)( numleafbrushes * sizeof( dleafbrushes[0] ) ) ); + printf( "%6i surfedges %7i\n" + ,numsurfedges, (int)( numsurfedges * sizeof( dsurfedges[0] ) ) ); + printf( "%6i edges %7i\n" + ,numedges, (int)( numedges * sizeof( dedge_t ) ) ); +//NEW + printf( "%6i areas %7i\n" + ,numareas, (int)( numareas * sizeof( darea_t ) ) ); + printf( "%6i areaportals %7i\n" + ,numareaportals, (int)( numareaportals * sizeof( dareaportal_t ) ) ); +//ENDNEW + printf( " lightdata %7i\n", lightdatasize ); + printf( " visdata %7i\n", visdatasize ); +} //end of the function Q2_PrintBSPFileSizes + +/* +================ +ParseEntities + +Parses the dentdata string into entities +================ +*/ +void Q2_ParseEntities( void ) { + script_t *script; + + num_entities = 0; + script = LoadScriptMemory( dentdata, entdatasize, "*Quake2 bsp file" ); + SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | + SCFL_NOSTRINGESCAPECHARS ); + + while ( ParseEntity( script ) ) + { + } //end while + + FreeScript( script ); +} //end of the function Q2_ParseEntities + + +/* +================ +UnparseEntities + +Generates the dentdata string from all the entities +================ +*/ +void Q2_UnparseEntities( void ) { + char *buf, *end; + epair_t *ep; + char line[2048]; + int i; + char key[1024], value[1024]; + + buf = dentdata; + end = buf; + *end = 0; + + for ( i = 0 ; i < num_entities ; i++ ) + { + ep = entities[i].epairs; + if ( !ep ) { + continue; // ent got removed + + } + strcat( end,"{\n" ); + end += 2; + + for ( ep = entities[i].epairs ; ep ; ep = ep->next ) + { + strcpy( key, ep->key ); + StripTrailing( key ); + strcpy( value, ep->value ); + StripTrailing( value ); + + sprintf( line, "\"%s\" \"%s\"\n", key, value ); + strcat( end, line ); + end += strlen( line ); + } + strcat( end,"}\n" ); + end += 2; + + if ( end > buf + MAX_MAP_ENTSTRING ) { + Error( "Entity text too long" ); + } + } + entdatasize = end - buf + 1; +} //end of the function Q2_UnparseEntities + diff --git a/src/bspc/l_bsp_q2.h b/src/bspc/l_bsp_q2.h new file mode 100644 index 0000000..4b9770d --- /dev/null +++ b/src/bspc/l_bsp_q2.h @@ -0,0 +1,104 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#ifndef ME +#define ME +#endif //ME + +extern int nummodels; +extern dmodel_t *dmodels; //[MAX_MAP_MODELS]; + +extern int visdatasize; +extern byte *dvisdata; //[MAX_MAP_VISIBILITY]; +extern dvis_t *dvis; + +extern int lightdatasize; +extern byte *dlightdata; //[MAX_MAP_LIGHTING]; + +extern int entdatasize; +extern char *dentdata; //[MAX_MAP_ENTSTRING]; + +extern int numleafs; +extern dleaf_t *dleafs; //[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t *dplanes; //[MAX_MAP_PLANES]; + +extern int numvertexes; +extern dvertex_t *dvertexes; //[MAX_MAP_VERTS]; + +extern int numnodes; +extern dnode_t *dnodes; //[MAX_MAP_NODES]; + +extern int numtexinfo; +extern texinfo_t texinfo[MAX_MAP_TEXINFO]; + +extern int numfaces; +extern dface_t *dfaces; //[MAX_MAP_FACES]; + +extern int numedges; +extern dedge_t *dedges; //[MAX_MAP_EDGES]; + +extern int numleaffaces; +extern unsigned short *dleaffaces; //[MAX_MAP_LEAFFACES]; + +extern int numleafbrushes; +extern unsigned short *dleafbrushes; //[MAX_MAP_LEAFBRUSHES]; + +extern int numsurfedges; +extern int *dsurfedges; //[MAX_MAP_SURFEDGES]; + +extern int numareas; +extern darea_t *dareas; //[MAX_MAP_AREAS]; + +extern int numareaportals; +extern dareaportal_t *dareaportals; //[MAX_MAP_AREAPORTALS]; + +extern int numbrushes; +extern dbrush_t *dbrushes; //[MAX_MAP_BRUSHES]; + +extern int numbrushsides; +extern dbrushside_t *dbrushsides; //[MAX_MAP_BRUSHSIDES]; + +extern byte dpop[256]; + +extern char brushsidetextured[MAX_MAP_BRUSHSIDES]; + +void Q2_AllocMaxBSP( void ); +void Q2_FreeMaxBSP( void ); + +void Q2_DecompressVis( byte *in, byte *decompressed ); +int Q2_CompressVis( byte *vis, byte *dest ); + +void Q2_LoadBSPFile( char *filename, int offset, int length ); +void Q2_LoadBSPFileTexinfo( char *filename ); // just for qdata +void Q2_WriteBSPFile( char *filename ); +void Q2_PrintBSPFileSizes( void ); +void Q2_ParseEntities( void ); +void Q2_UnparseEntities( void ); + diff --git a/src/bspc/l_bsp_q3.c b/src/bspc/l_bsp_q3.c new file mode 100644 index 0000000..fe32b8e --- /dev/null +++ b/src/bspc/l_bsp_q3.c @@ -0,0 +1,873 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "l_cmd.h" +#include "l_math.h" +#include "l_mem.h" +#include "l_log.h" +#include "l_poly.h" +#include "../botlib/l_script.h" +#include "l_qfiles.h" +#include "l_bsp_q3.h" +#include "l_bsp_ent.h" +//#include "qfiles.h" +//#include "q2files.h" +#include "..\game\surfaceflags.h" + +void Q3_ParseEntities( void ); +void Q3_PrintBSPFileSizes( void ); + +void GetLeafNums( void ); + +//============================================================================= + +#define WCONVEX_EPSILON 0.5 + +int q3_nummodels; +q3_dmodel_t *q3_dmodels; //[MAX_MAP_MODELS]; + +int q3_numShaders; +q3_dshader_t *q3_dshaders; //[Q3_MAX_MAP_SHADERS]; + +int q3_entdatasize; +char *q3_dentdata; //[Q3_MAX_MAP_ENTSTRING]; + +int q3_numleafs; +q3_dleaf_t *q3_dleafs; //[Q3_MAX_MAP_LEAFS]; + +int q3_numplanes; +q3_dplane_t *q3_dplanes; //[Q3_MAX_MAP_PLANES]; + +int q3_numnodes; +q3_dnode_t *q3_dnodes; //[Q3_MAX_MAP_NODES]; + +int q3_numleafsurfaces; +int *q3_dleafsurfaces; //[Q3_MAX_MAP_LEAFFACES]; + +int q3_numleafbrushes; +int *q3_dleafbrushes; //[Q3_MAX_MAP_LEAFBRUSHES]; + +int q3_numbrushes; +q3_dbrush_t *q3_dbrushes; //[Q3_MAX_MAP_BRUSHES]; + +int q3_numbrushsides; +q3_dbrushside_t *q3_dbrushsides; //[Q3_MAX_MAP_BRUSHSIDES]; + +int q3_numLightBytes; +byte *q3_lightBytes; //[Q3_MAX_MAP_LIGHTING]; + +int q3_numGridPoints; +byte *q3_gridData; //[Q3_MAX_MAP_LIGHTGRID]; + +int q3_numVisBytes; +byte *q3_visBytes; //[Q3_MAX_MAP_VISIBILITY]; + +int q3_numDrawVerts; +q3_drawVert_t *q3_drawVerts; //[Q3_MAX_MAP_DRAW_VERTS]; + +int q3_numDrawIndexes; +int *q3_drawIndexes; //[Q3_MAX_MAP_DRAW_INDEXES]; + +int q3_numDrawSurfaces; +q3_dsurface_t *q3_drawSurfaces; //[Q3_MAX_MAP_DRAW_SURFS]; + +int q3_numFogs; +q3_dfog_t *q3_dfogs; //[Q3_MAX_MAP_FOGS]; + +char q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES]; + +extern qboolean forcesidesvisible; + + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_FreeMaxBSP( void ) { + if ( q3_dmodels ) { + FreeMemory( q3_dmodels ); + } + q3_dmodels = NULL; + q3_nummodels = 0; + if ( q3_dshaders ) { + FreeMemory( q3_dshaders ); + } + q3_dshaders = NULL; + q3_numShaders = 0; + if ( q3_dentdata ) { + FreeMemory( q3_dentdata ); + } + q3_dentdata = NULL; + q3_entdatasize = 0; + if ( q3_dleafs ) { + FreeMemory( q3_dleafs ); + } + q3_dleafs = NULL; + q3_numleafs = 0; + if ( q3_dplanes ) { + FreeMemory( q3_dplanes ); + } + q3_dplanes = NULL; + q3_numplanes = 0; + if ( q3_dnodes ) { + FreeMemory( q3_dnodes ); + } + q3_dnodes = NULL; + q3_numnodes = 0; + if ( q3_dleafsurfaces ) { + FreeMemory( q3_dleafsurfaces ); + } + q3_dleafsurfaces = NULL; + q3_numleafsurfaces = 0; + if ( q3_dleafbrushes ) { + FreeMemory( q3_dleafbrushes ); + } + q3_dleafbrushes = NULL; + q3_numleafbrushes = 0; + if ( q3_dbrushes ) { + FreeMemory( q3_dbrushes ); + } + q3_dbrushes = NULL; + q3_numbrushes = 0; + if ( q3_dbrushsides ) { + FreeMemory( q3_dbrushsides ); + } + q3_dbrushsides = NULL; + q3_numbrushsides = 0; + if ( q3_lightBytes ) { + FreeMemory( q3_lightBytes ); + } + q3_lightBytes = NULL; + q3_numLightBytes = 0; + if ( q3_gridData ) { + FreeMemory( q3_gridData ); + } + q3_gridData = NULL; + q3_numGridPoints = 0; + if ( q3_visBytes ) { + FreeMemory( q3_visBytes ); + } + q3_visBytes = NULL; + q3_numVisBytes = 0; + if ( q3_drawVerts ) { + FreeMemory( q3_drawVerts ); + } + q3_drawVerts = NULL; + q3_numDrawVerts = 0; + if ( q3_drawIndexes ) { + FreeMemory( q3_drawIndexes ); + } + q3_drawIndexes = NULL; + q3_numDrawIndexes = 0; + if ( q3_drawSurfaces ) { + FreeMemory( q3_drawSurfaces ); + } + q3_drawSurfaces = NULL; + q3_numDrawSurfaces = 0; + if ( q3_dfogs ) { + FreeMemory( q3_dfogs ); + } + q3_dfogs = NULL; + q3_numFogs = 0; +} //end of the function Q3_FreeMaxBSP + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_PlaneFromPoints( vec3_t p0, vec3_t p1, vec3_t p2, vec3_t normal, float *dist ) { + vec3_t t1, t2; + + VectorSubtract( p0, p1, t1 ); + VectorSubtract( p2, p1, t2 ); + CrossProduct( t1, t2, normal ); + VectorNormalize( normal ); + + *dist = DotProduct( p0, normal ); +} //end of the function PlaneFromPoints +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_SurfacePlane( q3_dsurface_t *surface, vec3_t normal, float *dist ) { + int i; + float *p0, *p1, *p2; + vec3_t t1, t2; + + p0 = q3_drawVerts[surface->firstVert].xyz; + for ( i = 1; i < surface->numVerts - 1; i++ ) + { + p1 = q3_drawVerts[surface->firstVert + ( ( i ) % surface->numVerts )].xyz; + p2 = q3_drawVerts[surface->firstVert + ( ( i + 1 ) % surface->numVerts )].xyz; + VectorSubtract( p0, p1, t1 ); + VectorSubtract( p2, p1, t2 ); + CrossProduct( t1, t2, normal ); + VectorNormalize( normal ); + if ( VectorLength( normal ) ) { + break; + } + } //end for*/ +/* + float dot; + for (i = 0; i < surface->numVerts; i++) + { + p0 = q3_drawVerts[surface->firstVert + ((i) % surface->numVerts)].xyz; + p1 = q3_drawVerts[surface->firstVert + ((i+1) % surface->numVerts)].xyz; + p2 = q3_drawVerts[surface->firstVert + ((i+2) % surface->numVerts)].xyz; + VectorSubtract(p0, p1, t1); + VectorSubtract(p2, p1, t2); + VectorNormalize(t1); + VectorNormalize(t2); + dot = DotProduct(t1, t2); + if (dot > -0.9 && dot < 0.9 && + VectorLength(t1) > 0.1 && VectorLength(t2) > 0.1) break; + } //end for + CrossProduct(t1, t2, normal); + VectorNormalize(normal); +*/ + if ( VectorLength( normal ) < 0.9 ) { + printf( "surface %d bogus normal vector %f %f %f\n", surface - q3_drawSurfaces, normal[0], normal[1], normal[2] ); + printf( "t1 = %f %f %f, t2 = %f %f %f\n", t1[0], t1[1], t1[2], t2[0], t2[1], t2[2] ); + for ( i = 0; i < surface->numVerts; i++ ) + { + p1 = q3_drawVerts[surface->firstVert + ( ( i ) % surface->numVerts )].xyz; + Log_Print( "p%d = %f %f %f\n", i, p1[0], p1[1], p1[2] ); + } //end for + } //end if + *dist = DotProduct( p0, normal ); +} //end of the function Q3_SurfacePlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +q3_dplane_t *q3_surfaceplanes; + +void Q3_CreatePlanarSurfacePlanes( void ) { + int i; + q3_dsurface_t *surface; + + Log_Print( "creating planar surface planes...\n" ); + q3_surfaceplanes = (q3_dplane_t *) GetClearedMemory( q3_numDrawSurfaces * sizeof( q3_dplane_t ) ); + + for ( i = 0; i < q3_numDrawSurfaces; i++ ) + { + surface = &q3_drawSurfaces[i]; + if ( surface->surfaceType != MST_PLANAR ) { + continue; + } + Q3_SurfacePlane( surface, q3_surfaceplanes[i].normal, &q3_surfaceplanes[i].dist ); + //Log_Print("normal = %f %f %f, dist = %f\n", q3_surfaceplanes[i].normal[0], + // q3_surfaceplanes[i].normal[1], + // q3_surfaceplanes[i].normal[2], q3_surfaceplanes[i].dist); + } //end for +} //end of the function Q3_CreatePlanarSurfacePlanes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void Q3_SurfacePlane(q3_dsurface_t *surface, vec3_t normal, float *dist) +{ + //take the plane information from the lightmap vector + //VectorCopy(surface->lightmapVecs[2], normal); + //calculate plane dist with first surface vertex + //*dist = DotProduct(q3_drawVerts[surface->firstVert].xyz, normal); + Q3_PlaneFromPoints(q3_drawVerts[surface->firstVert].xyz, + q3_drawVerts[surface->firstVert+1].xyz, + q3_drawVerts[surface->firstVert+2].xyz, normal, dist); +} //end of the function Q3_SurfacePlane*/ +//=========================================================================== +// returns the amount the face and the winding overlap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q3_FaceOnWinding( q3_dsurface_t *surface, winding_t *winding ) { + int i; + float dist, area; + q3_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + winding_t *w; + + //copy the winding before chopping + w = CopyWinding( winding ); + //retrieve the surface plane + Q3_SurfacePlane( surface, plane.normal, &plane.dist ); + //chop the winding with the surface edge planes + for ( i = 0; i < surface->numVerts && w; i++ ) + { + v1 = q3_drawVerts[surface->firstVert + ( ( i ) % surface->numVerts )].xyz; + v2 = q3_drawVerts[surface->firstVert + ( ( i + 1 ) % surface->numVerts )].xyz; + //create a plane through the edge from v1 to v2, orthogonal to the + //surface plane and with the normal vector pointing inward + VectorSubtract( v2, v1, edgevec ); + CrossProduct( edgevec, plane.normal, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + ChopWindingInPlace( &w, normal, dist, -0.1 ); //CLIP_EPSILON + } //end for + if ( w ) { + area = WindingArea( w ); + FreeWinding( w ); + return area; + } //end if + return 0; +} //end of the function Q3_FaceOnWinding +//=========================================================================== +// creates a winding for the given brush side on the given brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +winding_t *Q3_BrushSideWinding( q3_dbrush_t *brush, q3_dbrushside_t *baseside ) { + int i; + q3_dplane_t *baseplane, *plane; + winding_t *w; + q3_dbrushside_t *side; + + //create a winding for the brush side with the given planenumber + baseplane = &q3_dplanes[baseside->planeNum]; + w = BaseWindingForPlane( baseplane->normal, baseplane->dist ); + for ( i = 0; i < brush->numSides && w; i++ ) + { + side = &q3_dbrushsides[brush->firstSide + i]; + //don't chop with the base plane + if ( side->planeNum == baseside->planeNum ) { + continue; + } + //also don't use planes that are almost equal + plane = &q3_dplanes[side->planeNum]; + if ( DotProduct( baseplane->normal, plane->normal ) > 0.999 + && fabs( baseplane->dist - plane->dist ) < 0.01 ) { + continue; + } + // + plane = &q3_dplanes[side->planeNum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, -0.1 ); //CLIP_EPSILON); + } //end for + return w; +} //end of the function Q3_BrushSideWinding +//=========================================================================== +// fix screwed brush texture references +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsTiny( winding_t *w ); + +void Q3_FindVisibleBrushSides( void ) { + int i, j, k, we, numtextured, numsides; + float dot; + q3_dplane_t *plane; + q3_dbrushside_t *brushside; + q3_dbrush_t *brush; + q3_dsurface_t *surface; + winding_t *w; + + memset( q3_dbrushsidetextured, false, Q3_MAX_MAP_BRUSHSIDES ); + // + numsides = 0; + //create planes for the planar surfaces + Q3_CreatePlanarSurfacePlanes(); + Log_Print( "searching visible brush sides...\n" ); + //go over all the brushes + if ( forcesidesvisible ) { + for ( i = 0; i < q3_numbrushsides; i++ ) { + if ( q3_dshaders[q3_dbrushsides[i].shaderNum].surfaceFlags & SURF_NODRAW ) { + q3_dbrushsidetextured[i] = false; + } else { + q3_dbrushsidetextured[i] = true; + } + } //end for + } else { + Log_Print( "%6d brush sides", numsides ); + for ( i = 0; i < q3_numbrushes; i++ ) + { + brush = &q3_dbrushes[i]; + // if one of the brush sides uses the terrain shader mark all sides as visible + for ( j = 0; j < brush->numSides; j++ ) { + brushside = &q3_dbrushsides[brush->firstSide + j]; + // NOTE: using "terrain" just like in q3map/terrain.c + if ( strstr( q3_dshaders[brushside->shaderNum].shader, "terrain" ) ) { + for ( j = 0; j < brush->numSides; j++ ) { + q3_dbrushsidetextured[brush->firstSide + j] = true; + } + break; + } + } + if ( j < brush->numSides ) { + continue; + } + //go over all the sides of the brush + for ( j = 0; j < brush->numSides; j++ ) + { + qprintf( "\r%6d", numsides++ ); + brushside = &q3_dbrushsides[brush->firstSide + j]; + // + w = Q3_BrushSideWinding( brush, brushside ); + if ( !w ) { + q3_dbrushsidetextured[brush->firstSide + j] = true; + continue; + } //end if + else + { + //RemoveEqualPoints(w, 0.2); + if ( WindingIsTiny( w ) ) { + FreeWinding( w ); + q3_dbrushsidetextured[brush->firstSide + j] = true; + continue; + } //end if + else + { + we = WindingError( w ); + if ( we == WE_NOTENOUGHPOINTS + || we == WE_SMALLAREA + || we == WE_POINTBOGUSRANGE + // || we == WE_NONCONVEX + ) { + FreeWinding( w ); + q3_dbrushsidetextured[brush->firstSide + j] = true; + continue; + } //end if + } //end else + } //end else + if ( WindingArea( w ) < 20 ) { + q3_dbrushsidetextured[brush->firstSide + j] = true; + continue; + } //end if + //find a face for texturing this brush + for ( k = 0; k < q3_numDrawSurfaces; k++ ) + { + surface = &q3_drawSurfaces[k]; + if ( surface->surfaceType != MST_PLANAR ) { + continue; + } + // + //Q3_SurfacePlane(surface, plane.normal, &plane.dist); + plane = &q3_surfaceplanes[k]; + //the surface plane and the brush side plane should be pretty much the same + if ( fabs( fabs( plane->dist ) - fabs( q3_dplanes[brushside->planeNum].dist ) ) > 5 ) { + continue; + } + dot = DotProduct( plane->normal, q3_dplanes[brushside->planeNum].normal ); + if ( dot > -0.9 && dot < 0.9 ) { + continue; + } + //if the face is partly or totally on the brush side + if ( Q3_FaceOnWinding( surface, w ) ) { + q3_dbrushsidetextured[brush->firstSide + j] = true; + //Log_Write("Q3_FaceOnWinding"); + break; + } //end if + } //end for + FreeWinding( w ); + } //end for + } //end for + qprintf( "\r%6d brush sides\n", numsides ); + } + numtextured = 0; + for ( i = 0; i < q3_numbrushsides; i++ ) { + if ( q3_dbrushsidetextured[i] ) { + numtextured++; + } + } //end for + Log_Print( "%d brush sides textured out of %d\n", numtextured, q3_numbrushsides ); +} //end of the function Q3_FindVisibleBrushSides + +/* +============= +Q3_SwapBlock + +If all values are 32 bits, this can be used to swap everything +============= +*/ +void Q3_SwapBlock( int *block, int sizeOfBlock ) { + int i; + + sizeOfBlock >>= 2; + for ( i = 0 ; i < sizeOfBlock ; i++ ) { + block[i] = LittleLong( block[i] ); + } +} //end of the function Q3_SwapBlock + +/* +============= +Q3_SwapBSPFile + +Byte swaps all data in a bsp file. +============= +*/ +void Q3_SwapBSPFile( void ) { + int i; + + // models + Q3_SwapBlock( (int *)q3_dmodels, q3_nummodels * sizeof( q3_dmodels[0] ) ); + + // shaders (don't swap the name) + for ( i = 0 ; i < q3_numShaders ; i++ ) { + + q3_dshaders[i].contentFlags = LittleLong( q3_dshaders[i].contentFlags ); + q3_dshaders[i].surfaceFlags = LittleLong( q3_dshaders[i].surfaceFlags ); + + // RF, HACK, clipweap causes problems, so convert to plain solid + if ( !strcmp( q3_dshaders[i].shader, "textures/common/clipweap" ) ) { + q3_dshaders[i].contentFlags = CONTENTS_MONSTERCLIP; + //q3_dshaders[i].surfaceFlags = SURF_NODRAW; + } + // + // RF, only keep content flags that are consistent with q3map + q3_dshaders[i].contentFlags &= ( CONTENTS_SOLID | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER | CONTENTS_AREAPORTAL | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_DETAIL | CONTENTS_CLUSTERPORTAL ); + } + + // planes + Q3_SwapBlock( (int *)q3_dplanes, q3_numplanes * sizeof( q3_dplanes[0] ) ); + + // nodes + Q3_SwapBlock( (int *)q3_dnodes, q3_numnodes * sizeof( q3_dnodes[0] ) ); + + // leafs + Q3_SwapBlock( (int *)q3_dleafs, q3_numleafs * sizeof( q3_dleafs[0] ) ); + + // leaffaces + Q3_SwapBlock( (int *)q3_dleafsurfaces, q3_numleafsurfaces * sizeof( q3_dleafsurfaces[0] ) ); + + // leafbrushes + Q3_SwapBlock( (int *)q3_dleafbrushes, q3_numleafbrushes * sizeof( q3_dleafbrushes[0] ) ); + + // brushes + Q3_SwapBlock( (int *)q3_dbrushes, q3_numbrushes * sizeof( q3_dbrushes[0] ) ); + + // brushsides + Q3_SwapBlock( (int *)q3_dbrushsides, q3_numbrushsides * sizeof( q3_dbrushsides[0] ) ); + + // vis + ( (int *)&q3_visBytes )[0] = LittleLong( ( (int *)&q3_visBytes )[0] ); + ( (int *)&q3_visBytes )[1] = LittleLong( ( (int *)&q3_visBytes )[1] ); + + // drawverts (don't swap colors ) + for ( i = 0 ; i < q3_numDrawVerts ; i++ ) { + q3_drawVerts[i].lightmap[0] = LittleFloat( q3_drawVerts[i].lightmap[0] ); + q3_drawVerts[i].lightmap[1] = LittleFloat( q3_drawVerts[i].lightmap[1] ); + q3_drawVerts[i].st[0] = LittleFloat( q3_drawVerts[i].st[0] ); + q3_drawVerts[i].st[1] = LittleFloat( q3_drawVerts[i].st[1] ); + q3_drawVerts[i].xyz[0] = LittleFloat( q3_drawVerts[i].xyz[0] ); + q3_drawVerts[i].xyz[1] = LittleFloat( q3_drawVerts[i].xyz[1] ); + q3_drawVerts[i].xyz[2] = LittleFloat( q3_drawVerts[i].xyz[2] ); + q3_drawVerts[i].normal[0] = LittleFloat( q3_drawVerts[i].normal[0] ); + q3_drawVerts[i].normal[1] = LittleFloat( q3_drawVerts[i].normal[1] ); + q3_drawVerts[i].normal[2] = LittleFloat( q3_drawVerts[i].normal[2] ); + } + + // drawindexes + Q3_SwapBlock( (int *)q3_drawIndexes, q3_numDrawIndexes * sizeof( q3_drawIndexes[0] ) ); + + // drawsurfs + Q3_SwapBlock( (int *)q3_drawSurfaces, q3_numDrawSurfaces * sizeof( q3_drawSurfaces[0] ) ); + + // fogs + for ( i = 0 ; i < q3_numFogs ; i++ ) { + q3_dfogs[i].brushNum = LittleLong( q3_dfogs[i].brushNum ); + } +} + + + +/* +============= +Q3_CopyLump +============= +*/ +int Q3_CopyLump( q3_dheader_t *header, int lump, void **dest, int size ) { + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if ( length % size ) { + Error( "Q3_LoadBSPFile: odd lump size" ); + } + + *dest = GetMemory( length ); + + memcpy( *dest, (byte *)header + ofs, length ); + + return length / size; +} + +/* +============= +Q3_LoadBSPFile +============= +*/ +void Q3_LoadBSPFile( struct quakefile_s *qf ) { + q3_dheader_t *header; + + // load the file header + //LoadFile(filename, (void **)&header, offset, length); + // + LoadQuakeFile( qf, (void **)&header ); + + // swap the header + Q3_SwapBlock( (int *)header, sizeof( *header ) ); + + if ( header->ident != Q3_BSP_IDENT ) { + Error( "%s is not a IBSP file", qf->filename ); + } + if ( header->version != Q3_BSP_VERSION ) { + Error( "%s is version %i, not %i", qf->filename, header->version, Q3_BSP_VERSION ); + } + + q3_numShaders = Q3_CopyLump( header, Q3_LUMP_SHADERS, (void *) &q3_dshaders, sizeof( q3_dshader_t ) ); + q3_nummodels = Q3_CopyLump( header, Q3_LUMP_MODELS, (void *) &q3_dmodels, sizeof( q3_dmodel_t ) ); + q3_numplanes = Q3_CopyLump( header, Q3_LUMP_PLANES, (void *) &q3_dplanes, sizeof( q3_dplane_t ) ); + q3_numleafs = Q3_CopyLump( header, Q3_LUMP_LEAFS, (void *) &q3_dleafs, sizeof( q3_dleaf_t ) ); + q3_numnodes = Q3_CopyLump( header, Q3_LUMP_NODES, (void *) &q3_dnodes, sizeof( q3_dnode_t ) ); + q3_numleafsurfaces = Q3_CopyLump( header, Q3_LUMP_LEAFSURFACES, (void *) &q3_dleafsurfaces, sizeof( q3_dleafsurfaces[0] ) ); + q3_numleafbrushes = Q3_CopyLump( header, Q3_LUMP_LEAFBRUSHES, (void *) &q3_dleafbrushes, sizeof( q3_dleafbrushes[0] ) ); + q3_numbrushes = Q3_CopyLump( header, Q3_LUMP_BRUSHES, (void *) &q3_dbrushes, sizeof( q3_dbrush_t ) ); + q3_numbrushsides = Q3_CopyLump( header, Q3_LUMP_BRUSHSIDES, (void *) &q3_dbrushsides, sizeof( q3_dbrushside_t ) ); + q3_numDrawVerts = Q3_CopyLump( header, Q3_LUMP_DRAWVERTS, (void *) &q3_drawVerts, sizeof( q3_drawVert_t ) ); + q3_numDrawSurfaces = Q3_CopyLump( header, Q3_LUMP_SURFACES, (void *) &q3_drawSurfaces, sizeof( q3_dsurface_t ) ); + q3_numFogs = Q3_CopyLump( header, Q3_LUMP_FOGS, (void *) &q3_dfogs, sizeof( q3_dfog_t ) ); + q3_numDrawIndexes = Q3_CopyLump( header, Q3_LUMP_DRAWINDEXES, (void *) &q3_drawIndexes, sizeof( q3_drawIndexes[0] ) ); + + q3_numVisBytes = Q3_CopyLump( header, Q3_LUMP_VISIBILITY, (void *) &q3_visBytes, 1 ); + q3_numLightBytes = Q3_CopyLump( header, Q3_LUMP_LIGHTMAPS, (void *) &q3_lightBytes, 1 ); + q3_entdatasize = Q3_CopyLump( header, Q3_LUMP_ENTITIES, (void *) &q3_dentdata, 1 ); + + q3_numGridPoints = Q3_CopyLump( header, Q3_LUMP_LIGHTGRID, (void *) &q3_gridData, 8 ); + + + FreeMemory( header ); // everything has been copied out + + // swap everything + Q3_SwapBSPFile(); + + Q3_PrintBSPFileSizes(); + + Q3_FindVisibleBrushSides(); + + //Q3_PrintBSPFileSizes(); +} + + +//============================================================================ + +/* +============= +Q3_AddLump +============= +*/ +void Q3_AddLump( FILE *bspfile, q3_dheader_t *header, int lumpnum, void *data, int len ) { + q3_lump_t *lump; + + lump = &header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( bspfile ) ); + lump->filelen = LittleLong( len ); + SafeWrite( bspfile, data, ( len + 3 ) & ~3 ); +} + +/* +============= +Q3_WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void Q3_WriteBSPFile( char *filename ) { + q3_dheader_t outheader, *header; + FILE *bspfile; + + header = &outheader; + memset( header, 0, sizeof( q3_dheader_t ) ); + + Q3_SwapBSPFile(); + + header->ident = LittleLong( Q3_BSP_IDENT ); + header->version = LittleLong( Q3_BSP_VERSION ); + + bspfile = SafeOpenWrite( filename ); + SafeWrite( bspfile, header, sizeof( q3_dheader_t ) ); // overwritten later + + Q3_AddLump( bspfile, header, Q3_LUMP_SHADERS, q3_dshaders, q3_numShaders * sizeof( q3_dshader_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_PLANES, q3_dplanes, q3_numplanes * sizeof( q3_dplane_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_LEAFS, q3_dleafs, q3_numleafs * sizeof( q3_dleaf_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_NODES, q3_dnodes, q3_numnodes * sizeof( q3_dnode_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHES, q3_dbrushes, q3_numbrushes * sizeof( q3_dbrush_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_BRUSHSIDES, q3_dbrushsides, q3_numbrushsides * sizeof( q3_dbrushside_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_LEAFSURFACES, q3_dleafsurfaces, q3_numleafsurfaces * sizeof( q3_dleafsurfaces[0] ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_LEAFBRUSHES, q3_dleafbrushes, q3_numleafbrushes * sizeof( q3_dleafbrushes[0] ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_MODELS, q3_dmodels, q3_nummodels * sizeof( q3_dmodel_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_DRAWVERTS, q3_drawVerts, q3_numDrawVerts * sizeof( q3_drawVert_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_SURFACES, q3_drawSurfaces, q3_numDrawSurfaces * sizeof( q3_dsurface_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_VISIBILITY, q3_visBytes, q3_numVisBytes ); + Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTMAPS, q3_lightBytes, q3_numLightBytes ); + Q3_AddLump( bspfile, header, Q3_LUMP_LIGHTGRID, q3_gridData, 8 * q3_numGridPoints ); + Q3_AddLump( bspfile, header, Q3_LUMP_ENTITIES, q3_dentdata, q3_entdatasize ); + Q3_AddLump( bspfile, header, Q3_LUMP_FOGS, q3_dfogs, q3_numFogs * sizeof( q3_dfog_t ) ); + Q3_AddLump( bspfile, header, Q3_LUMP_DRAWINDEXES, q3_drawIndexes, q3_numDrawIndexes * sizeof( q3_drawIndexes[0] ) ); + + fseek( bspfile, 0, SEEK_SET ); + SafeWrite( bspfile, header, sizeof( q3_dheader_t ) ); + fclose( bspfile ); +} + +//============================================================================ + +/* +============= +Q3_PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void Q3_PrintBSPFileSizes( void ) { + if ( !num_entities ) { + Q3_ParseEntities(); + } + + Log_Print( "%6i models %7i\n" + ,q3_nummodels, (int)( q3_nummodels * sizeof( q3_dmodel_t ) ) ); + Log_Print( "%6i shaders %7i\n" + ,q3_numShaders, (int)( q3_numShaders * sizeof( q3_dshader_t ) ) ); + Log_Print( "%6i brushes %7i\n" + ,q3_numbrushes, (int)( q3_numbrushes * sizeof( q3_dbrush_t ) ) ); + Log_Print( "%6i brushsides %7i\n" + ,q3_numbrushsides, (int)( q3_numbrushsides * sizeof( q3_dbrushside_t ) ) ); + Log_Print( "%6i fogs %7i\n" + ,q3_numFogs, (int)( q3_numFogs * sizeof( q3_dfog_t ) ) ); + Log_Print( "%6i planes %7i\n" + ,q3_numplanes, (int)( q3_numplanes * sizeof( q3_dplane_t ) ) ); + Log_Print( "%6i entdata %7i\n", num_entities, q3_entdatasize ); + + Log_Print( "\n" ); + + Log_Print( "%6i nodes %7i\n" + ,q3_numnodes, (int)( q3_numnodes * sizeof( q3_dnode_t ) ) ); + Log_Print( "%6i leafs %7i\n" + ,q3_numleafs, (int)( q3_numleafs * sizeof( q3_dleaf_t ) ) ); + Log_Print( "%6i leafsurfaces %7i\n" + ,q3_numleafsurfaces, (int)( q3_numleafsurfaces * sizeof( q3_dleafsurfaces[0] ) ) ); + Log_Print( "%6i leafbrushes %7i\n" + ,q3_numleafbrushes, (int)( q3_numleafbrushes * sizeof( q3_dleafbrushes[0] ) ) ); + Log_Print( "%6i drawverts %7i\n" + ,q3_numDrawVerts, (int)( q3_numDrawVerts * sizeof( q3_drawVerts[0] ) ) ); + Log_Print( "%6i drawindexes %7i\n" + ,q3_numDrawIndexes, (int)( q3_numDrawIndexes * sizeof( q3_drawIndexes[0] ) ) ); + Log_Print( "%6i drawsurfaces %7i\n" + ,q3_numDrawSurfaces, (int)( q3_numDrawSurfaces * sizeof( q3_drawSurfaces[0] ) ) ); + + Log_Print( "%6i lightmaps %7i\n" + ,q3_numLightBytes / ( LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3 ), q3_numLightBytes ); + Log_Print( " visibility %7i\n" + , q3_numVisBytes ); +} + +/* +================ +Q3_ParseEntities + +Parses the q3_dentdata string into entities +================ +*/ +void Q3_ParseEntities( void ) { + script_t *script; + + num_entities = 0; + script = LoadScriptMemory( q3_dentdata, q3_entdatasize, "*Quake3 bsp file" ); + SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | + SCFL_NOSTRINGESCAPECHARS ); + + while ( ParseEntity( script ) ) + { + } //end while + + FreeScript( script ); +} //end of the function Q3_ParseEntities + + +/* +================ +Q3_UnparseEntities + +Generates the q3_dentdata string from all the entities +================ +*/ +void Q3_UnparseEntities( void ) { + char *buf, *end; + epair_t *ep; + char line[2048]; + int i; + + buf = q3_dentdata; + end = buf; + *end = 0; + + for ( i = 0 ; i < num_entities ; i++ ) + { + ep = entities[i].epairs; + if ( !ep ) { + continue; // ent got removed + + } + strcat( end,"{\n" ); + end += 2; + + for ( ep = entities[i].epairs ; ep ; ep = ep->next ) + { + sprintf( line, "\"%s\" \"%s\"\n", ep->key, ep->value ); + strcat( end, line ); + end += strlen( line ); + } + strcat( end,"}\n" ); + end += 2; + + if ( end > buf + Q3_MAX_MAP_ENTSTRING ) { + Error( "Entity text too long" ); + } + } + q3_entdatasize = end - buf + 1; +} //end of the function Q3_UnparseEntities + diff --git a/src/bspc/l_bsp_q3.h b/src/bspc/l_bsp_q3.h new file mode 100644 index 0000000..146e7ea --- /dev/null +++ b/src/bspc/l_bsp_q3.h @@ -0,0 +1,88 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "q3files.h" +//#include "surfaceflags.h" + +extern int q3_nummodels; +extern q3_dmodel_t *q3_dmodels; //[MAX_MAP_MODELS]; + +extern int q3_numShaders; +extern q3_dshader_t *q3_dshaders; //[Q3_MAX_MAP_SHADERS]; + +extern int q3_entdatasize; +extern char *q3_dentdata; //[Q3_MAX_MAP_ENTSTRING]; + +extern int q3_numleafs; +extern q3_dleaf_t *q3_dleafs; //[Q3_MAX_MAP_LEAFS]; + +extern int q3_numplanes; +extern q3_dplane_t *q3_dplanes; //[Q3_MAX_MAP_PLANES]; + +extern int q3_numnodes; +extern q3_dnode_t *q3_dnodes; //[Q3_MAX_MAP_NODES]; + +extern int q3_numleafsurfaces; +extern int *q3_dleafsurfaces; //[Q3_MAX_MAP_LEAFFACES]; + +extern int q3_numleafbrushes; +extern int *q3_dleafbrushes; //[Q3_MAX_MAP_LEAFBRUSHES]; + +extern int q3_numbrushes; +extern q3_dbrush_t *q3_dbrushes; //[Q3_MAX_MAP_BRUSHES]; + +extern int q3_numbrushsides; +extern q3_dbrushside_t *q3_dbrushsides; //[Q3_MAX_MAP_BRUSHSIDES]; + +extern int q3_numLightBytes; +extern byte *q3_lightBytes; //[Q3_MAX_MAP_LIGHTING]; + +extern int q3_numGridPoints; +extern byte *q3_gridData; //[Q3_MAX_MAP_LIGHTGRID]; + +extern int q3_numVisBytes; +extern byte *q3_visBytes; //[Q3_MAX_MAP_VISIBILITY]; + +extern int q3_numDrawVerts; +extern q3_drawVert_t *q3_drawVerts; //[Q3_MAX_MAP_DRAW_VERTS]; + +extern int q3_numDrawIndexes; +extern int *q3_drawIndexes; //[Q3_MAX_MAP_DRAW_INDEXES]; + +extern int q3_numDrawSurfaces; +extern q3_dsurface_t *q3_drawSurfaces; //[Q3_MAX_MAP_DRAW_SURFS]; + +extern int q3_numFogs; +extern q3_dfog_t *q3_dfogs; //[Q3_MAX_MAP_FOGS]; + +extern char q3_dbrushsidetextured[Q3_MAX_MAP_BRUSHSIDES]; + +void Q3_LoadBSPFile( struct quakefile_s *qf ); +void Q3_FreeMaxBSP( void ); +void Q3_ParseEntities( void ); diff --git a/src/bspc/l_bsp_sin.c b/src/bspc/l_bsp_sin.c new file mode 100644 index 0000000..b407428 --- /dev/null +++ b/src/bspc/l_bsp_sin.c @@ -0,0 +1,1186 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "l_cmd.h" +#include "l_math.h" +#include "l_mem.h" +#include "l_log.h" +#include "l_poly.h" +#include "../botlib/l_script.h" +#include "l_bsp_ent.h" +#include "l_bsp_sin.h" + +void GetLeafNums( void ); + +//============================================================================= + +int sin_nummodels; +sin_dmodel_t *sin_dmodels; //[SIN_MAX_MAP_MODELS]; + +int sin_visdatasize; +byte *sin_dvisdata; //[SIN_MAX_MAP_VISIBILITY]; +sin_dvis_t *sin_dvis; // = (sin_dvis_t *)sin_sin_dvisdata; + +int sin_lightdatasize; +byte *sin_dlightdata; //[SIN_MAX_MAP_LIGHTING]; + +int sin_entdatasize; +char *sin_dentdata; //[SIN_MAX_MAP_ENTSTRING]; + +int sin_numleafs; +sin_dleaf_t *sin_dleafs; //[SIN_MAX_MAP_LEAFS]; + +int sin_numplanes; +sin_dplane_t *sin_dplanes; //[SIN_MAX_MAP_PLANES]; + +int sin_numvertexes; +sin_dvertex_t *sin_dvertexes; //[SIN_MAX_MAP_VERTS]; + +int sin_numnodes; +sin_dnode_t *sin_dnodes; //[SIN_MAX_MAP_NODES]; + +int sin_numtexinfo; +sin_texinfo_t *sin_texinfo; //[SIN_MAX_MAP_sin_texinfo]; + +int sin_numfaces; +sin_dface_t *sin_dfaces; //[SIN_MAX_MAP_FACES]; + +int sin_numedges; +sin_dedge_t *sin_dedges; //[SIN_MAX_MAP_EDGES]; + +int sin_numleaffaces; +unsigned short *sin_dleaffaces; //[SIN_MAX_MAP_LEAFFACES]; + +int sin_numleafbrushes; +unsigned short *sin_dleafbrushes; //[SIN_MAX_MAP_LEAFBRUSHES]; + +int sin_numsurfedges; +int *sin_dsurfedges; //[SIN_MAX_MAP_SURFEDGES]; + +int sin_numbrushes; +sin_dbrush_t *sin_dbrushes; //[SIN_MAX_MAP_BRUSHES]; + +int sin_numbrushsides; +sin_dbrushside_t *sin_dbrushsides; //[SIN_MAX_MAP_BRUSHSIDES]; + +int sin_numareas; +sin_darea_t *sin_dareas; //[SIN_MAX_MAP_AREAS]; + +int sin_numareaportals; +sin_dareaportal_t *sin_dareaportals; //[SIN_MAX_MAP_AREAPORTALS]; + +int sin_numlightinfo; +sin_lightvalue_t *sin_lightinfo; //[SIN_MAX_MAP_LIGHTINFO]; + +byte sin_dpop[256]; + +char sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES]; + +int sin_bspallocated = false; +int sin_allocatedbspmem = 0; + +void Sin_AllocMaxBSP( void ) { + //models + sin_nummodels = 0; + sin_dmodels = (sin_dmodel_t *) GetClearedMemory( SIN_MAX_MAP_MODELS * sizeof( sin_dmodel_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_MODELS * sizeof( sin_dmodel_t ); + //vis data + sin_visdatasize = 0; + sin_dvisdata = (byte *) GetClearedMemory( SIN_MAX_MAP_VISIBILITY * sizeof( byte ) ); + sin_dvis = (sin_dvis_t *) sin_dvisdata; + sin_allocatedbspmem += SIN_MAX_MAP_VISIBILITY * sizeof( byte ); + //light data + sin_lightdatasize = 0; + sin_dlightdata = (byte *) GetClearedMemory( SIN_MAX_MAP_LIGHTING * sizeof( byte ) ); + sin_allocatedbspmem += SIN_MAX_MAP_LIGHTING * sizeof( byte ); + //entity data + sin_entdatasize = 0; + sin_dentdata = (char *) GetClearedMemory( SIN_MAX_MAP_ENTSTRING * sizeof( char ) ); + sin_allocatedbspmem += SIN_MAX_MAP_ENTSTRING * sizeof( char ); + //leafs + sin_numleafs = 0; + sin_dleafs = (sin_dleaf_t *) GetClearedMemory( SIN_MAX_MAP_LEAFS * sizeof( sin_dleaf_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_LEAFS * sizeof( sin_dleaf_t ); + //planes + sin_numplanes = 0; + sin_dplanes = (sin_dplane_t *) GetClearedMemory( SIN_MAX_MAP_PLANES * sizeof( sin_dplane_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_PLANES * sizeof( sin_dplane_t ); + //vertexes + sin_numvertexes = 0; + sin_dvertexes = (sin_dvertex_t *) GetClearedMemory( SIN_MAX_MAP_VERTS * sizeof( sin_dvertex_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_VERTS * sizeof( sin_dvertex_t ); + //nodes + sin_numnodes = 0; + sin_dnodes = (sin_dnode_t *) GetClearedMemory( SIN_MAX_MAP_NODES * sizeof( sin_dnode_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_NODES * sizeof( sin_dnode_t ); + //texture info + sin_numtexinfo = 0; + sin_texinfo = (sin_texinfo_t *) GetClearedMemory( SIN_MAX_MAP_TEXINFO * sizeof( sin_texinfo_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_TEXINFO * sizeof( sin_texinfo_t ); + //faces + sin_numfaces = 0; + sin_dfaces = (sin_dface_t *) GetClearedMemory( SIN_MAX_MAP_FACES * sizeof( sin_dface_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_FACES * sizeof( sin_dface_t ); + //edges + sin_numedges = 0; + sin_dedges = (sin_dedge_t *) GetClearedMemory( SIN_MAX_MAP_EDGES * sizeof( sin_dedge_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_EDGES * sizeof( sin_dedge_t ); + //leaf faces + sin_numleaffaces = 0; + sin_dleaffaces = (unsigned short *) GetClearedMemory( SIN_MAX_MAP_LEAFFACES * sizeof( unsigned short ) ); + sin_allocatedbspmem += SIN_MAX_MAP_LEAFFACES * sizeof( unsigned short ); + //leaf brushes + sin_numleafbrushes = 0; + sin_dleafbrushes = (unsigned short *) GetClearedMemory( SIN_MAX_MAP_LEAFBRUSHES * sizeof( unsigned short ) ); + sin_allocatedbspmem += SIN_MAX_MAP_LEAFBRUSHES * sizeof( unsigned short ); + //surface edges + sin_numsurfedges = 0; + sin_dsurfedges = (int *) GetClearedMemory( SIN_MAX_MAP_SURFEDGES * sizeof( int ) ); + sin_allocatedbspmem += SIN_MAX_MAP_SURFEDGES * sizeof( int ); + //brushes + sin_numbrushes = 0; + sin_dbrushes = (sin_dbrush_t *) GetClearedMemory( SIN_MAX_MAP_BRUSHES * sizeof( sin_dbrush_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_BRUSHES * sizeof( sin_dbrush_t ); + //brushsides + sin_numbrushsides = 0; + sin_dbrushsides = (sin_dbrushside_t *) GetClearedMemory( SIN_MAX_MAP_BRUSHSIDES * sizeof( sin_dbrushside_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_BRUSHSIDES * sizeof( sin_dbrushside_t ); + //areas + sin_numareas = 0; + sin_dareas = (sin_darea_t *) GetClearedMemory( SIN_MAX_MAP_AREAS * sizeof( sin_darea_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_AREAS * sizeof( sin_darea_t ); + //area portals + sin_numareaportals = 0; + sin_dareaportals = (sin_dareaportal_t *) GetClearedMemory( SIN_MAX_MAP_AREAPORTALS * sizeof( sin_dareaportal_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_AREAPORTALS * sizeof( sin_dareaportal_t ); + //light info + sin_numlightinfo = 0; + sin_lightinfo = (sin_lightvalue_t *) GetClearedMemory( SIN_MAX_MAP_LIGHTINFO * sizeof( sin_lightvalue_t ) ); + sin_allocatedbspmem += SIN_MAX_MAP_LIGHTINFO * sizeof( sin_lightvalue_t ); + //print allocated memory + Log_Print( "allocated " ); + PrintMemorySize( sin_allocatedbspmem ); + Log_Print( " of BSP memory\n" ); +} //end of the function Sin_AllocMaxBSP + +void Sin_FreeMaxBSP( void ) { + //models + sin_nummodels = 0; + FreeMemory( sin_dmodels ); + sin_dmodels = NULL; + //vis data + sin_visdatasize = 0; + FreeMemory( sin_dvisdata ); + sin_dvisdata = NULL; + sin_dvis = NULL; + //light data + sin_lightdatasize = 0; + FreeMemory( sin_dlightdata ); + sin_dlightdata = NULL; + //entity data + sin_entdatasize = 0; + FreeMemory( sin_dentdata ); + sin_dentdata = NULL; + //leafs + sin_numleafs = 0; + FreeMemory( sin_dleafs ); + sin_dleafs = NULL; + //planes + sin_numplanes = 0; + FreeMemory( sin_dplanes ); + sin_dplanes = NULL; + //vertexes + sin_numvertexes = 0; + FreeMemory( sin_dvertexes ); + sin_dvertexes = NULL; + //nodes + sin_numnodes = 0; + FreeMemory( sin_dnodes ); + sin_dnodes = NULL; + //texture info + sin_numtexinfo = 0; + FreeMemory( sin_texinfo ); + sin_texinfo = NULL; + //faces + sin_numfaces = 0; + FreeMemory( sin_dfaces ); + sin_dfaces = NULL; + //edges + sin_numedges = 0; + FreeMemory( sin_dedges ); + sin_dedges = NULL; + //leaf faces + sin_numleaffaces = 0; + FreeMemory( sin_dleaffaces ); + sin_dleaffaces = NULL; + //leaf brushes + sin_numleafbrushes = 0; + FreeMemory( sin_dleafbrushes ); + sin_dleafbrushes = NULL; + //surface edges + sin_numsurfedges = 0; + FreeMemory( sin_dsurfedges ); + sin_dsurfedges = NULL; + //brushes + sin_numbrushes = 0; + FreeMemory( sin_dbrushes ); + sin_dbrushes = NULL; + //brushsides + sin_numbrushsides = 0; + FreeMemory( sin_dbrushsides ); + sin_dbrushsides = NULL; + //areas + sin_numareas = 0; + FreeMemory( sin_dareas ); + sin_dareas = NULL; + //area portals + sin_numareaportals = 0; + FreeMemory( sin_dareaportals ); + sin_dareaportals = NULL; + //light info + sin_numlightinfo = 0; + FreeMemory( sin_lightinfo ); + sin_lightinfo = NULL; + // + Log_Print( "freed " ); + PrintMemorySize( sin_allocatedbspmem ); + Log_Print( " of BSP memory\n" ); + sin_allocatedbspmem = 0; +} //end of the function Sin_FreeMaxBSP + +#define WCONVEX_EPSILON 0.5 + +//=========================================================================== +// returns the amount the face and the winding overlap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Sin_FaceOnWinding( sin_dface_t *face, winding_t *winding ) { + int i, edgenum, side; + float dist, area; + sin_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + winding_t *w; + + // + w = CopyWinding( winding ); + memcpy( &plane, &sin_dplanes[face->planenum], sizeof( sin_dplane_t ) ); + //check on which side of the plane the face is + if ( face->side ) { + VectorNegate( plane.normal, plane.normal ); + plane.dist = -plane.dist; + } //end if + for ( i = 0; i < face->numedges && w; i++ ) + { + //get the first and second vertex of the edge + edgenum = sin_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = sin_dvertexes[sin_dedges[abs( edgenum )].v[side]].point; + v2 = sin_dvertexes[sin_dedges[abs( edgenum )].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract( v1, v2, edgevec ); + CrossProduct( edgevec, plane.normal, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + ChopWindingInPlace( &w, normal, dist, 0.9 ); //CLIP_EPSILON + } //end for + if ( w ) { + area = WindingArea( w ); + FreeWinding( w ); + return area; + } //end if + return 0; +} //end of the function Sin_FaceOnWinding +//=========================================================================== +// creates a winding for the given brush side on the given brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +winding_t *Sin_BrushSideWinding( sin_dbrush_t *brush, sin_dbrushside_t *baseside ) { + int i; + sin_dplane_t *baseplane, *plane; + sin_dbrushside_t *side; + winding_t *w; + + //create a winding for the brush side with the given planenumber + baseplane = &sin_dplanes[baseside->planenum]; + w = BaseWindingForPlane( baseplane->normal, baseplane->dist ); + for ( i = 0; i < brush->numsides && w; i++ ) + { + side = &sin_dbrushsides[brush->firstside + i]; + //don't chop with the base plane + if ( side->planenum == baseside->planenum ) { + continue; + } + //also don't use planes that are almost equal + plane = &sin_dplanes[side->planenum]; + if ( DotProduct( baseplane->normal, plane->normal ) > 0.999 + && fabs( baseplane->dist - plane->dist ) < 0.01 ) { + continue; + } + // + plane = &sin_dplanes[side->planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } //end for + return w; +} //end of the function Sin_BrushSideWinding +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Sin_HintSkipBrush( sin_dbrush_t *brush ) { + int j; + sin_dbrushside_t *brushside; + + for ( j = 0; j < brush->numsides; j++ ) + { + brushside = &sin_dbrushsides[brush->firstside + j]; + if ( brushside->texinfo > 0 ) { + if ( sin_texinfo[brushside->texinfo].flags & ( SURF_SKIP | SURF_HINT ) ) { + return true; + } //end if + } //end if + } //end for + return false; +} //end of the function Sin_HintSkipBrush +//=========================================================================== +// fix screwed brush texture references +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsTiny( winding_t *w ); + +void Sin_FixTextureReferences( void ) { + int i, j, k, we; + sin_dbrushside_t *brushside; + sin_dbrush_t *brush; + sin_dface_t *face; + winding_t *w; + + memset( sin_dbrushsidetextured, false, SIN_MAX_MAP_BRUSHSIDES ); + //go over all the brushes + for ( i = 0; i < sin_numbrushes; i++ ) + { + brush = &sin_dbrushes[i]; + //hint brushes are not textured + if ( Sin_HintSkipBrush( brush ) ) { + continue; + } + //go over all the sides of the brush + for ( j = 0; j < brush->numsides; j++ ) + { + brushside = &sin_dbrushsides[brush->firstside + j]; + // + w = Sin_BrushSideWinding( brush, brushside ); + if ( !w ) { + sin_dbrushsidetextured[brush->firstside + j] = true; + continue; + } //end if + else + { + //RemoveEqualPoints(w, 0.2); + if ( WindingIsTiny( w ) ) { + FreeWinding( w ); + sin_dbrushsidetextured[brush->firstside + j] = true; + continue; + } //end if + else + { + we = WindingError( w ); + if ( we == WE_NOTENOUGHPOINTS + || we == WE_SMALLAREA + || we == WE_POINTBOGUSRANGE +// || we == WE_NONCONVEX + ) { + FreeWinding( w ); + sin_dbrushsidetextured[brush->firstside + j] = true; + continue; + } //end if + } //end else + } //end else + if ( WindingArea( w ) < 20 ) { + sin_dbrushsidetextured[brush->firstside + j] = true; + } //end if + //find a face for texturing this brush + for ( k = 0; k < sin_numfaces; k++ ) + { + face = &sin_dfaces[k]; + //if the face is in the same plane as the brush side + if ( ( face->planenum & ~1 ) != ( brushside->planenum & ~1 ) ) { + continue; + } + //if the face is partly or totally on the brush side + if ( Sin_FaceOnWinding( face, w ) ) { + brushside->texinfo = face->texinfo; + sin_dbrushsidetextured[brush->firstside + j] = true; + break; + } //end if + } //end for + FreeWinding( w ); + } //end for + } //end for +} //end of the function Sin_FixTextureReferences*/ + +/* +=============== +CompressVis + +=============== +*/ +int Sin_CompressVis( byte *vis, byte *dest ) { + int j; + int rep; + int visrow; + byte *dest_p; + + dest_p = dest; +// visrow = (r_numvisleafs + 7)>>3; + visrow = ( sin_dvis->numclusters + 7 ) >> 3; + + for ( j = 0 ; j < visrow ; j++ ) + { + *dest_p++ = vis[j]; + if ( vis[j] ) { + continue; + } + + rep = 1; + for ( j++; j < visrow ; j++ ) + if ( vis[j] || rep == 255 ) { + break; + } else { + rep++; + } + *dest_p++ = rep; + j--; + } + + return dest_p - dest; +} //end of the function Sin_CompressVis + + +/* +=================== +DecompressVis +=================== +*/ +void Sin_DecompressVis( byte *in, byte *decompressed ) { + int c; + byte *out; + int row; + +// row = (r_numvisleafs+7)>>3; + row = ( sin_dvis->numclusters + 7 ) >> 3; + out = decompressed; + + do + { + if ( *in ) { + *out++ = *in++; + continue; + } + + c = in[1]; + if ( !c ) { + Error( "DecompressVis: 0 repeat" ); + } + in += 2; + while ( c ) + { + *out++ = 0; + c--; + } + } while ( out - decompressed < row ); +} //end of the function Sin_DecompressVis + +//============================================================================= + +/* +============= +Sin_SwapBSPFile + +Byte swaps all data in a bsp file. +============= +*/ +void Sin_SwapBSPFile( qboolean todisk ) { + int i, j; + sin_dmodel_t *d; + + +// models + for ( i = 0 ; i < sin_nummodels ; i++ ) + { + d = &sin_dmodels[i]; + + d->firstface = LittleLong( d->firstface ); + d->numfaces = LittleLong( d->numfaces ); + d->headnode = LittleLong( d->headnode ); + + for ( j = 0 ; j < 3 ; j++ ) + { + d->mins[j] = LittleFloat( d->mins[j] ); + d->maxs[j] = LittleFloat( d->maxs[j] ); + d->origin[j] = LittleFloat( d->origin[j] ); + } + } + +// +// vertexes +// + for ( i = 0 ; i < sin_numvertexes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + sin_dvertexes[i].point[j] = LittleFloat( sin_dvertexes[i].point[j] ); + } + +// +// planes +// + for ( i = 0 ; i < sin_numplanes ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + sin_dplanes[i].normal[j] = LittleFloat( sin_dplanes[i].normal[j] ); + sin_dplanes[i].dist = LittleFloat( sin_dplanes[i].dist ); + sin_dplanes[i].type = LittleLong( sin_dplanes[i].type ); + } + +// +// sin_texinfos +// + for ( i = 0; i < sin_numtexinfo; i++ ) + { + for ( j = 0 ; j < 8 ; j++ ) + sin_texinfo[i].vecs[0][j] = LittleFloat( sin_texinfo[i].vecs[0][j] ); +#ifdef SIN + sin_texinfo[i].trans_mag = LittleFloat( sin_texinfo[i].trans_mag ); + sin_texinfo[i].trans_angle = LittleLong( sin_texinfo[i].trans_angle ); + sin_texinfo[i].animtime = LittleFloat( sin_texinfo[i].animtime ); + sin_texinfo[i].nonlit = LittleFloat( sin_texinfo[i].nonlit ); + sin_texinfo[i].translucence = LittleFloat( sin_texinfo[i].translucence ); + sin_texinfo[i].friction = LittleFloat( sin_texinfo[i].friction ); + sin_texinfo[i].restitution = LittleFloat( sin_texinfo[i].restitution ); + sin_texinfo[i].flags = LittleUnsigned( sin_texinfo[i].flags ); +#else + sin_texinfo[i].value = LittleLong( sin_texinfo[i].value ); + sin_texinfo[i].flags = LittleLong( sin_texinfo[i].flags ); +#endif + sin_texinfo[i].nexttexinfo = LittleLong( sin_texinfo[i].nexttexinfo ); + } + +#ifdef SIN +// +// lightinfos +// + for ( i = 0; i < sin_numlightinfo; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + { + sin_lightinfo[i].color[j] = LittleFloat( sin_lightinfo[i].color[j] ); + } + sin_lightinfo[i].value = LittleLong( sin_lightinfo[i].value ); + sin_lightinfo[i].direct = LittleFloat( sin_lightinfo[i].direct ); + sin_lightinfo[i].directangle = LittleFloat( sin_lightinfo[i].directangle ); + sin_lightinfo[i].directstyle = LittleFloat( sin_lightinfo[i].directstyle ); + } +#endif + +// +// faces +// + for ( i = 0 ; i < sin_numfaces ; i++ ) + { + sin_dfaces[i].texinfo = LittleShort( sin_dfaces[i].texinfo ); +#ifdef SIN + sin_dfaces[i].lightinfo = LittleLong( sin_dfaces[i].lightinfo ); + sin_dfaces[i].planenum = LittleUnsignedShort( sin_dfaces[i].planenum ); +#else + sin_dfaces[i].planenum = LittleShort( sin_dfaces[i].planenum ); +#endif + sin_dfaces[i].side = LittleShort( sin_dfaces[i].side ); + sin_dfaces[i].lightofs = LittleLong( sin_dfaces[i].lightofs ); + sin_dfaces[i].firstedge = LittleLong( sin_dfaces[i].firstedge ); + sin_dfaces[i].numedges = LittleShort( sin_dfaces[i].numedges ); + } + +// +// nodes +// + for ( i = 0 ; i < sin_numnodes ; i++ ) + { + sin_dnodes[i].planenum = LittleLong( sin_dnodes[i].planenum ); + for ( j = 0 ; j < 3 ; j++ ) + { + sin_dnodes[i].mins[j] = LittleShort( sin_dnodes[i].mins[j] ); + sin_dnodes[i].maxs[j] = LittleShort( sin_dnodes[i].maxs[j] ); + } + sin_dnodes[i].children[0] = LittleLong( sin_dnodes[i].children[0] ); + sin_dnodes[i].children[1] = LittleLong( sin_dnodes[i].children[1] ); +#ifdef SIN + sin_dnodes[i].firstface = LittleUnsignedShort( sin_dnodes[i].firstface ); + sin_dnodes[i].numfaces = LittleUnsignedShort( sin_dnodes[i].numfaces ); +#else + sin_dnodes[i].firstface = LittleShort( sin_dnodes[i].firstface ); + sin_dnodes[i].numfaces = LittleShort( sin_dnodes[i].numfaces ); +#endif + } + +// +// leafs +// + for ( i = 0 ; i < sin_numleafs ; i++ ) + { + sin_dleafs[i].contents = LittleLong( sin_dleafs[i].contents ); + sin_dleafs[i].cluster = LittleShort( sin_dleafs[i].cluster ); + sin_dleafs[i].area = LittleShort( sin_dleafs[i].area ); + for ( j = 0 ; j < 3 ; j++ ) + { + sin_dleafs[i].mins[j] = LittleShort( sin_dleafs[i].mins[j] ); + sin_dleafs[i].maxs[j] = LittleShort( sin_dleafs[i].maxs[j] ); + } +#ifdef SIN + sin_dleafs[i].firstleafface = LittleUnsignedShort( sin_dleafs[i].firstleafface ); + sin_dleafs[i].numleaffaces = LittleUnsignedShort( sin_dleafs[i].numleaffaces ); + sin_dleafs[i].firstleafbrush = LittleUnsignedShort( sin_dleafs[i].firstleafbrush ); + sin_dleafs[i].numleafbrushes = LittleUnsignedShort( sin_dleafs[i].numleafbrushes ); +#else + sin_dleafs[i].firstleafface = LittleShort( sin_dleafs[i].firstleafface ); + sin_dleafs[i].numleaffaces = LittleShort( sin_dleafs[i].numleaffaces ); + sin_dleafs[i].firstleafbrush = LittleShort( sin_dleafs[i].firstleafbrush ); + sin_dleafs[i].numleafbrushes = LittleShort( sin_dleafs[i].numleafbrushes ); +#endif + } + +// +// leaffaces +// + for ( i = 0 ; i < sin_numleaffaces ; i++ ) + sin_dleaffaces[i] = LittleShort( sin_dleaffaces[i] ); + +// +// leafbrushes +// + for ( i = 0 ; i < sin_numleafbrushes ; i++ ) + sin_dleafbrushes[i] = LittleShort( sin_dleafbrushes[i] ); + +// +// surfedges +// + for ( i = 0 ; i < sin_numsurfedges ; i++ ) + sin_dsurfedges[i] = LittleLong( sin_dsurfedges[i] ); + +// +// edges +// + for ( i = 0 ; i < sin_numedges ; i++ ) + { +#ifdef SIN + sin_dedges[i].v[0] = LittleUnsignedShort( sin_dedges[i].v[0] ); + sin_dedges[i].v[1] = LittleUnsignedShort( sin_dedges[i].v[1] ); +#else + sin_dedges[i].v[0] = LittleShort( sin_dedges[i].v[0] ); + sin_dedges[i].v[1] = LittleShort( sin_dedges[i].v[1] ); +#endif + } + +// +// brushes +// + for ( i = 0 ; i < sin_numbrushes ; i++ ) + { + sin_dbrushes[i].firstside = LittleLong( sin_dbrushes[i].firstside ); + sin_dbrushes[i].numsides = LittleLong( sin_dbrushes[i].numsides ); + sin_dbrushes[i].contents = LittleLong( sin_dbrushes[i].contents ); + } + +// +// areas +// + for ( i = 0 ; i < sin_numareas ; i++ ) + { + sin_dareas[i].numareaportals = LittleLong( sin_dareas[i].numareaportals ); + sin_dareas[i].firstareaportal = LittleLong( sin_dareas[i].firstareaportal ); + } + +// +// areasportals +// + for ( i = 0 ; i < sin_numareaportals ; i++ ) + { + sin_dareaportals[i].portalnum = LittleLong( sin_dareaportals[i].portalnum ); + sin_dareaportals[i].otherarea = LittleLong( sin_dareaportals[i].otherarea ); + } + +// +// brushsides +// + for ( i = 0 ; i < sin_numbrushsides ; i++ ) + { +#ifdef SIN + sin_dbrushsides[i].planenum = LittleUnsignedShort( sin_dbrushsides[i].planenum ); +#else + sin_dbrushsides[i].planenum = LittleShort( sin_dbrushsides[i].planenum ); +#endif + sin_dbrushsides[i].texinfo = LittleShort( sin_dbrushsides[i].texinfo ); +#ifdef SIN + sin_dbrushsides[i].lightinfo = LittleLong( sin_dbrushsides[i].lightinfo ); +#endif + } + +// +// visibility +// + if ( todisk ) { + j = sin_dvis->numclusters; + } else { + j = LittleLong( sin_dvis->numclusters ); + } + sin_dvis->numclusters = LittleLong( sin_dvis->numclusters ); + for ( i = 0 ; i < j ; i++ ) + { + sin_dvis->bitofs[i][0] = LittleLong( sin_dvis->bitofs[i][0] ); + sin_dvis->bitofs[i][1] = LittleLong( sin_dvis->bitofs[i][1] ); + } +} //end of the function Sin_SwapBSPFile + + +sin_dheader_t *header; +#ifdef SIN +int Sin_CopyLump( int lump, void *dest, int size, int maxsize ) { + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if ( length % size ) { + Error( "Sin_LoadBSPFile: odd lump size" ); + } + + if ( ( length / size ) > maxsize ) { + Error( "Sin_LoadBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lump, ( length / size ), maxsize ); + } + + memcpy( dest, (byte *)header + ofs, length ); + + return length / size; +} +#else +int Sin_CopyLump( int lump, void *dest, int size ) { + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if ( length % size ) { + Error( "Sin_LoadBSPFile: odd lump size" ); + } + + memcpy( dest, (byte *)header + ofs, length ); + + return length / size; +} +#endif + +/* +============= +Sin_LoadBSPFile +============= +*/ +void Sin_LoadBSPFile( char *filename, int offset, int length ) { + int i; + +// +// load the file header +// + LoadFile( filename, (void **)&header, offset, length ); + +// swap the header + for ( i = 0 ; i < sizeof( sin_dheader_t ) / 4 ; i++ ) + ( (int *)header )[i] = LittleLong( ( (int *)header )[i] ); + + if ( header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER ) { + Error( "%s is not a IBSP file", filename ); + } + if ( header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION ) { + Error( "%s is version %i, not %i", filename, header->version, SIN_BSPVERSION ); + } + +#ifdef SIN + sin_nummodels = Sin_CopyLump( SIN_LUMP_MODELS, sin_dmodels, sizeof( sin_dmodel_t ), SIN_MAX_MAP_MODELS ); + sin_numvertexes = Sin_CopyLump( SIN_LUMP_VERTEXES, sin_dvertexes, sizeof( sin_dvertex_t ), SIN_MAX_MAP_VERTS ); + sin_numplanes = Sin_CopyLump( SIN_LUMP_PLANES, sin_dplanes, sizeof( sin_dplane_t ), SIN_MAX_MAP_PLANES ); + sin_numleafs = Sin_CopyLump( SIN_LUMP_LEAFS, sin_dleafs, sizeof( sin_dleaf_t ), SIN_MAX_MAP_LEAFS ); + sin_numnodes = Sin_CopyLump( SIN_LUMP_NODES, sin_dnodes, sizeof( sin_dnode_t ), SIN_MAX_MAP_NODES ); + sin_numtexinfo = Sin_CopyLump( SIN_LUMP_TEXINFO, sin_texinfo, sizeof( sin_texinfo_t ), SIN_MAX_MAP_TEXINFO ); + sin_numfaces = Sin_CopyLump( SIN_LUMP_FACES, sin_dfaces, sizeof( sin_dface_t ), SIN_MAX_MAP_FACES ); + sin_numleaffaces = Sin_CopyLump( SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof( sin_dleaffaces[0] ), SIN_MAX_MAP_LEAFFACES ); + sin_numleafbrushes = Sin_CopyLump( SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof( sin_dleafbrushes[0] ), SIN_MAX_MAP_LEAFBRUSHES ); + sin_numsurfedges = Sin_CopyLump( SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof( sin_dsurfedges[0] ), SIN_MAX_MAP_SURFEDGES ); + sin_numedges = Sin_CopyLump( SIN_LUMP_EDGES, sin_dedges, sizeof( sin_dedge_t ), SIN_MAX_MAP_EDGES ); + sin_numbrushes = Sin_CopyLump( SIN_LUMP_BRUSHES, sin_dbrushes, sizeof( sin_dbrush_t ), SIN_MAX_MAP_BRUSHES ); + sin_numbrushsides = Sin_CopyLump( SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof( sin_dbrushside_t ), SIN_MAX_MAP_BRUSHSIDES ); + sin_numareas = Sin_CopyLump( SIN_LUMP_AREAS, sin_dareas, sizeof( sin_darea_t ), SIN_MAX_MAP_AREAS ); + sin_numareaportals = Sin_CopyLump( SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof( sin_dareaportal_t ), SIN_MAX_MAP_AREAPORTALS ); + sin_numlightinfo = Sin_CopyLump( SIN_LUMP_LIGHTINFO, sin_lightinfo, sizeof( sin_lightvalue_t ), SIN_MAX_MAP_LIGHTINFO ); + + sin_visdatasize = Sin_CopyLump( SIN_LUMP_VISIBILITY, sin_dvisdata, 1, SIN_MAX_MAP_VISIBILITY ); + sin_lightdatasize = Sin_CopyLump( SIN_LUMP_LIGHTING, sin_dlightdata, 1, SIN_MAX_MAP_LIGHTING ); + sin_entdatasize = Sin_CopyLump( SIN_LUMP_ENTITIES, sin_dentdata, 1, SIN_MAX_MAP_ENTSTRING ); + + Sin_CopyLump( SIN_LUMP_POP, sin_dpop, 1, sizeof( sin_dpop ) ); +#else + sin_nummodels = Sin_CopyLump( SIN_LUMP_MODELS, sin_dmodels, sizeof( sin_dmodel_t ) ); + sin_numvertexes = Sin_CopyLump( SIN_LUMP_VERTEXES, sin_dvertexes, sizeof( sin_dvertex_t ) ); + sin_numplanes = Sin_CopyLump( SIN_LUMP_PLANES, sin_dplanes, sizeof( sin_dplane_t ) ); + sin_numleafs = Sin_CopyLump( SIN_LUMP_LEAFS, sin_dleafs, sizeof( sin_dleaf_t ) ); + sin_numnodes = Sin_CopyLump( SIN_LUMP_NODES, sin_dnodes, sizeof( sin_dnode_t ) ); + sin_numtexinfo = Sin_CopyLump( SIN_LUMP_TEXINFO, sin_texinfo, sizeof( sin_texinfo_t ) ); + sin_numfaces = Sin_CopyLump( SIN_LUMP_FACES, sin_dfaces, sizeof( sin_dface_t ) ); + sin_numleaffaces = Sin_CopyLump( SIN_LUMP_LEAFFACES, sin_dleaffaces, sizeof( sin_dleaffaces[0] ) ); + sin_numleafbrushes = Sin_CopyLump( SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sizeof( sin_dleafbrushes[0] ) ); + sin_numsurfedges = Sin_CopyLump( SIN_LUMP_SURFEDGES, sin_dsurfedges, sizeof( sin_dsurfedges[0] ) ); + sin_numedges = Sin_CopyLump( SIN_LUMP_EDGES, sin_dedges, sizeof( sin_dedge_t ) ); + sin_numbrushes = Sin_CopyLump( SIN_LUMP_BRUSHES, sin_dbrushes, sizeof( sin_dbrush_t ) ); + sin_numbrushsides = Sin_CopyLump( SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sizeof( sin_dbrushside_t ) ); + sin_numareas = Sin_CopyLump( SIN_LUMP_AREAS, sin_dareas, sizeof( sin_darea_t ) ); + sin_numareaportals = Sin_CopyLump( SIN_LUMP_AREAPORTALS, sin_dareaportals, sizeof( sin_dareaportal_t ) ); + + sin_visdatasize = Sin_CopyLump( SIN_LUMP_VISIBILITY, sin_dvisdata, 1 ); + sin_lightdatasize = Sin_CopyLump( SIN_LUMP_LIGHTING, sin_dlightdata, 1 ); + sin_entdatasize = Sin_CopyLump( SIN_LUMP_ENTITIES, sin_dentdata, 1 ); + + Sin_CopyLump( SIN_LUMP_POP, sin_dpop, 1 ); +#endif + + FreeMemory( header ); // everything has been copied out + +// +// swap everything +// + Sin_SwapBSPFile( false ); +} //end of the function Sin_LoadBSPFile + +/* +============= +Sin_LoadBSPFilesTexinfo + +Only loads the sin_texinfo lump, so qdata can scan for textures +============= +*/ +void Sin_LoadBSPFileTexinfo( char *filename ) { + int i; + FILE *f; + int length, ofs; + + header = GetMemory( sizeof( sin_dheader_t ) ); + + f = fopen( filename, "rb" ); + fread( header, sizeof( sin_dheader_t ), 1, f ); + +// swap the header + for ( i = 0 ; i < sizeof( sin_dheader_t ) / 4 ; i++ ) + ( (int *)header )[i] = LittleLong( ( (int *)header )[i] ); + + if ( header->ident != SIN_BSPHEADER && header->ident != SINGAME_BSPHEADER ) { + Error( "%s is not a IBSP file", filename ); + } + if ( header->version != SIN_BSPVERSION && header->version != SINGAME_BSPVERSION ) { + Error( "%s is version %i, not %i", filename, header->version, SIN_BSPVERSION ); + } + + + length = header->lumps[SIN_LUMP_TEXINFO].filelen; + ofs = header->lumps[SIN_LUMP_TEXINFO].fileofs; + + fseek( f, ofs, SEEK_SET ); + fread( sin_texinfo, length, 1, f ); + fclose( f ); + + sin_numtexinfo = length / sizeof( sin_texinfo_t ); + + FreeMemory( header ); // everything has been copied out + + Sin_SwapBSPFile( false ); +} //end of the function Sin_LoadBSPFilesTexinfo + + +//============================================================================ + +FILE *wadfile; +sin_dheader_t outheader; + +#ifdef SIN +void Sin_AddLump( int lumpnum, void *data, int len, int size, int maxsize ) { + sin_lump_t *lump; + int totallength; + + totallength = len * size; + + if ( len > maxsize ) { + Error( "Sin_WriteBSPFile: exceeded max size for lump %d size %d > maxsize %d\n", lumpnum, len, maxsize ); + } + + lump = &header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( wadfile ) ); + lump->filelen = LittleLong( totallength ); + SafeWrite( wadfile, data, ( totallength + 3 ) & ~3 ); +} +#else +void Sin_AddLump( int lumpnum, void *data, int len ) { + sin_lump_t *lump; + + lump = &header->lumps[lumpnum]; + + lump->fileofs = LittleLong( ftell( wadfile ) ); + lump->filelen = LittleLong( len ); + SafeWrite( wadfile, data, ( len + 3 ) & ~3 ); +} +#endif +/* +============= +Sin_WriteBSPFile + +Swaps the bsp file in place, so it should not be referenced again +============= +*/ +void Sin_WriteBSPFile( char *filename ) { + header = &outheader; + memset( header, 0, sizeof( sin_dheader_t ) ); + + Sin_SwapBSPFile( true ); + + header->ident = LittleLong( SIN_BSPHEADER ); + header->version = LittleLong( SIN_BSPVERSION ); + + wadfile = SafeOpenWrite( filename ); + SafeWrite( wadfile, header, sizeof( sin_dheader_t ) ); // overwritten later + +#ifdef SIN + Sin_AddLump( SIN_LUMP_PLANES, sin_dplanes, sin_numplanes, sizeof( sin_dplane_t ), SIN_MAX_MAP_PLANES ); + Sin_AddLump( SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs, sizeof( sin_dleaf_t ), SIN_MAX_MAP_LEAFS ); + Sin_AddLump( SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes, sizeof( sin_dvertex_t ), SIN_MAX_MAP_VERTS ); + Sin_AddLump( SIN_LUMP_NODES, sin_dnodes, sin_numnodes, sizeof( sin_dnode_t ), SIN_MAX_MAP_NODES ); + Sin_AddLump( SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo, sizeof( sin_texinfo_t ), SIN_MAX_MAP_TEXINFO ); + Sin_AddLump( SIN_LUMP_FACES, sin_dfaces, sin_numfaces, sizeof( sin_dface_t ), SIN_MAX_MAP_FACES ); + Sin_AddLump( SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes, sizeof( sin_dbrush_t ), SIN_MAX_MAP_BRUSHES ); + Sin_AddLump( SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides, sizeof( sin_dbrushside_t ), SIN_MAX_MAP_BRUSHSIDES ); + Sin_AddLump( SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces, sizeof( sin_dleaffaces[0] ), SIN_MAX_MAP_LEAFFACES ); + Sin_AddLump( SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes, sizeof( sin_dleafbrushes[0] ), SIN_MAX_MAP_LEAFBRUSHES ); + Sin_AddLump( SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges, sizeof( sin_dsurfedges[0] ), SIN_MAX_MAP_SURFEDGES ); + Sin_AddLump( SIN_LUMP_EDGES, sin_dedges, sin_numedges, sizeof( sin_dedge_t ), SIN_MAX_MAP_EDGES ); + Sin_AddLump( SIN_LUMP_MODELS, sin_dmodels, sin_nummodels, sizeof( sin_dmodel_t ), SIN_MAX_MAP_MODELS ); + Sin_AddLump( SIN_LUMP_AREAS, sin_dareas, sin_numareas, sizeof( sin_darea_t ), SIN_MAX_MAP_AREAS ); + Sin_AddLump( SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals, sizeof( sin_dareaportal_t ), SIN_MAX_MAP_AREAPORTALS ); + Sin_AddLump( SIN_LUMP_LIGHTINFO, sin_lightinfo, sin_numlightinfo, sizeof( sin_lightvalue_t ), SIN_MAX_MAP_LIGHTINFO ); + + Sin_AddLump( SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize, 1, SIN_MAX_MAP_LIGHTING ); + Sin_AddLump( SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize, 1, SIN_MAX_MAP_VISIBILITY ); + Sin_AddLump( SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize, 1, SIN_MAX_MAP_ENTSTRING ); + Sin_AddLump( SIN_LUMP_POP, sin_dpop, sizeof( sin_dpop ), 1, sizeof( sin_dpop ) ); +#else + Sin_AddLump( SIN_LUMP_PLANES, sin_dplanes, sin_numplanes * sizeof( sin_dplane_t ) ); + Sin_AddLump( SIN_LUMP_LEAFS, sin_dleafs, sin_numleafs * sizeof( sin_dleaf_t ) ); + Sin_AddLump( SIN_LUMP_VERTEXES, sin_dvertexes, sin_numvertexes * sizeof( sin_dvertex_t ) ); + Sin_AddLump( SIN_LUMP_NODES, sin_dnodes, sin_numnodes * sizeof( sin_dnode_t ) ); + Sin_AddLump( SIN_LUMP_TEXINFO, sin_texinfo, sin_numtexinfo * sizeof( sin_texinfo_t ) ); + Sin_AddLump( SIN_LUMP_FACES, sin_dfaces, sin_numfaces * sizeof( sin_dface_t ) ); + Sin_AddLump( SIN_LUMP_BRUSHES, sin_dbrushes, sin_numbrushes * sizeof( sin_dbrush_t ) ); + Sin_AddLump( SIN_LUMP_BRUSHSIDES, sin_dbrushsides, sin_numbrushsides * sizeof( sin_dbrushside_t ) ); + Sin_AddLump( SIN_LUMP_LEAFFACES, sin_dleaffaces, sin_numleaffaces * sizeof( sin_dleaffaces[0] ) ); + Sin_AddLump( SIN_LUMP_LEAFBRUSHES, sin_dleafbrushes, sin_numleafbrushes * sizeof( sin_dleafbrushes[0] ) ); + Sin_AddLump( SIN_LUMP_SURFEDGES, sin_dsurfedges, sin_numsurfedges * sizeof( sin_dsurfedges[0] ) ); + Sin_AddLump( SIN_LUMP_EDGES, sin_dedges, sin_numedges * sizeof( sin_dedge_t ) ); + Sin_AddLump( SIN_LUMP_MODELS, sin_dmodels, sin_nummodels * sizeof( sin_dmodel_t ) ); + Sin_AddLump( SIN_LUMP_AREAS, sin_dareas, sin_numareas * sizeof( sin_darea_t ) ); + Sin_AddLump( SIN_LUMP_AREAPORTALS, sin_dareaportals, sin_numareaportals * sizeof( sin_dareaportal_t ) ); + + Sin_AddLump( SIN_LUMP_LIGHTING, sin_dlightdata, sin_lightdatasize ); + Sin_AddLump( SIN_LUMP_VISIBILITY, sin_dvisdata, sin_visdatasize ); + Sin_AddLump( SIN_LUMP_ENTITIES, sin_dentdata, sin_entdatasize ); + Sin_AddLump( SIN_LUMP_POP, sin_dpop, sizeof( sin_dpop ) ); +#endif + + fseek( wadfile, 0, SEEK_SET ); + SafeWrite( wadfile, header, sizeof( sin_dheader_t ) ); + fclose( wadfile ); +} + +//============================================================================ + + +//============================================ + +/* +================ +ParseEntities + +Parses the sin_dentdata string into entities +================ +*/ +void Sin_ParseEntities( void ) { + script_t *script; + + num_entities = 0; + script = LoadScriptMemory( sin_dentdata, sin_entdatasize, "*sin bsp file" ); + SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | + SCFL_NOSTRINGESCAPECHARS ); + + while ( ParseEntity( script ) ) + { + } //end while + + FreeScript( script ); +} //end of the function Sin_ParseEntities + + +/* +================ +UnparseEntities + +Generates the sin_dentdata string from all the entities +================ +*/ +void Sin_UnparseEntities( void ) { + char *buf, *end; + epair_t *ep; + char line[2048]; + int i; + char key[1024], value[1024]; + + buf = sin_dentdata; + end = buf; + *end = 0; + + for ( i = 0 ; i < num_entities ; i++ ) + { + ep = entities[i].epairs; + if ( !ep ) { + continue; // ent got removed + + } + strcat( end,"{\n" ); + end += 2; + + for ( ep = entities[i].epairs ; ep ; ep = ep->next ) + { + strcpy( key, ep->key ); + StripTrailing( key ); + strcpy( value, ep->value ); + StripTrailing( value ); + + sprintf( line, "\"%s\" \"%s\"\n", key, value ); + strcat( end, line ); + end += strlen( line ); + } + strcat( end,"}\n" ); + end += 2; + + if ( end > buf + SIN_MAX_MAP_ENTSTRING ) { + Error( "Entity text too long" ); + } + } + sin_entdatasize = end - buf + 1; +} //end of the function Sin_UnparseEntities + +#ifdef SIN +void FreeValueKeys( entity_t *ent ) { + epair_t *ep,*next; + + for ( ep = ent->epairs ; ep ; ep = next ) + { + next = ep->next; + FreeMemory( ep->value ); + FreeMemory( ep->key ); + FreeMemory( ep ); + } + ent->epairs = NULL; +} +#endif + +/* +============= +Sin_PrintBSPFileSizes + +Dumps info about current file +============= +*/ +void Sin_PrintBSPFileSizes( void ) { + if ( !num_entities ) { + Sin_ParseEntities(); + } + + Log_Print( "%6i models %7i\n" + ,sin_nummodels, (int)( sin_nummodels * sizeof( sin_dmodel_t ) ) ); + Log_Print( "%6i brushes %7i\n" + ,sin_numbrushes, (int)( sin_numbrushes * sizeof( sin_dbrush_t ) ) ); + Log_Print( "%6i brushsides %7i\n" + ,sin_numbrushsides, (int)( sin_numbrushsides * sizeof( sin_dbrushside_t ) ) ); + Log_Print( "%6i planes %7i\n" + ,sin_numplanes, (int)( sin_numplanes * sizeof( sin_dplane_t ) ) ); + Log_Print( "%6i texinfo %7i\n" + ,sin_numtexinfo, (int)( sin_numtexinfo * sizeof( sin_texinfo_t ) ) ); +#ifdef SIN + Log_Print( "%6i lightinfo %7i\n" + ,sin_numlightinfo, (int)( sin_numlightinfo * sizeof( sin_lightvalue_t ) ) ); +#endif + Log_Print( "%6i entdata %7i\n", num_entities, sin_entdatasize ); + + Log_Print( "\n" ); + + Log_Print( "%6i vertexes %7i\n" + ,sin_numvertexes, (int)( sin_numvertexes * sizeof( sin_dvertex_t ) ) ); + Log_Print( "%6i nodes %7i\n" + ,sin_numnodes, (int)( sin_numnodes * sizeof( sin_dnode_t ) ) ); + Log_Print( "%6i faces %7i\n" + ,sin_numfaces, (int)( sin_numfaces * sizeof( sin_dface_t ) ) ); + Log_Print( "%6i leafs %7i\n" + ,sin_numleafs, (int)( sin_numleafs * sizeof( sin_dleaf_t ) ) ); + Log_Print( "%6i leaffaces %7i\n" + ,sin_numleaffaces, (int)( sin_numleaffaces * sizeof( sin_dleaffaces[0] ) ) ); + Log_Print( "%6i leafbrushes %7i\n" + ,sin_numleafbrushes, (int)( sin_numleafbrushes * sizeof( sin_dleafbrushes[0] ) ) ); + Log_Print( "%6i surfedges %7i\n" + ,sin_numsurfedges, (int)( sin_numsurfedges * sizeof( sin_dsurfedges[0] ) ) ); + Log_Print( "%6i edges %7i\n" + ,sin_numedges, (int)( sin_numedges * sizeof( sin_dedge_t ) ) ); + Log_Print( " lightdata %7i\n", sin_lightdatasize ); + Log_Print( " visdata %7i\n", sin_visdatasize ); +} diff --git a/src/bspc/l_bsp_sin.h b/src/bspc/l_bsp_sin.h new file mode 100644 index 0000000..aea69fd --- /dev/null +++ b/src/bspc/l_bsp_sin.h @@ -0,0 +1,113 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "sinfiles.h" + +#define SINGAME_BSPHEADER ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'R' ) //RBSP +#define SINGAME_BSPVERSION 1 + +#define SIN_BSPHEADER ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' ) //IBSP +#define SIN_BSPVERSION 41 + + +extern int sin_nummodels; +extern sin_dmodel_t *sin_dmodels; //[MAX_MAP_MODELS]; + +extern int sin_visdatasize; +extern byte *sin_dvisdata; //[MAX_MAP_VISIBILITY]; +extern sin_dvis_t *sin_dvis; // = (dvis_t *)sin_sin_dvisdata; + +extern int sin_lightdatasize; +extern byte *sin_dlightdata; //[MAX_MAP_LIGHTING]; + +extern int sin_entdatasize; +extern char *sin_dentdata; //[MAX_MAP_ENTSTRING]; + +extern int sin_numleafs; +extern sin_dleaf_t *sin_dleafs; //[MAX_MAP_LEAFS]; + +extern int sin_numplanes; +extern sin_dplane_t *sin_dplanes; //[MAX_MAP_PLANES]; + +extern int sin_numvertexes; +extern sin_dvertex_t *sin_dvertexes; //[MAX_MAP_VERTS]; + +extern int sin_numnodes; +extern sin_dnode_t *sin_dnodes; //[MAX_MAP_NODES]; + +extern int sin_numtexinfo; +extern sin_texinfo_t *sin_texinfo; //[MAX_MAP_sin_texinfo]; + +extern int sin_numfaces; +extern sin_dface_t *sin_dfaces; //[MAX_MAP_FACES]; + +extern int sin_numedges; +extern sin_dedge_t *sin_dedges; //[MAX_MAP_EDGES]; + +extern int sin_numleaffaces; +extern unsigned short *sin_dleaffaces; //[MAX_MAP_LEAFFACES]; + +extern int sin_numleafbrushes; +extern unsigned short *sin_dleafbrushes; //[MAX_MAP_LEAFBRUSHES]; + +extern int sin_numsurfedges; +extern int *sin_dsurfedges; //[MAX_MAP_SURFEDGES]; + +extern int sin_numbrushes; +extern sin_dbrush_t *sin_dbrushes; //[MAX_MAP_BRUSHES]; + +extern int sin_numbrushsides; +extern sin_dbrushside_t *sin_dbrushsides; //[MAX_MAP_BRUSHSIDES]; + +extern int sin_numareas; +extern sin_darea_t *sin_dareas; //[MAX_MAP_AREAS]; + +extern int sin_numareaportals; +extern sin_dareaportal_t *sin_dareaportals; //[MAX_MAP_AREAPORTALS]; + +extern int sin_numlightinfo; +extern sin_lightvalue_t *sin_lightinfo; //[MAX_MAP_LIGHTINFO]; + +extern byte sin_dpop[256]; + +extern char sin_dbrushsidetextured[SIN_MAX_MAP_BRUSHSIDES]; + +void Sin_AllocMaxBSP( void ); +void Sin_FreeMaxBSP( void ); + +void Sin_DecompressVis( byte *in, byte *decompressed ); +int Sin_CompressVis( byte *vis, byte *dest ); + +void Sin_LoadBSPFile( char *filename, int offset, int length ); +void Sin_LoadBSPFileTexinfo( char *filename ); // just for qdata +void Sin_WriteBSPFile( char *filename ); +void Sin_PrintBSPFileSizes( void ); +void Sin_ParseEntities( void ); +void Sin_UnparseEntities( void ); + diff --git a/src/bspc/l_cmd.c b/src/bspc/l_cmd.c new file mode 100644 index 0000000..9e3d4c0 --- /dev/null +++ b/src/bspc/l_cmd.c @@ -0,0 +1,1170 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +// cmdlib.c + +#include "l_cmd.h" +#include "l_log.h" +#include "l_mem.h" +#include +#include + +#ifndef SIN +#define SIN +#endif //SIN + +#if defined( WIN32 ) || defined( _WIN32 ) +#include +#else +#include +#endif + +#ifdef NeXT +#include +#endif + +#define BASEDIRNAME "wolf" +#define PATHSEPERATOR '/' + +// set these before calling CheckParm +int myargc; +char **myargv; + +char com_token[1024]; +qboolean com_eof; + +qboolean archive; +char archivedir[1024]; + + +/* +=================== +ExpandWildcards + +Mimic unix command line expansion +=================== +*/ +#define MAX_EX_ARGC 1024 +int ex_argc; +char *ex_argv[MAX_EX_ARGC]; +#ifdef _WIN32 +#include "io.h" +void ExpandWildcards( int *argc, char ***argv ) { + struct _finddata_t fileinfo; + int handle; + int i; + char filename[1024]; + char filebase[1024]; + char *path; + + ex_argc = 0; + for ( i = 0 ; i < *argc ; i++ ) + { + path = ( *argv )[i]; + if ( path[0] == '-' + || ( !strstr( path, "*" ) && !strstr( path, "?" ) ) ) { + ex_argv[ex_argc++] = path; + continue; + } + + handle = _findfirst( path, &fileinfo ); + if ( handle == -1 ) { + return; + } + + ExtractFilePath( path, filebase ); + + do + { + sprintf( filename, "%s%s", filebase, fileinfo.name ); + ex_argv[ex_argc++] = copystring( filename ); + } while ( _findnext( handle, &fileinfo ) != -1 ); + + _findclose( handle ); + } + + *argc = ex_argc; + *argv = ex_argv; +} +#else +void ExpandWildcards( int *argc, char ***argv ) { +} +#endif + +#ifdef WINBSPC + +#include + +HWND program_hwnd; + +void SetProgramHandle( HWND hwnd ) { + program_hwnd = hwnd; +} //end of the function SetProgramHandle + +/* +================= +Error + +For abnormal program terminations in windowed apps +================= +*/ +void Error( char *error, ... ) { + va_list argptr; + char text[1024]; + char text2[1024]; + int err; + + err = GetLastError(); + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + + sprintf( text2, "%s\nGetLastError() = %i", text, err ); + MessageBox( program_hwnd, text2, "Error", 0 /* MB_OK */ ); + + Log_Write( text ); + Log_Close(); + + exit( 1 ); +} //end of the function Error + +void Warning( char *szFormat, ... ) { + char szBuffer[256]; + va_list argptr; + + va_start( argptr, szFormat ); + Q_vsnprintf( szBuffer, sizeof( szBuffer ), szFormat, argptr ); + va_end( argptr ); + + MessageBox( program_hwnd, szBuffer, "Warning", MB_OK ); + + Log_Write( szBuffer ); +} //end of the function Warning + + +#else +/* +================= +Error + +For abnormal program terminations in console apps +================= +*/ +void Error( char *error, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + printf( "ERROR: %s\n", text ); + + Log_Write( text ); + Log_Close(); + + exit( 1 ); +} //end of the function Error + +void Warning( char *warning, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, warning ); + Q_vsnprintf( text, sizeof( text ), warning, argptr ); + va_end( argptr ); + printf( "WARNING: %s\n", text ); + + Log_Write( text ); +} //end of the function Warning + +#endif + +//only printf if in verbose mode +qboolean verbose = true; + +void qprintf( char *format, ... ) { + va_list argptr; +#ifdef WINBSPC + char buf[2048]; +#endif //WINBSPC + + if ( !verbose ) { + return; + } + + va_start( argptr,format ); +#ifdef WINBSPC + Q_vsnprintf( buf, sizeof( buf ), format, argptr ); + WinBSPCPrint( buf ); +#else + vprintf( format, argptr ); +#endif //WINBSPC + va_end( argptr ); +} //end of the function qprintf + +void Com_Error( int level, char *error, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + Error( text ); +} //end of the funcion Com_Error + +void Com_Printf( const char *fmt, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + Log_Print( text ); +} //end of the funcion Com_Printf + +/* + +qdir will hold the path up to the quake directory, including the slash + + f:\quake \ + /raid/quake/ + +gamedir will hold qdir + the game directory (id1, id2, etc) + + */ + +char qdir[1024]; +char gamedir[1024]; + +void SetQdirFromPath( char *path ) { + char temp[1024]; + char *c; + int len; + + if ( !( path[0] == '/' || path[0] == '\\' || path[1] == ':' ) ) { // path is partial + Q_getwd( temp ); + strcat( temp, path ); + path = temp; + } + + // search for "quake2" in path + + len = strlen( BASEDIRNAME ); + for ( c = path + strlen( path ) - 1 ; c != path ; c-- ) + if ( !Q_strncasecmp( c, BASEDIRNAME, len ) ) { + strncpy( qdir, path, c + len + 1 - path ); + qprintf( "qdir: %s\n", qdir ); + c += len + 1; + while ( *c ) + { + if ( *c == '/' || *c == '\\' ) { + strncpy( gamedir, path, c + 1 - path ); + qprintf( "gamedir: %s\n", gamedir ); + return; + } + c++; + } + Error( "No gamedir in %s", path ); + return; + } + Error( "SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path ); +} + +char *ExpandArg( char *path ) { + static char full[1024]; + + if ( path[0] != '/' && path[0] != '\\' && path[1] != ':' ) { + Q_getwd( full ); + strcat( full, path ); + } else { + strcpy( full, path ); + } + return full; +} + +char *ExpandPath( char *path ) { + static char full[1024]; + if ( !qdir ) { + Error( "ExpandPath called without qdir set" ); + } + if ( path[0] == '/' || path[0] == '\\' || path[1] == ':' ) { + return path; + } + sprintf( full, "%s%s", qdir, path ); + return full; +} + +char *ExpandPathAndArchive( char *path ) { + char *expanded; + char archivename[1024]; + + expanded = ExpandPath( path ); + + if ( archive ) { + sprintf( archivename, "%s/%s", archivedir, path ); + QCopyFile( expanded, archivename ); + } + return expanded; +} + + +char *copystring( char *s ) { + char *b; + b = GetMemory( strlen( s ) + 1 ); + strcpy( b, s ); + return b; +} + + + +/* +================ +I_FloatTime +================ +*/ +double I_FloatTime( void ) { + time_t t; + + time( &t ); + + return t; +#if 0 +// more precise, less portable + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday( &tp, &tzp ); + + if ( !secbase ) { + secbase = tp.tv_sec; + return tp.tv_usec / 1000000.0; + } + + return ( tp.tv_sec - secbase ) + tp.tv_usec / 1000000.0; +#endif +} + +void Q_getwd( char *out ) { +#if defined( WIN32 ) || defined( _WIN32 ) + getcwd( out, 256 ); + strcat( out, "\\" ); +#else + getwd( out ); + strcat( out, "/" ); +#endif +} + + +void Q_mkdir( char *path ) { +#ifdef WIN32 + if ( _mkdir( path ) != -1 ) { + return; + } +#else + if ( mkdir( path, 0777 ) != -1 ) { + return; + } +#endif + if ( errno != EEXIST ) { + Error( "mkdir %s: %s",path, strerror( errno ) ); + } +} + +/* +============ +FileTime + +returns -1 if not present +============ +*/ +int FileTime( char *path ) { + struct stat buf; + + if ( stat( path,&buf ) == -1 ) { + return -1; + } + + return buf.st_mtime; +} + + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse( char *data ) { + int c; + int len; + + len = 0; + com_token[0] = 0; + + if ( !data ) { + return NULL; + } + +// skip whitespace +skipwhite: + while ( ( c = *data ) <= ' ' ) + { + if ( c == 0 ) { + com_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if ( c == '/' && data[1] == '/' ) { + while ( *data && *data != '\n' ) + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if ( c == '\"' ) { + data++; + do + { + c = *data++; + if ( c == '\"' ) { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } while ( 1 ); + } + +// parse single characters + if ( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':' ) { + com_token[len] = c; + len++; + com_token[len] = 0; + return data + 1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if ( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':' ) { + break; + } + } while ( c > 32 ); + + com_token[len] = 0; + return data; +} + + +int Q_strncasecmp( char *s1, char *s2, int n ) { + int c1, c2; + + do + { + c1 = *s1++; + c2 = *s2++; + + if ( !n-- ) { + return 0; // strings are equal until end point + + } + if ( c1 != c2 ) { + if ( c1 >= 'a' && c1 <= 'z' ) { + c1 -= ( 'a' - 'A' ); + } + if ( c2 >= 'a' && c2 <= 'z' ) { + c2 -= ( 'a' - 'A' ); + } + if ( c1 != c2 ) { + return -1; // strings not equal + } + } + } while ( c1 ); + + return 0; // strings are equal +} + +int Q_strcasecmp( char *s1, char *s2 ) { + return Q_strncasecmp( s1, s2, 99999 ); +} + +int Q_stricmp( char *s1, char *s2 ) { + return Q_strncasecmp( s1, s2, 99999 ); +} + +void Q_strncpyz( char *dest, const char *src, int destsize ) { + strncpy( dest, src, destsize - 1 ); + dest[destsize - 1] = 0; +} + +char *strlower( char *start ) { + char *in; + in = start; + while ( *in ) + { + *in = tolower( *in ); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int CheckParm( char *check ) { + int i; + + for ( i = 1; i < myargc; i++ ) + { + if ( !Q_strcasecmp( check, myargv[i] ) ) { + return i; + } + } + + return 0; +} + + + +/* +================ +Q_filelength +================ +*/ +int Q_filelength( FILE *f ) { + int pos; + int end; + + pos = ftell( f ); + fseek( f, 0, SEEK_END ); + end = ftell( f ); + fseek( f, pos, SEEK_SET ); + + return end; +} + + +FILE *SafeOpenWrite( char *filename ) { + FILE *f; + + f = fopen( filename, "wb" ); + + if ( !f ) { + Error( "Error opening %s: %s",filename,strerror( errno ) ); + } + + return f; +} + +FILE *SafeOpenRead( char *filename ) { + FILE *f; + + f = fopen( filename, "rb" ); + + if ( !f ) { + Error( "Error opening %s: %s",filename,strerror( errno ) ); + } + + return f; +} + + +void SafeRead( FILE *f, void *buffer, int count ) { + if ( fread( buffer, 1, count, f ) != (size_t)count ) { + Error( "File read failure" ); + } +} + + +void SafeWrite( FILE *f, void *buffer, int count ) { + if ( fwrite( buffer, 1, count, f ) != (size_t)count ) { + Error( "File write failure" ); + } +} + + +/* +============== +FileExists +============== +*/ +qboolean FileExists( char *filename ) { + FILE *f; + + f = fopen( filename, "r" ); + if ( !f ) { + return false; + } + fclose( f ); + return true; +} + +/* +============== +LoadFile +============== +*/ +int LoadFile( char *filename, void **bufferptr, int offset, int length ) { + FILE *f; + void *buffer; + + f = SafeOpenRead( filename ); + fseek( f, offset, SEEK_SET ); + if ( !length ) { + length = Q_filelength( f ); + } + buffer = GetMemory( length + 1 ); + ( (char *)buffer )[length] = 0; + SafeRead( f, buffer, length ); + fclose( f ); + + *bufferptr = buffer; + return length; +} + + +/* +============== +TryLoadFile + +Allows failure +============== +*/ +int TryLoadFile( char *filename, void **bufferptr ) { + FILE *f; + int length; + void *buffer; + + *bufferptr = NULL; + + f = fopen( filename, "rb" ); + if ( !f ) { + return -1; + } + length = Q_filelength( f ); + buffer = GetMemory( length + 1 ); + ( (char *)buffer )[length] = 0; + SafeRead( f, buffer, length ); + fclose( f ); + + *bufferptr = buffer; + return length; +} + + +/* +============== +SaveFile +============== +*/ +void SaveFile( char *filename, void *buffer, int count ) { + FILE *f; + + f = SafeOpenWrite( filename ); + SafeWrite( f, buffer, count ); + fclose( f ); +} + + + +void DefaultExtension( char *path, char *extension ) { + char *src; +// +// if path doesnt have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen( path ) - 1; + + while ( *src != PATHSEPERATOR && src != path ) + { + if ( *src == '.' ) { + return; // it has an extension + } + src--; + } + + strcat( path, extension ); +} + + +void DefaultPath( char *path, char *basepath ) { + char temp[128]; + + if ( path[0] == PATHSEPERATOR ) { + return; // absolute path location + } + strcpy( temp,path ); + strcpy( path,basepath ); + strcat( path,temp ); +} + + +void StripFilename( char *path ) { + int length; + + length = strlen( path ) - 1; + while ( length > 0 && path[length] != PATHSEPERATOR ) + length--; + path[length] = 0; +} + +void StripExtension( char *path ) { + int length; + + length = strlen( path ) - 1; + while ( length > 0 && path[length] != '.' ) + { + length--; + if ( path[length] == '/' ) { + return; // no extension + } + } + if ( length ) { + path[length] = 0; + } +} + + +/* +==================== +Extract file parts +==================== +*/ +// FIXME: should include the slash, otherwise +// backing to an empty path will be wrong when appending a slash +void ExtractFilePath( char *path, char *dest ) { + char *src; + + src = path + strlen( path ) - 1; + +// +// back up until a \ or the start +// + while ( src != path && *( src - 1 ) != '\\' && *( src - 1 ) != '/' ) + src--; + + memcpy( dest, path, src - path ); + dest[src - path] = 0; +} + +void ExtractFileBase( char *path, char *dest ) { + char *src; + + src = path + strlen( path ) - 1; + +// +// back up until a \ or the start +// + while ( src != path && *( src - 1 ) != '\\' && *( src - 1 ) != '/' ) + src--; + + while ( *src && *src != '.' ) + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension( char *path, char *dest ) { + char *src; + + src = path + strlen( path ) - 1; + +// +// back up until a . or the start +// + while ( src != path && *( src - 1 ) != '.' ) + src--; + if ( src == path ) { + *dest = 0; // no extension + return; + } + + strcpy( dest,src ); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +int ParseHex( char *hex ) { + char *str; + int num; + + num = 0; + str = hex; + + while ( *str ) + { + num <<= 4; + if ( *str >= '0' && *str <= '9' ) { + num += *str - '0'; + } else if ( *str >= 'a' && *str <= 'f' ) { + num += 10 + *str - 'a'; + } else if ( *str >= 'A' && *str <= 'F' ) { + num += 10 + *str - 'A'; + } else { + Error( "Bad hex number: %s",hex ); + } + str++; + } + + return num; +} + + +int ParseNum( char *str ) { + if ( str[0] == '$' ) { + return ParseHex( str + 1 ); + } + if ( str[0] == '0' && str[1] == 'x' ) { + return ParseHex( str + 2 ); + } + return atol( str ); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ + +short LittleShort( short l ) { + byte b1,b2; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + + return ( b1 << 8 ) + b2; +} + +short BigShort( short l ) { + return l; +} + + +int LittleLong( int l ) { + byte b1,b2,b3,b4; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + b3 = ( l >> 16 ) & 255; + b4 = ( l >> 24 ) & 255; + + return ( (int)b1 << 24 ) + ( (int)b2 << 16 ) + ( (int)b3 << 8 ) + b4; +} + +int BigLong( int l ) { + return l; +} + + +float LittleFloat( float l ) { + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat( float l ) { + return l; +} + +#ifdef SIN +unsigned short LittleUnsignedShort( unsigned short l ) { + byte b1,b2; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + + return ( b1 << 8 ) + b2; +} + +unsigned short BigUnsignedShort( unsigned short l ) { + return l; +} + +unsigned LittleUnsigned( unsigned l ) { + byte b1,b2,b3,b4; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + b3 = ( l >> 16 ) & 255; + b4 = ( l >> 24 ) & 255; + + return ( (unsigned)b1 << 24 ) + ( (unsigned)b2 << 16 ) + ( (unsigned)b3 << 8 ) + b4; +} + +unsigned BigUnsigned( unsigned l ) { + return l; +} +#endif + + +#else + + +short BigShort( short l ) { + byte b1,b2; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + + return ( b1 << 8 ) + b2; +} + +short LittleShort( short l ) { + return l; +} + + +int BigLong( int l ) { + byte b1,b2,b3,b4; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + b3 = ( l >> 16 ) & 255; + b4 = ( l >> 24 ) & 255; + + return ( (int)b1 << 24 ) + ( (int)b2 << 16 ) + ( (int)b3 << 8 ) + b4; +} + +int LittleLong( int l ) { + return l; +} + +float BigFloat( float l ) { + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat( float l ) { + return l; +} + +#ifdef SIN +unsigned short BigUnsignedShort( unsigned short l ) { + byte b1,b2; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + + return ( b1 << 8 ) + b2; +} + +unsigned short LittleUnsignedShort( unsigned short l ) { + return l; +} + + +unsigned BigUnsigned( unsigned l ) { + byte b1,b2,b3,b4; + + b1 = l & 255; + b2 = ( l >> 8 ) & 255; + b3 = ( l >> 16 ) & 255; + b4 = ( l >> 24 ) & 255; + + return ( (unsigned)b1 << 24 ) + ( (unsigned)b2 << 16 ) + ( (unsigned)b3 << 8 ) + b4; +} + +unsigned LittleUnsigned( unsigned l ) { + return l; +} +#endif + + +#endif + + +//======================================================= + + +// 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 + +static unsigned short crctable[256] = +{ + 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 +}; + +void CRC_Init( unsigned short *crcvalue ) { + *crcvalue = CRC_INIT_VALUE; +} + +void CRC_ProcessByte( unsigned short *crcvalue, byte data ) { + *crcvalue = ( *crcvalue << 8 ) ^ crctable[( *crcvalue >> 8 ) ^ data]; +} + +unsigned short CRC_Value( unsigned short crcvalue ) { + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + +/* +============ +CreatePath +============ +*/ +void CreatePath( char *path ) { + char *ofs, c; + + if ( path[1] == ':' ) { + path += 2; + } + + for ( ofs = path + 1 ; *ofs ; ofs++ ) + { + c = *ofs; + if ( c == '/' || c == '\\' ) { // create the directory + *ofs = 0; + Q_mkdir( path ); + *ofs = c; + } + } +} + + +/* +============ +QCopyFile + + Used to archive source files +============ +*/ +void QCopyFile( char *from, char *to ) { + void *buffer; + int length; + + length = LoadFile( from, &buffer, 0, 0 ); + CreatePath( to ); + SaveFile( to, buffer, length ); + FreeMemory( buffer ); +} + +void FS_FreeFile( void *buf ) { + FreeMemory( buf ); +} //end of the function FS_FreeFile + +int FS_ReadFileAndCache( const char *qpath, void **buffer ) { + return LoadFile( (char *) qpath, buffer, 0, 0 ); +} //end of the function FS_ReadFileAndCache + +int FS_FOpenFileRead( const char *filename, FILE **file, qboolean uniqueFILE ) { + *file = fopen( filename, "rb" ); + return ( *file != NULL ); +} //end of the function FS_FOpenFileRead diff --git a/src/bspc/l_cmd.h b/src/bspc/l_cmd.h new file mode 100644 index 0000000..525f88c --- /dev/null +++ b/src/bspc/l_cmd.h @@ -0,0 +1,164 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cmdlib.h + +#ifndef SIN +#define SIN +#endif //SIN + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#ifdef _WIN32 +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4305) // truncate from double to float +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum {false, true} qboolean; +typedef unsigned char byte; +#endif + +// the dec offsetof macro doesnt work very well... +#define myoffsetof( type,identifier ) ( (size_t)&( (type *)0 )->identifier ) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +char *strupr( char *in ); +char *strlower( char *in ); +int Q_strncasecmp( char *s1, char *s2, int n ); +int Q_strcasecmp( char *s1, char *s2 ); +void Q_getwd( char *out ); + +int Q_filelength( FILE *f ); +int FileTime( char *path ); + +void Q_mkdir( char *path ); + +extern char qdir[1024]; +extern char gamedir[1024]; +void SetQdirFromPath( char *path ); +char *ExpandArg( char *path ); // from cmd line +char *ExpandPath( char *path ); // from scripts +char *ExpandPathAndArchive( char *path ); + + +double I_FloatTime( void ); + +void Error( char *error, ... ); +void Warning( char *warning, ... ); + +int CheckParm( char *check ); + +FILE *SafeOpenWrite( char *filename ); +FILE *SafeOpenRead( char *filename ); +void SafeRead( FILE *f, void *buffer, int count ); +void SafeWrite( FILE *f, void *buffer, int count ); + +int LoadFile( char *filename, void **bufferptr, int offset, int length ); +int TryLoadFile( char *filename, void **bufferptr ); +void SaveFile( char *filename, void *buffer, int count ); +qboolean FileExists( char *filename ); + +void DefaultExtension( char *path, char *extension ); +void DefaultPath( char *path, char *basepath ); +void StripFilename( char *path ); +void StripExtension( char *path ); + +void ExtractFilePath( char *path, char *dest ); +void ExtractFileBase( char *path, char *dest ); +void ExtractFileExtension( char *path, char *dest ); + +int ParseNum( char *str ); + +short BigShort( short l ); +short LittleShort( short l ); +int BigLong( int l ); +int LittleLong( int l ); +float BigFloat( float l ); +float LittleFloat( float l ); + +#ifdef SIN +unsigned short BigUnsignedShort( unsigned short l ); +unsigned short LittleUnsignedShort( unsigned short l ); +unsigned BigUnsigned( unsigned l ); +unsigned LittleUnsigned( unsigned l ); +#endif + + +char *COM_Parse( char *data ); + +extern char com_token[1024]; +extern qboolean com_eof; + +char *copystring( char *s ); + + +void CRC_Init( unsigned short *crcvalue ); +void CRC_ProcessByte( unsigned short *crcvalue, byte data ); +unsigned short CRC_Value( unsigned short crcvalue ); + +void CreatePath( char *path ); +void QCopyFile( char *from, char *to ); + +extern qboolean archive; +extern char archivedir[1024]; + + +extern qboolean verbose; +void qprintf( char *format, ... ); + +void ExpandWildcards( int *argc, char ***argv ); + + +// for compression routines +typedef struct +{ + byte *data; + int count; +} cblock_t; + +#endif + diff --git a/src/bspc/l_log.c b/src/bspc/l_log.c new file mode 100644 index 0000000..3c6c005 --- /dev/null +++ b/src/bspc/l_log.c @@ -0,0 +1,219 @@ +/* +=========================================================================== + +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_log.c +// Function: log file stuff +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +#include +#include +#include + +#include "qbsp.h" + +#define MAX_LOGFILENAMESIZE 1024 + +typedef struct logfile_s +{ + char filename[MAX_LOGFILENAMESIZE]; + FILE *fp; + int numwrites; +} logfile_t; + +logfile_t logfile; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Open( char *filename ) { + if ( !filename || !strlen( filename ) ) { + printf( "openlog \n" ); + return; + } //end if + if ( logfile.fp ) { + printf( "log file %s is already opened\n", logfile.filename ); + return; + } //end if + logfile.fp = fopen( filename, "wb" ); + if ( !logfile.fp ) { + printf( "can't open the log file %s\n", filename ); + return; + } //end if + strncpy( logfile.filename, filename, MAX_LOGFILENAMESIZE ); + printf( "Opened log %s\n", logfile.filename ); +} //end of the function Log_Create +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Close( void ) { + if ( !logfile.fp ) { + printf( "no log file to close\n" ); + return; + } //end if + if ( fclose( logfile.fp ) ) { + printf( "can't close log file %s\n", logfile.filename ); + return; + } //end if + logfile.fp = NULL; + printf( "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 Log_UnifyEndOfLine( char *buf ) { + int i; + + for ( i = 0; buf[i]; i++ ) + { + if ( buf[i] == '\n' ) { + if ( i <= 0 || buf[i - 1] != '\r' ) { + memmove( &buf[i + 1], &buf[i], strlen( &buf[i] ) + 1 ); + buf[i] = '\r'; + i++; + } //end if + } //end if + } //end for +} //end of the function Log_UnifyEndOfLine +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Print( char *fmt, ... ) { + va_list ap; + char buf[2048]; + + va_start( ap, fmt ); + Q_vsnprintf( buf, sizeof( buf ), fmt, ap ); + va_end( ap ); + + if ( verbose ) { +#ifdef WINBSPC + WinBSPCPrint( buf ); +#else + printf( "%s", buf ); +#endif //WINBSPS + } //end if + + if ( logfile.fp ) { + Log_UnifyEndOfLine( buf ); + fprintf( logfile.fp, "%s", buf ); + fflush( logfile.fp ); + } //end if +} //end of the function Log_Print +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Write( char *fmt, ... ) { + va_list ap; + char buf[2048]; + + if ( !logfile.fp ) { + return; + } + va_start( ap, fmt ); + Q_vsnprintf( buf, sizeof( buf ), fmt, ap ); + va_end( ap ); + Log_UnifyEndOfLine( buf ); + fprintf( logfile.fp, "%s", buf ); + fflush( logfile.fp ); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void 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 ); + logfile.numwrites++; + fflush( logfile.fp ); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +FILE *Log_FileStruct( void ) { + return logfile.fp; +} //end of the function Log_FileStruct +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Flush( void ) { + if ( logfile.fp ) { + fflush( logfile.fp ); + } +} //end of the function Log_Flush diff --git a/src/bspc/l_log.h b/src/bspc/l_log.h new file mode 100644 index 0000000..112743e --- /dev/null +++ b/src/bspc/l_log.h @@ -0,0 +1,57 @@ +/* +=========================================================================== + +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_log.h +// Function: log file stuff +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +//open a log file +void Log_Open( char *filename ); +//close the current log file +void Log_Close( void ); +//close log file if present +void Log_Shutdown( void ); +//print on stdout and write to the current opened log file +void Log_Print( char *fmt, ... ); +//write to the current opened log file +void Log_Write( char *fmt, ... ); +//write to the current opened log file with a time stamp +void Log_WriteTimeStamped( char *fmt, ... ); +//returns the log file structure +FILE *Log_FileStruct( void ); +//flush log file +void Log_Flush( void ); + +#ifdef WINBSPC +void WinBSPCPrint( char *str ); +#endif //WINBSPC diff --git a/src/bspc/l_math.c b/src/bspc/l_math.c new file mode 100644 index 0000000..503ce7e --- /dev/null +++ b/src/bspc/l_math.c @@ -0,0 +1,302 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// mathlib.c -- math primitives + +#include "l_cmd.h" +#include "l_math.h" + +vec3_t vec3_origin = {0,0,0}; + +void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up ) { + float angle; + static float sr, sp, sy, cr, cp, cy; + // static to help MS compiler fp bugs + + angle = angles[YAW] * ( M_PI * 2 / 360 ); + sy = sin( angle ); + cy = cos( angle ); + angle = angles[PITCH] * ( M_PI * 2 / 360 ); + sp = sin( angle ); + cp = cos( angle ); + angle = angles[ROLL] * ( M_PI * 2 / 360 ); + sr = sin( angle ); + cr = cos( angle ); + + if ( forward ) { + forward[0] = cp * cy; + forward[1] = cp * sy; + forward[2] = -sp; + } + if ( right ) { + right[0] = ( -1 * sr * sp * cy + - 1 * cr * -sy ); + right[1] = ( -1 * sr * sp * sy + - 1 * cr * cy ); + right[2] = -1 * sr * cp; + } + if ( up ) { + up[0] = ( cr * sp * cy + - sr * -sy ); + up[1] = ( cr * sp * sy + - sr * cy ); + up[2] = cr * cp; + } +} + +/* +================ +MakeNormalVectors + +Given a normalized forward vector, create two +other perpendicular vectors +================ +*/ +void MakeNormalVectors( const vec3_t forward, vec3_t right, vec3_t up ) { + float d; + + // this rotate and negate guarantees a vector + // not colinear with the original + right[1] = -forward[0]; + right[2] = forward[1]; + right[0] = forward[2]; + + d = DotProduct( right, forward ); + VectorMA( right, -d, forward, right ); + VectorNormalize( right ); + CrossProduct( right, forward, up ); +} + +/* +================= +RadiusFromBounds +================= +*/ +float RadiusFromBounds( const vec3_t mins, const vec3_t maxs ) { + int i; + vec3_t corner; + float a, b; + + for ( i = 0 ; i < 3 ; i++ ) { + a = fabs( mins[i] ); + b = fabs( maxs[i] ); + corner[i] = a > b ? a : b; + } + + return VectorLength( corner ); +} + +/* +================ +R_ConcatRotations +================ +*/ +void R_ConcatRotations( float in1[3][3], float in2[3][3], float out[3][3] ) { + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + +void AxisClear( vec3_t axis[3] ) { + axis[0][0] = 1; + axis[0][1] = 0; + axis[0][2] = 0; + axis[1][0] = 0; + axis[1][1] = 1; + axis[1][2] = 0; + axis[2][0] = 0; + axis[2][1] = 0; + axis[2][2] = 1; +} + +double VectorLength( vec3_t v ) { + int i; + double length; + + length = 0; + for ( i = 0 ; i < 3 ; i++ ) + length += v[i] * v[i]; + length = sqrt( length ); // FIXME + + return length; +} + +float VectorLengthSquared( vec3_t v ) { + return DotProduct( v, v ); +} + +qboolean VectorCompare( vec3_t v1, vec3_t v2 ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) + if ( fabs( v1[i] - v2[i] ) > EQUAL_EPSILON ) { + return false; + } + + return true; +} + +vec_t Q_rint( vec_t in ) { + return floor( in + 0.5 ); +} + +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ) { + cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; + cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; + cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; +} + +void _VectorMA( vec3_t va, double scale, vec3_t vb, vec3_t vc ) { + vc[0] = va[0] + scale * vb[0]; + vc[1] = va[1] + scale * vb[1]; + vc[2] = va[2] + scale * vb[2]; +} + +vec_t _DotProduct( vec3_t v1, vec3_t v2 ) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +void _VectorSubtract( vec3_t va, vec3_t vb, vec3_t out ) { + out[0] = va[0] - vb[0]; + out[1] = va[1] - vb[1]; + out[2] = va[2] - vb[2]; +} + +void _VectorAdd( vec3_t va, vec3_t vb, vec3_t out ) { + out[0] = va[0] + vb[0]; + out[1] = va[1] + vb[1]; + out[2] = va[2] + vb[2]; +} + +void _VectorCopy( vec3_t in, vec3_t out ) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +void _VectorScale( vec3_t v, vec_t scale, vec3_t out ) { + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; +} + +vec_t VectorNormalize( vec3_t inout ) { + vec_t length, ilength; + + length = sqrt( inout[0] * inout[0] + inout[1] * inout[1] + inout[2] * inout[2] ); + if ( length == 0 ) { + VectorClear( inout ); + return 0; + } + + ilength = 1.0 / length; + inout[0] = inout[0] * ilength; + inout[1] = inout[1] * ilength; + inout[2] = inout[2] * ilength; + + return length; +} + +vec_t VectorNormalize2( const vec3_t in, vec3_t out ) { + vec_t length, ilength; + + length = sqrt( in[0] * in[0] + in[1] * in[1] + in[2] * in[2] ); + if ( length == 0 ) { + VectorClear( out ); + return 0; + } + + ilength = 1.0 / length; + out[0] = in[0] * ilength; + out[1] = in[1] * ilength; + out[2] = in[2] * ilength; + + return length; +} + +vec_t ColorNormalize( vec3_t in, vec3_t out ) { + float max, scale; + + max = in[0]; + if ( in[1] > max ) { + max = in[1]; + } + if ( in[2] > max ) { + max = in[2]; + } + + if ( max == 0 ) { + return 0; + } + + scale = 1.0 / max; + + VectorScale( in, scale, out ); + + return max; +} + + + +void VectorInverse( vec3_t v ) { + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +void ClearBounds( vec3_t mins, vec3_t maxs ) { + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; +} + +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ) { + int i; + vec_t val; + + for ( i = 0 ; i < 3 ; i++ ) + { + val = v[i]; + if ( val < mins[i] ) { + mins[i] = val; + } + if ( val > maxs[i] ) { + maxs[i] = val; + } + } +} diff --git a/src/bspc/l_math.h b/src/bspc/l_math.h new file mode 100644 index 0000000..1290330 --- /dev/null +++ b/src/bspc/l_math.h @@ -0,0 +1,100 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include + +#ifdef DOUBLEVEC_T +typedef double vec_t; +#else +typedef float vec_t; +#endif +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +#define Q_PI 3.14159265358979323846 + +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +extern vec3_t vec3_origin; + +#define EQUAL_EPSILON 0.001 + +qboolean VectorCompare( vec3_t v1, vec3_t v2 ); + +#define DotProduct( x,y ) ( x[0] * y[0] + x[1] * y[1] + x[2] * y[2] ) +#define VectorSubtract( a,b,c ) {c[0] = a[0] - b[0]; c[1] = a[1] - b[1]; c[2] = a[2] - b[2];} +#define VectorAdd( a,b,c ) {c[0] = a[0] + b[0]; c[1] = a[1] + b[1]; c[2] = a[2] + b[2];} +#define VectorCopy( a,b ) {b[0] = a[0]; b[1] = a[1]; b[2] = a[2];} +#define Vector4Copy( a,b ) {b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];} +#define VectorScale( v, s, o ) ( ( o )[0] = ( v )[0] * ( s ),( o )[1] = ( v )[1] * ( s ),( o )[2] = ( v )[2] * ( s ) ) +#define VectorClear( x ) {x[0] = x[1] = x[2] = 0;} +#define VectorNegate( x, y ) {y[0] = -x[0]; y[1] = -x[1]; y[2] = -x[2];} +#define VectorMA( v, s, b, o ) ( ( o )[0] = ( v )[0] + ( b )[0] * ( s ),( o )[1] = ( v )[1] + ( b )[1] * ( s ),( o )[2] = ( v )[2] + ( b )[2] * ( s ) ) + +vec_t Q_rint( vec_t in ); +vec_t _DotProduct( vec3_t v1, vec3_t v2 ); +void _VectorSubtract( vec3_t va, vec3_t vb, vec3_t out ); +void _VectorAdd( vec3_t va, vec3_t vb, vec3_t out ); +void _VectorCopy( vec3_t in, vec3_t out ); +void _VectorScale( vec3_t v, vec_t scale, vec3_t out ); +void _VectorMA( vec3_t va, double scale, vec3_t vb, vec3_t vc ); + +double VectorLength( vec3_t v ); +void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ); +vec_t VectorNormalize( vec3_t inout ); +vec_t ColorNormalize( vec3_t in, vec3_t out ); +vec_t VectorNormalize2( const vec3_t v, vec3_t out ); +void VectorInverse( vec3_t v ); + +void ClearBounds( vec3_t mins, vec3_t maxs ); +void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs ); + +void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up ); +void R_ConcatRotations( float in1[3][3], float in2[3][3], float out[3][3] ); +void RotatePoint( vec3_t point, float matrix[3][3] ); +void CreateRotationMatrix( vec3_t angles, float matrix[3][3] ); + +#endif diff --git a/src/bspc/l_mem.c b/src/bspc/l_mem.c new file mode 100644 index 0000000..1c998cc --- /dev/null +++ b/src/bspc/l_mem.c @@ -0,0 +1,468 @@ +/* +=========================================================================== + +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_mem.c +// Function: +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1999-06-02 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_log.h" + +int allocedmemory; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemorySize( unsigned long size ) { + unsigned long number1, number2, number3; + number1 = size >> 20; + number2 = ( size & 0xFFFFF ) >> 10; + number3 = ( size & 0x3FF ); + if ( number1 ) { + Log_Print( "%ld MB", number1 ); + } + if ( number1 && number2 ) { + Log_Print( " and " ); + } + if ( number2 ) { + Log_Print( "%ld KB", number2 ); + } + if ( number2 && number3 ) { + Log_Print( " and " ); + } + if ( number3 ) { + Log_Print( "%ld bytes", number3 ); + } +} //end of the function PrintFileSize + +#ifndef MEMDEBUG +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MemorySize( void *ptr ) { +#if defined( WIN32 ) || defined( _WIN32 ) + #ifdef __WATCOMC__ + //Intel 32 bits memory addressing, 16 bytes aligned + return ( _msize( ptr ) + 15 ) >> 4 << 4; + #else + return _msize( ptr ); + #endif +#else + return 0; +#endif +} //end of the function MemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *GetClearedMemory( int size ) { + void *ptr; + + ptr = (void *) malloc( size ); + if ( !ptr ) { + Error( "out of memory" ); + } + memset( ptr, 0, size ); + allocedmemory += MemorySize( ptr ); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *GetMemory( unsigned long size ) { + void *ptr; + ptr = malloc( size ); + if ( !ptr ) { + Error( "out of memory" ); + } + allocedmemory += MemorySize( ptr ); + return ptr; +} //end of the function GetMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int fmemsize; +void FreeMemory( void *ptr ) { + // RF, modified this for better memory trash testing + fmemsize = MemorySize( ptr ); + allocedmemory -= fmemsize; + + // RF, somehow this crashes windows if size is less than or equal 8 + if ( fmemsize <= 8 ) { + return; + } + + // RF, set this memory to something that will cause problems if accessed again + memset( ptr, 0xAA, fmemsize ); + + free( ptr ); +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int TotalAllocatedMemory( void ) { + return allocedmemory; +} //end of the function TotalAllocatedMemory + +#else + +#define MEM_ID 0x12345678l + +int totalmemorysize; +int numblocks; + +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 = new char[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 ); + totalmemorysize += block->size; + 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 GetClearedMemoryLabelled +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *GetClearedHunkMemory( unsigned long size ) { + return GetClearedMemory( size ); +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *GetHunkMemory( unsigned long size ) { + return GetMemory( size ); +} //end of the function GetHunkMemory +//=========================================================================== +// +// 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; + Error( "%s: NULL pointer\n", str ); +#endif MEMDEBUG + return NULL; + } //end if + block = ( memoryblock_t * )( (char *) ptr - sizeof( memoryblock_t ) ); + if ( block->id != MEM_ID ) { + Error( "%s: invalid memory block\n", str ); + } //end if + if ( block->ptr != ptr ) { + + Error( "%s: memory block pointer invalid\n", str ); + } //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 ); + totalmemorysize -= block->size; + numblocks--; + // + delete[] block; +} //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: - +//=========================================================================== +int MemorySize( void *ptr ) { + return MemoryByteSize( ptr ); +} //end of the function MemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize( void ) { + printf( "total botlib memory: %d KB\n", totalmemorysize >> 10 ); + printf( "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; + for ( block = memory; block; block = block->next ) + { +#ifdef MEMDEBUG + Log_Write( "%6d, %p, %8d: %24s line %6d: %s", i, block->ptr, block->size, block->file, block->line, block->label ); +#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; +} //end of the function DumpMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int TotalAllocatedMemory( void ) { + return totalmemorysize; +} //end of the function TotalAllocatedMemory +#endif + +//=========================================================================== +// Q3 Hunk and Z_ memory management +//=========================================================================== + +typedef struct memhunk_s +{ + void *ptr; + struct memhunk_s *next; +} memhunk_t; + +memhunk_t *memhunk_high; +memhunk_t *memhunk_low; +int memhunk_high_size = 16 * 1024 * 1024; +int memhunk_low_size = 0; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Hunk_ClearHigh( void ) { + memhunk_t *h, *nexth; + + for ( h = memhunk_high; h; h = nexth ) + { + nexth = h->next; + FreeMemory( h ); + } //end for + memhunk_high = NULL; + memhunk_high_size = 16 * 1024 * 1024; +} //end of the function Hunk_ClearHigh +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *Hunk_Alloc( int size ) { + memhunk_t *h; + + if ( !size ) { + return (void *) memhunk_high_size; + } + // + h = GetClearedMemory( size + sizeof( memhunk_t ) ); + h->ptr = (char *) h + sizeof( memhunk_t ); + h->next = memhunk_high; + memhunk_high = h; + memhunk_high_size -= size; + return h->ptr; +} //end of the function Hunk_Alloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void *Z_Malloc( int size ) { + return GetClearedMemory( size ); +} //end of the function Z_Malloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Z_Free( void *ptr ) { + FreeMemory( ptr ); +} //end of the function Z_Free diff --git a/src/bspc/l_mem.h b/src/bspc/l_mem.h new file mode 100644 index 0000000..d14b851 --- /dev/null +++ b/src/bspc/l_mem.h @@ -0,0 +1,58 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +//============================================================================= + +// memory.h +//#define MEMDEBUG +#undef MEMDEBUG + +#ifndef MEMDEBUG + +void *GetClearedMemory( int size ); +void *GetMemory( unsigned long size ); + +#else + +#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 ); +// +void PrintMemoryLabels( void ); +#endif //MEMDEBUG + +void FreeMemory( void *ptr ); +int MemorySize( void *ptr ); +void PrintMemorySize( unsigned long size ); +int TotalAllocatedMemory( void ); + diff --git a/src/bspc/l_poly.c b/src/bspc/l_poly.c new file mode 100644 index 0000000..a4c6494 --- /dev/null +++ b/src/bspc/l_poly.c @@ -0,0 +1,1449 @@ +/* +=========================================================================== + +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_poly.c +// Function: +// Programmer: id Sofware +// Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +#include +#include "l_cmd.h" +#include "l_math.h" +#include "l_poly.h" +#include "l_log.h" +#include "l_mem.h" + +//#define BOGUS_RANGE 8192 +#define BOGUS_RANGE ( 128 * 1024 ) + +extern int numthreads; + +// counters are only bumped when running single threaded, +// because they are an awefull coherence problem +int c_active_windings; +int c_peak_windings; +int c_winding_allocs; +int c_winding_points; +int c_windingmemory; +int c_peak_windingmemory; + +char windingerror[1024]; + +void pw( winding_t *w ) { + int i; + for ( i = 0 ; i < w->numpoints ; i++ ) + printf( "(%5.3f, %5.3f, %5.3f)\n",w->p[i][0], w->p[i][1],w->p[i][2] ); +} + + +void ResetWindings( void ) { + c_active_windings = 0; + c_peak_windings = 0; + c_winding_allocs = 0; + c_winding_points = 0; + c_windingmemory = 0; + c_peak_windingmemory = 0; + + strcpy( windingerror, "" ); +} //end of the function ResetWindings +/* +============= +AllocWinding +============= +*/ +winding_t *AllocWinding( int points ) { + winding_t *w; + int s; + + s = sizeof( vec_t ) * 3 * points + sizeof( int ); + w = GetMemory( s ); + memset( w, 0, s ); + + if ( numthreads == 1 ) { + c_winding_allocs++; + c_winding_points += points; + c_active_windings++; + if ( c_active_windings > c_peak_windings ) { + c_peak_windings = c_active_windings; + } + c_windingmemory += MemorySize( w ); + if ( c_windingmemory > c_peak_windingmemory ) { + c_peak_windingmemory = c_windingmemory; + } + } //end if + return w; +} //end of the function AllocWinding + +void FreeWinding( winding_t *w ) { + if ( *(unsigned *)w == 0xdeaddead ) { + Error( "FreeWinding: freed a freed winding" ); + } + + if ( numthreads == 1 ) { + c_active_windings--; + c_windingmemory -= MemorySize( w ); + } //end if + + *(unsigned *)w = 0xdeaddead; + + FreeMemory( w ); +} //end of the function FreeWinding + +int WindingMemory( void ) { + return c_windingmemory; +} //end of the function WindingMemory + +int WindingPeakMemory( void ) { + return c_peak_windingmemory; +} //end of the function WindingPeakMemory + +int ActiveWindings( void ) { + return c_active_windings; +} //end of the function ActiveWindings +/* +============ +RemoveColinearPoints +============ +*/ +int c_removed; + +void RemoveColinearPoints( winding_t *w ) { + int i, j, k; + vec3_t v1, v2; + int nump; + vec3_t p[MAX_POINTS_ON_WINDING]; + + nump = 0; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + j = ( i + 1 ) % w->numpoints; + k = ( i + w->numpoints - 1 ) % w->numpoints; + VectorSubtract( w->p[j], w->p[i], v1 ); + VectorSubtract( w->p[i], w->p[k], v2 ); + VectorNormalize( v1 ); + VectorNormalize( v2 ); + if ( DotProduct( v1, v2 ) < 0.999 ) { + if ( nump >= MAX_POINTS_ON_WINDING ) { + Error( "RemoveColinearPoints: MAX_POINTS_ON_WINDING" ); + } + VectorCopy( w->p[i], p[nump] ); + nump++; + } + } + + if ( nump == w->numpoints ) { + return; + } + + if ( numthreads == 1 ) { + c_removed += w->numpoints - nump; + } + w->numpoints = nump; + memcpy( w->p, p, nump * sizeof( p[0] ) ); +} + +/* +============ +WindingPlane +============ +*/ +void WindingPlane( winding_t *w, vec3_t normal, vec_t *dist ) { + vec3_t v1, v2; + int i; + + //find two vectors each longer than 0.5 units + for ( i = 0; i < w->numpoints; i++ ) + { + VectorSubtract( w->p[( i + 1 ) % w->numpoints], w->p[i], v1 ); + VectorSubtract( w->p[( i + 2 ) % w->numpoints], w->p[i], v2 ); + if ( VectorLength( v1 ) > 0.5 && VectorLength( v2 ) > 0.5 ) { + break; + } + } //end for + CrossProduct( v2, v1, normal ); + VectorNormalize( normal ); + *dist = DotProduct( w->p[0], normal ); +} //end of the function WindingPlane + +/* +============= +WindingArea +============= +*/ +vec_t WindingArea( winding_t *w ) { + int i; + vec3_t d1, d2, cross; + vec_t total; + + total = 0; + for ( i = 2 ; i < w->numpoints ; i++ ) + { + VectorSubtract( w->p[i - 1], w->p[0], d1 ); + VectorSubtract( w->p[i], w->p[0], d2 ); + CrossProduct( d1, d2, cross ); + total += 0.5 * VectorLength( cross ); + } + return total; +} + +void WindingBounds( winding_t *w, vec3_t mins, vec3_t maxs ) { + vec_t v; + int i,j; + + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; + + for ( i = 0 ; i < w->numpoints ; i++ ) + { + for ( j = 0 ; j < 3 ; j++ ) + { + v = w->p[i][j]; + if ( v < mins[j] ) { + mins[j] = v; + } + if ( v > maxs[j] ) { + maxs[j] = v; + } + } + } +} + +/* +============= +WindingCenter +============= +*/ +void WindingCenter( winding_t *w, vec3_t center ) { + int i; + float scale; + + VectorCopy( vec3_origin, center ); + for ( i = 0 ; i < w->numpoints ; i++ ) + VectorAdd( w->p[i], center, center ); + + scale = 1.0 / w->numpoints; + VectorScale( center, scale, center ); +} + +/* +================= +BaseWindingForPlane +================= +*/ +winding_t *BaseWindingForPlane( vec3_t normal, vec_t dist ) { + int i, x; + vec_t max, v; + vec3_t org, vright, vup; + winding_t *w; + +// find the major axis + + max = -BOGUS_RANGE; + x = -1; + for ( i = 0 ; i < 3; i++ ) + { + v = fabs( normal[i] ); + if ( v > max ) { + x = i; + max = v; + } + } + if ( x == -1 ) { + Error( "BaseWindingForPlane: no axis found" ); + } + + VectorCopy( vec3_origin, vup ); + switch ( x ) + { + case 0: + case 1: + vup[2] = 1; + break; + case 2: + vup[0] = 1; + break; + } + + v = DotProduct( vup, normal ); + VectorMA( vup, -v, normal, vup ); + VectorNormalize( vup ); + + VectorScale( normal, dist, org ); + + CrossProduct( vup, normal, vright ); + + VectorScale( vup, BOGUS_RANGE, vup ); + VectorScale( vright, BOGUS_RANGE, vright ); + +// project a really big axis aligned box onto the plane + w = AllocWinding( 4 ); + + VectorSubtract( org, vright, w->p[0] ); + VectorAdd( w->p[0], vup, w->p[0] ); + + VectorAdd( org, vright, w->p[1] ); + VectorAdd( w->p[1], vup, w->p[1] ); + + VectorAdd( org, vright, w->p[2] ); + VectorSubtract( w->p[2], vup, w->p[2] ); + + VectorSubtract( org, vright, w->p[3] ); + VectorSubtract( w->p[3], vup, w->p[3] ); + + w->numpoints = 4; + + return w; +} + +/* +================== +CopyWinding +================== +*/ +winding_t *CopyWinding( winding_t *w ) { + int size; + winding_t *c; + + c = AllocWinding( w->numpoints ); + size = (int)( (winding_t *)0 )->p[w->numpoints]; + memcpy( c, w, size ); + return c; +} + +/* +================== +ReverseWinding +================== +*/ +winding_t *ReverseWinding( winding_t *w ) { + int i; + winding_t *c; + + c = AllocWinding( w->numpoints ); + for ( i = 0 ; i < w->numpoints ; i++ ) + { + VectorCopy( w->p[w->numpoints - 1 - i], c->p[i] ); + } + c->numpoints = w->numpoints; + return c; +} + + +/* +============= +ClipWindingEpsilon +============= +*/ +void ClipWindingEpsilon( winding_t *in, vec3_t normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back ) { + vec_t dists[MAX_POINTS_ON_WINDING + 4]; + int sides[MAX_POINTS_ON_WINDING + 4]; + int counts[3]; + //MrElusive: DOH can't use statics when unsing multithreading!!! + vec_t dot; // VC 4.2 optimizer bug if not static + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *f, *b; + int maxpts; + + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for ( i = 0 ; i < in->numpoints ; i++ ) + { + dot = DotProduct( in->p[i], normal ); + dot -= dist; + dists[i] = dot; + if ( dot > epsilon ) { + sides[i] = SIDE_FRONT; + } else if ( dot < -epsilon ) { + sides[i] = SIDE_BACK; + } else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *front = *back = NULL; + + if ( !counts[0] ) { + *back = CopyWinding( in ); + return; + } + if ( !counts[1] ) { + *front = CopyWinding( in ); + return; + } + + maxpts = in->numpoints + 4; // cant use counts[0]+2 because + // of fp grouping errors + + *front = f = AllocWinding( maxpts ); + *back = b = AllocWinding( maxpts ); + + for ( i = 0 ; i < in->numpoints ; i++ ) + { + p1 = in->p[i]; + + if ( sides[i] == SIDE_ON ) { + VectorCopy( p1, f->p[f->numpoints] ); + f->numpoints++; + VectorCopy( p1, b->p[b->numpoints] ); + b->numpoints++; + continue; + } + + if ( sides[i] == SIDE_FRONT ) { + VectorCopy( p1, f->p[f->numpoints] ); + f->numpoints++; + } + if ( sides[i] == SIDE_BACK ) { + VectorCopy( p1, b->p[b->numpoints] ); + b->numpoints++; + } + + if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) { + continue; + } + + // generate a split point + p2 = in->p[( i + 1 ) % in->numpoints]; + + dot = dists[i] / ( dists[i] - dists[i + 1] ); + for ( j = 0 ; j < 3 ; j++ ) + { // avoid round off error when possible + if ( normal[j] == 1 ) { + mid[j] = dist; + } else if ( normal[j] == -1 ) { + mid[j] = -dist; + } else { + mid[j] = p1[j] + dot * ( p2[j] - p1[j] ); + } + } + + VectorCopy( mid, f->p[f->numpoints] ); + f->numpoints++; + VectorCopy( mid, b->p[b->numpoints] ); + b->numpoints++; + } + + if ( f->numpoints > maxpts || b->numpoints > maxpts ) { + Error( "ClipWinding: points exceeded estimate" ); + } + if ( f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING ) { + Error( "ClipWinding: MAX_POINTS_ON_WINDING" ); + } +} + + +/* +============= +ChopWindingInPlace +============= +*/ +void ChopWindingInPlace( winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon ) { + winding_t *in; + vec_t dists[MAX_POINTS_ON_WINDING + 4]; + int sides[MAX_POINTS_ON_WINDING + 4]; + int counts[3]; + //MrElusive: DOH can't use statics when unsing multithreading!!! + vec_t dot; // VC 4.2 optimizer bug if not static + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *f; + int maxpts; + + in = *inout; + counts[0] = counts[1] = counts[2] = 0; + +// determine sides for each point + for ( i = 0 ; i < in->numpoints ; i++ ) + { + dot = DotProduct( in->p[i], normal ); + dot -= dist; + dists[i] = dot; + if ( dot > epsilon ) { + sides[i] = SIDE_FRONT; + } else if ( dot < -epsilon ) { + sides[i] = SIDE_BACK; + } else + { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + if ( !counts[0] ) { + FreeWinding( in ); + *inout = NULL; + return; + } + if ( !counts[1] ) { + return; // inout stays the same + + } + maxpts = in->numpoints + 4; // cant use counts[0]+2 because + // of fp grouping errors + + f = AllocWinding( maxpts ); + + for ( i = 0 ; i < in->numpoints ; i++ ) + { + p1 = in->p[i]; + + if ( sides[i] == SIDE_ON ) { + VectorCopy( p1, f->p[f->numpoints] ); + f->numpoints++; + continue; + } + + if ( sides[i] == SIDE_FRONT ) { + VectorCopy( p1, f->p[f->numpoints] ); + f->numpoints++; + } + + if ( sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i] ) { + continue; + } + + // generate a split point + p2 = in->p[( i + 1 ) % in->numpoints]; + + dot = dists[i] / ( dists[i] - dists[i + 1] ); + for ( j = 0 ; j < 3 ; j++ ) + { // avoid round off error when possible + if ( normal[j] == 1 ) { + mid[j] = dist; + } else if ( normal[j] == -1 ) { + mid[j] = -dist; + } else { + mid[j] = p1[j] + dot * ( p2[j] - p1[j] ); + } + } + + VectorCopy( mid, f->p[f->numpoints] ); + f->numpoints++; + } + + if ( f->numpoints > maxpts ) { + Error( "ClipWinding: points exceeded estimate" ); + } + if ( f->numpoints > MAX_POINTS_ON_WINDING ) { + Error( "ClipWinding: MAX_POINTS_ON_WINDING" ); + } + + FreeWinding( in ); + *inout = f; +} + + +/* +================= +ChopWinding + +Returns the fragment of in that is on the front side +of the cliping plane. The original is freed. +================= +*/ +winding_t *ChopWinding( winding_t *in, vec3_t normal, vec_t dist ) { + winding_t *f, *b; + + ClipWindingEpsilon( in, normal, dist, ON_EPSILON, &f, &b ); + FreeWinding( in ); + if ( b ) { + FreeWinding( b ); + } + return f; +} + + +/* +================= +CheckWinding + +================= +*/ +void CheckWinding( winding_t *w ) { + int i, j; + vec_t *p1, *p2; + vec_t d, edgedist; + vec3_t dir, edgenormal, facenormal; + vec_t area; + vec_t facedist; + + if ( w->numpoints < 3 ) { + Error( "CheckWinding: %i points",w->numpoints ); + } + + area = WindingArea( w ); + if ( area < 1 ) { + Error( "CheckWinding: %f area", area ); + } + + WindingPlane( w, facenormal, &facedist ); + + for ( i = 0 ; i < w->numpoints ; i++ ) + { + p1 = w->p[i]; + + for ( j = 0 ; j < 3 ; j++ ) + if ( p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE ) { + Error( "CheckWinding: BUGUS_RANGE: %f",p1[j] ); + } + + j = i + 1 == w->numpoints ? 0 : i + 1; + + // check the point is on the face plane + d = DotProduct( p1, facenormal ) - facedist; + if ( d < -ON_EPSILON || d > ON_EPSILON ) { + Error( "CheckWinding: point off plane" ); + } + + // check the edge isnt degenerate + p2 = w->p[j]; + VectorSubtract( p2, p1, dir ); + + if ( VectorLength( dir ) < ON_EPSILON ) { + Error( "CheckWinding: degenerate edge" ); + } + + CrossProduct( facenormal, dir, edgenormal ); + VectorNormalize( edgenormal ); + edgedist = DotProduct( p1, edgenormal ); + edgedist += ON_EPSILON; + + // all other points must be on front side + for ( j = 0 ; j < w->numpoints ; j++ ) + { + if ( j == i ) { + continue; + } + d = DotProduct( w->p[j], edgenormal ); + if ( d > edgedist ) { + Error( "CheckWinding: non-convex" ); + } + } + } +} + + +/* +============ +WindingOnPlaneSide +============ +*/ +int WindingOnPlaneSide( winding_t *w, vec3_t normal, vec_t dist ) { + qboolean front, back; + int i; + vec_t d; + + front = false; + back = false; + for ( i = 0 ; i < w->numpoints ; i++ ) + { + d = DotProduct( w->p[i], normal ) - dist; + if ( d < -ON_EPSILON ) { + if ( front ) { + return SIDE_CROSS; + } + back = true; + continue; + } + if ( d > ON_EPSILON ) { + if ( back ) { + return SIDE_CROSS; + } + front = true; + continue; + } + } + + if ( back ) { + return SIDE_BACK; + } + if ( front ) { + return SIDE_FRONT; + } + return SIDE_ON; +} + +//#ifdef ME + #define CONTINUOUS_EPSILON 0.005 +//#else +// #define CONTINUOUS_EPSILON 0.001 +//#endif + +/* +============= +TryMergeWinding + +If two polygons share a common edge and the edges that meet at the +common points are both inside the other polygons, merge them + +Returns NULL if the faces couldn't be merged, or the new face. +The originals will NOT be freed. +============= +*/ + +winding_t *TryMergeWinding( winding_t *f1, winding_t *f2, vec3_t planenormal ) { + vec_t *p1, *p2, *p3, *p4, *back; + winding_t *newf; + int i, j, k, l; + vec3_t normal, delta; + vec_t dot; + qboolean keep1, keep2; + + + // + // find a common edge + // + p1 = p2 = NULL; // stop compiler warning + j = 0; // + + for ( i = 0; i < f1->numpoints; i++ ) + { + p1 = f1->p[i]; + p2 = f1->p[( i + 1 ) % f1->numpoints]; + for ( j = 0; j < f2->numpoints; j++ ) + { + p3 = f2->p[j]; + p4 = f2->p[( j + 1 ) % f2->numpoints]; + for ( k = 0; k < 3; k++ ) + { + if ( fabs( p1[k] - p4[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME + break; + } + if ( fabs( p2[k] - p3[k] ) > 0.1 ) { //EQUAL_EPSILON) //ME + break; + } + } //end for + if ( k == 3 ) { + break; + } + } //end for + if ( j < f2->numpoints ) { + break; + } + } //end for + + if ( i == f1->numpoints ) { + return NULL; // no matching edges + + } + // + // check slope of connected lines + // if the slopes are colinear, the point can be removed + // + back = f1->p[( i + f1->numpoints - 1 ) % f1->numpoints]; + VectorSubtract( p1, back, delta ); + CrossProduct( planenormal, delta, normal ); + VectorNormalize( normal ); + + back = f2->p[( j + 2 ) % f2->numpoints]; + VectorSubtract( back, p1, delta ); + dot = DotProduct( delta, normal ); + if ( dot > CONTINUOUS_EPSILON ) { + return NULL; // not a convex polygon + } + keep1 = (qboolean)( dot < -CONTINUOUS_EPSILON ); + + back = f1->p[( i + 2 ) % f1->numpoints]; + VectorSubtract( back, p2, delta ); + CrossProduct( planenormal, delta, normal ); + VectorNormalize( normal ); + + back = f2->p[( j + f2->numpoints - 1 ) % f2->numpoints]; + VectorSubtract( back, p2, delta ); + dot = DotProduct( delta, normal ); + if ( dot > CONTINUOUS_EPSILON ) { + return NULL; // not a convex polygon + } + keep2 = (qboolean)( dot < -CONTINUOUS_EPSILON ); + + // + // build the new polygon + // + newf = AllocWinding( f1->numpoints + f2->numpoints ); + + // copy first polygon + for ( k = ( i + 1 ) % f1->numpoints ; k != i ; k = ( k + 1 ) % f1->numpoints ) + { + if ( k == ( i + 1 ) % f1->numpoints && !keep2 ) { + continue; + } + + VectorCopy( f1->p[k], newf->p[newf->numpoints] ); + newf->numpoints++; + } + + // copy second polygon + for ( l = ( j + 1 ) % f2->numpoints ; l != j ; l = ( l + 1 ) % f2->numpoints ) + { + if ( l == ( j + 1 ) % f2->numpoints && !keep1 ) { + continue; + } + VectorCopy( f2->p[l], newf->p[newf->numpoints] ); + newf->numpoints++; + } + + return newf; +} + +//#ifdef ME +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +winding_t *MergeWindings( winding_t *w1, winding_t *w2, vec3_t planenormal ) { + winding_t *neww; + float dist; + int i, j, n, found, insertafter; + int sides[MAX_POINTS_ON_WINDING + 4]; + vec3_t newp[MAX_POINTS_ON_WINDING + 4]; + int numpoints; + vec3_t edgevec, sepnormal, v; + + RemoveEqualPoints( w1, 0.2 ); + numpoints = w1->numpoints; + memcpy( newp, w1->p, w1->numpoints * sizeof( vec3_t ) ); + // + for ( i = 0; i < w2->numpoints; i++ ) + { + VectorCopy( w2->p[i], v ); + for ( j = 0; j < numpoints; j++ ) + { + VectorSubtract( newp[( j + 1 ) % numpoints], + newp[( j ) % numpoints], edgevec ); + CrossProduct( edgevec, planenormal, sepnormal ); + VectorNormalize( sepnormal ); + if ( VectorLength( sepnormal ) < 0.9 ) { + //remove the point from the new winding + for ( n = j; n < numpoints - 1; n++ ) + { + VectorCopy( newp[n + 1], newp[n] ); + sides[n] = sides[n + 1]; + } //end for + numpoints--; + j--; + Log_Print( "MergeWindings: degenerate edge on winding %f %f %f\n", sepnormal[0], + sepnormal[1], + sepnormal[2] ); + continue; + } //end if + dist = DotProduct( newp[( j ) % numpoints], sepnormal ); + if ( DotProduct( v, sepnormal ) - dist < -0.1 ) { + sides[j] = SIDE_BACK; + } else { sides[j] = SIDE_FRONT;} + } //end for + //remove all unnecesary points + for ( j = 0; j < numpoints; ) + { + if ( sides[j] == SIDE_BACK + && sides[( j + 1 ) % numpoints] == SIDE_BACK ) { + //remove the point from the new winding + for ( n = ( j + 1 ) % numpoints; n < numpoints - 1; n++ ) + { + VectorCopy( newp[n + 1], newp[n] ); + sides[n] = sides[n + 1]; + } //end for + numpoints--; + } //end if + else + { + j++; + } //end else + } //end for + // + found = false; + for ( j = 0; j < numpoints; j++ ) + { + if ( sides[j] == SIDE_FRONT + && sides[( j + 1 ) % numpoints] == SIDE_BACK ) { + if ( found ) { + Log_Print( "Warning: MergeWindings: front to back found twice\n" ); + } + found = true; + } //end if + } //end for + // + for ( j = 0; j < numpoints; j++ ) + { + if ( sides[j] == SIDE_FRONT + && sides[( j + 1 ) % numpoints] == SIDE_BACK ) { + insertafter = ( j + 1 ) % numpoints; + //insert the new point after j+1 + for ( n = numpoints - 1; n > insertafter; n-- ) + { + VectorCopy( newp[n], newp[n + 1] ); + } //end for + numpoints++; + VectorCopy( v, newp[( insertafter + 1 ) % numpoints] ); + break; + } //end if + } //end for + } //end for + neww = AllocWinding( numpoints ); + neww->numpoints = numpoints; + memcpy( neww->p, newp, numpoints * sizeof( vec3_t ) ); + RemoveColinearPoints( neww ); + return neww; +} //end of the function MergeWindings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WindingErrorString( void ) { + return windingerror; +} //end of the function WindingErrorString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WindingError( winding_t *w ) { + int i, j; + vec_t *p1, *p2; + vec_t d, edgedist; + vec3_t dir, edgenormal, facenormal; + vec_t area; + vec_t facedist; + + if ( w->numpoints < 3 ) { + sprintf( windingerror, "winding %i points", w->numpoints ); + return WE_NOTENOUGHPOINTS; + } //end if + + area = WindingArea( w ); + if ( area < 1 ) { + sprintf( windingerror, "winding %f area", area ); + return WE_SMALLAREA; + } //end if + + WindingPlane( w, facenormal, &facedist ); + + for ( i = 0 ; i < w->numpoints ; i++ ) + { + p1 = w->p[i]; + + for ( j = 0 ; j < 3 ; j++ ) + { + if ( p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE ) { + sprintf( windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2] ); + return WE_POINTBOGUSRANGE; + } //end if + } //end for + + j = i + 1 == w->numpoints ? 0 : i + 1; + + // check the point is on the face plane + d = DotProduct( p1, facenormal ) - facedist; + if ( d < -ON_EPSILON || d > ON_EPSILON ) { + sprintf( windingerror, "winding point %d off plane", i ); + return WE_POINTOFFPLANE; + } //end if + + // check the edge isnt degenerate + p2 = w->p[j]; + VectorSubtract( p2, p1, dir ); + + if ( VectorLength( dir ) < ON_EPSILON ) { + sprintf( windingerror, "winding degenerate edge %d-%d", i, j ); + return WE_DEGENERATEEDGE; + } //end if + + CrossProduct( facenormal, dir, edgenormal ); + VectorNormalize( edgenormal ); + edgedist = DotProduct( p1, edgenormal ); + edgedist += ON_EPSILON; + + // all other points must be on front side + for ( j = 0 ; j < w->numpoints ; j++ ) + { + if ( j == i ) { + continue; + } + d = DotProduct( w->p[j], edgenormal ); + if ( d > edgedist ) { + sprintf( windingerror, "winding non-convex" ); + return WE_NONCONVEX; + } //end if + } //end for + } //end for + return WE_NONE; +} //end of the function WindingError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveEqualPoints( winding_t *w, float epsilon ) { + int i, nump; + vec3_t v; + vec3_t p[MAX_POINTS_ON_WINDING]; + + VectorCopy( w->p[0], p[0] ); + nump = 1; + for ( i = 1; i < w->numpoints; i++ ) + { + VectorSubtract( w->p[i], p[nump - 1], v ); + if ( VectorLength( v ) > epsilon ) { + if ( nump >= MAX_POINTS_ON_WINDING ) { + Error( "RemoveColinearPoints: MAX_POINTS_ON_WINDING" ); + } + VectorCopy( w->p[i], p[nump] ); + nump++; + } //end if + } //end for + + if ( nump == w->numpoints ) { + return; + } + + w->numpoints = nump; + memcpy( w->p, p, nump * sizeof( p[0] ) ); +} //end of the function RemoveEqualPoints +//=========================================================================== +// adds the given point to a winding at the given spot +// (for instance when spot is zero then the point is added at position zero) +// the original winding is NOT freed +// +// Parameter: - +// Returns: the new winding with the added point +// Changes Globals: - +//=========================================================================== +winding_t *AddWindingPoint( winding_t *w, vec3_t point, int spot ) { + int i, j; + winding_t *neww; + + if ( spot > w->numpoints ) { + Error( "AddWindingPoint: num > w->numpoints" ); + } //end if + if ( spot < 0 ) { + Error( "AddWindingPoint: num < 0" ); + } //end if + neww = AllocWinding( w->numpoints + 1 ); + neww->numpoints = w->numpoints + 1; + for ( i = 0, j = 0; i < neww->numpoints; i++ ) + { + if ( i == spot ) { + VectorCopy( point, neww->p[i] ); + } //end if + else + { + VectorCopy( w->p[j], neww->p[i] ); + j++; + } //end else + } //end for + return neww; +} //end of the function AddWindingPoint +//=========================================================================== +// the position where the new point should be added in the winding is +// stored in *spot +// +// Parameter: - +// Returns: true if the point is on the winding +// Changes Globals: - +//=========================================================================== +#define MELT_ON_EPSILON 0.2 + +int PointOnWinding( winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot ) { + int i, j; + vec3_t v1, v2; + vec3_t edgenormal, edgevec; + float edgedist, dot; + + *spot = 0; + //the point must be on the winding plane + dot = DotProduct( point, normal ) - dist; + if ( dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON ) { + return false; + } + // + for ( i = 0; i < w->numpoints; i++ ) + { + j = ( i + 1 ) % w->numpoints; + //get a plane orthogonal to the winding plane through the edge + VectorSubtract( w->p[j], w->p[i], edgevec ); + CrossProduct( normal, edgevec, edgenormal ); + VectorNormalize( edgenormal ); + edgedist = DotProduct( edgenormal, w->p[i] ); + //point must be not too far from the plane + dot = DotProduct( point, edgenormal ) - edgedist; + if ( dot < -MELT_ON_EPSILON || dot > MELT_ON_EPSILON ) { + continue; + } + //vector from first point of winding to the point to test + VectorSubtract( point, w->p[i], v1 ); + //vector from second point of winding to the point to test + VectorSubtract( point, w->p[j], v2 ); + //if the length of the vector is not larger than 0.5 units then + //the point is assumend to be the same as one of the winding points + if ( VectorNormalize( v1 ) < 0.5 ) { + return false; + } + if ( VectorNormalize( v2 ) < 0.5 ) { + return false; + } + //point must be between the two winding points + //(the two vectors must be directed towards each other, and on the + //same straight line) + if ( DotProduct( v1, v2 ) < -0.99 ) { + *spot = i + 1; + return true; + } //end if + } //end for + return false; +} //end of the function PointOnWinding +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define NORMAL_EPSILON 0.0001 +#define DIST_EPSILON 0.02 + +int FindPlaneSeperatingWindings( winding_t *w1, winding_t *w2, vec3_t dir, vec3_t normal, float *dist, vec3_t* points ) { + int i, i2, j, j2, n; + int sides1[3], sides2[3]; + float dist1, dist2, dot, diff; + vec3_t normal1, normal2; + vec3_t v1, v2; + + for ( i = 0; i < w1->numpoints; i++ ) { + i2 = ( i + 1 ) % w1->numpoints; + + VectorSubtract( w1->p[i2], w1->p[i], v1 ); + if ( VectorLength( v1 ) < 0.1 ) { + //Log_Write("FindPlaneSeperatingWindings: winding1 with degenerate edge\r\n"); + continue; + } + + CrossProduct( v1, dir, normal1 ); + VectorNormalize( normal1 ); + dist1 = DotProduct( normal1, w1->p[i] ); + + for ( j = 0; j < w2->numpoints; j++ ) { + j2 = ( j + 1 ) % w2->numpoints; + + VectorSubtract( w2->p[j2], w2->p[j], v2 ); + if ( VectorLength( v2 ) < 0.1 ) { + //Log_Write("FindPlaneSeperatingWindings: winding2 with degenerate edge\r\n"); + continue; + } + + CrossProduct( v2, dir, normal2 ); + VectorNormalize( normal2 ); + dist2 = DotProduct( normal2, w2->p[j] ); + + diff = dist1 - dist2; + if ( fabs( diff ) > DIST_EPSILON ) { + dist2 = -dist2; + VectorNegate( normal2, normal2 ); + diff = dist1 - dist2; + if ( fabs( diff ) > DIST_EPSILON ) { + continue; + } + } + + //check if the normal vectors are equal + for ( n = 0; n < 3; n++ ) { + if ( fabs( normal1[n] - normal2[n] ) > NORMAL_EPSILON ) { + break; + } + } + + if ( n != 3 ) { + continue; + } + + //check on which side of the seperating plane the points of + //the first winding are + sides1[0] = sides1[1] = sides1[2] = 0; + for ( n = 0; n < w1->numpoints; n++ ) { + dot = DotProduct( w1->p[n], normal1 ) - dist1; + if ( dot > DIST_EPSILON ) { + sides1[0]++; + } else if ( dot < -DIST_EPSILON ) { + sides1[1]++; + } else { + sides1[2]++; + } + } + + //check on which side of the seperating plane the points of + //the second winding are + sides2[0] = sides2[1] = sides2[2] = 0; + for ( n = 0; n < w2->numpoints; n++ ) { + //used normal1 and dist1 (they are equal to normal2 and dist2) + dot = DotProduct( w2->p[n], normal1 ) - dist1; + if ( dot > DIST_EPSILON ) { + sides2[0]++; + } else if ( dot < -DIST_EPSILON ) { + sides2[1]++; + } else { + sides2[2]++; + } + } + + //if the first winding has points at both sides + if ( sides1[0] && sides1[1] ) { + Log_Write( "FindPlaneSeperatingWindings: winding1 non-convex\r\n" ); + continue; + } + + //if the second winding has points at both sides + if ( sides2[0] && sides2[1] ) { + Log_Write( "FindPlaneSeperatingWindings: winding2 non-convex\r\n" ); + continue; + } + + if ( ( !sides1[0] && !sides1[1] ) || ( !sides2[0] && !sides2[1] ) ) { + //don't use one of the winding planes as the seperating plane + continue; + } + + //the windings must be at different sides of the seperating plane + if ( ( !sides1[0] && !sides2[1] ) || ( !sides1[1] && !sides2[0] ) ) { + VectorCopy( normal1, normal ); + *dist = dist1; + if ( points ) { + VectorCopy( w1->p[i], points[0] ); + VectorCopy( w1->p[i2], points[1] ); + VectorCopy( w2->p[j], points[2] ); + VectorCopy( w2->p[j2], points[3] ); + } + return true; + } + } + } + return false; +} //end of the function FindPlaneSeperatingWindings +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define WCONVEX_EPSILON 0.2 + +int WindingsNonConvex( winding_t *w1, winding_t *w2, + vec3_t normal1, vec3_t normal2, + float dist1, float dist2 ) { + int i; + + if ( !w1 || !w2 ) { + return false; + } + + //check if one of the points of face1 is at the back of the plane of face2 + for ( i = 0; i < w1->numpoints; i++ ) + { + if ( DotProduct( normal2, w1->p[i] ) - dist2 > WCONVEX_EPSILON ) { + return true; + } + } //end for + //check if one of the points of face2 is at the back of the plane of face1 + for ( i = 0; i < w2->numpoints; i++ ) + { + if ( DotProduct( normal1, w2->p[i] ) - dist1 > WCONVEX_EPSILON ) { + return true; + } + } //end for + + return false; +} //end of the function WindingsNonConvex +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +#define VERTEX_EPSILON 0.5 + +qboolean EqualVertexes(vec3_t v1, vec3_t v2) +{ + float diff; + + diff = v1[0] - v2[0]; + if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) + { + diff = v1[1] - v2[1]; + if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) + { + diff = v1[2] - v2[2]; + if (diff > -VERTEX_EPSILON && diff < VERTEX_EPSILON) + { + return true; + } //end if + } //end if + } //end if + return false; +} //end of the function EqualVertexes + +#define CONTINUOUS_EPSILON 0.001 + +winding_t *AAS_MergeWindings(winding_t *w1, winding_t *w2, vec3_t windingnormal) +{ + int n, i, k; + vec3_t normal, delta; + winding_t *winding, *neww; + float dist, dot; + int p1, p2; + int points[2][64]; + int numpoints[2] = {0, 0}; + int newnumpoints; + int keep[2]; + + if (!FindPlaneSeperatingWindings(w1, w2, windingnormal, normal, &dist)) return NULL; + + //for both windings + for (n = 0; n < 2; n++) + { + if (n == 0) winding = w1; + else winding = w2; + //get the points of the winding which are on the seperating plane + for (i = 0; i < winding->numpoints; i++) + { + dot = DotProduct(winding->p[i], normal) - dist; + if (dot > -ON_EPSILON && dot < ON_EPSILON) + { + //don't allow more than 64 points on the seperating plane + if (numpoints[n] >= 64) Error("AAS_MergeWindings: more than 64 points on seperating plane\n"); + points[n][numpoints[n]++] = i; + } //end if + } //end for + //there must be at least two points of each winding on the seperating plane + if (numpoints[n] < 2) return NULL; + } //end for + + //if the first point of winding1 (which is on the seperating plane) is unequal + //to the last point of winding2 (which is on the seperating plane) + if (!EqualVertexes(w1->p[points[0][0]], w2->p[points[1][numpoints[1]-1]])) + { + return NULL; + } //end if + //if the last point of winding1 (which is on the seperating plane) is unequal + //to the first point of winding2 (which is on the seperating plane) + if (!EqualVertexes(w1->p[points[0][numpoints[0]-1]], w2->p[points[1][0]])) + { + return NULL; + } //end if + // + // check slope of connected lines + // if the slopes are colinear, the point can be removed + // + //first point of winding1 which is on the seperating plane + p1 = points[0][0]; + //point before p1 + p2 = (p1 + w1->numpoints - 1) % w1->numpoints; + VectorSubtract(w1->p[p1], w1->p[p2], delta); + CrossProduct(windingnormal, delta, normal); + VectorNormalize(normal, normal); + + //last point of winding2 which is on the seperating plane + p1 = points[1][numpoints[1]-1]; + //point after p1 + p2 = (p1 + 1) % w2->numpoints; + VectorSubtract(w2->p[p2], w2->p[p1], delta); + dot = DotProduct(delta, normal); + if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon + keep[0] = (qboolean)(dot < -CONTINUOUS_EPSILON); + + //first point of winding2 which is on the seperating plane + p1 = points[1][0]; + //point before p1 + p2 = (p1 + w2->numpoints - 1) % w2->numpoints; + VectorSubtract(w2->p[p1], w2->p[p2], delta); + CrossProduct(windingnormal, delta, normal); + VectorNormalize(normal, normal); + + //last point of winding1 which is on the seperating plane + p1 = points[0][numpoints[0]-1]; + //point after p1 + p2 = (p1 + 1) % w1->numpoints; + VectorSubtract(w1->p[p2], w1->p[p1], delta); + dot = DotProduct(delta, normal); + if (dot > CONTINUOUS_EPSILON) return NULL; //merging would create a non-convex polygon + keep[1] = (qboolean)(dot < -CONTINUOUS_EPSILON); + + //number of points on the new winding + newnumpoints = w1->numpoints - numpoints[0] + w2->numpoints - numpoints[1] + 2; + //allocate the winding + neww = AllocWinding(newnumpoints); + neww->numpoints = newnumpoints; + //copy all the points + k = 0; + //for both windings + for (n = 0; n < 2; n++) + { + if (n == 0) winding = w1; + else winding = w2; + //copy the points of the winding starting with the last point on the + //seperating plane and ending before the first point on the seperating plane + for (i = points[n][numpoints[n]-1]; i != points[n][0]; i = (i+1)%winding->numpoints) + { + if (k >= newnumpoints) + { + Log_Print("numpoints[0] = %d\n", numpoints[0]); + Log_Print("numpoints[1] = %d\n", numpoints[1]); + Error("AAS_MergeWindings: k = %d >= newnumpoints = %d\n", k, newnumpoints); + } //end if + VectorCopy(winding->p[i], neww->p[k]); + k++; + } //end for + } //end for + RemoveEqualPoints(neww); + if (!WindingIsOk(neww, 1)) + { + Log_Print("AAS_MergeWindings: winding not ok after merging\n"); + FreeWinding(neww); + return NULL; + } //end if + return neww; +} //end of the function AAS_MergeWindings*/ +//#endif //ME diff --git a/src/bspc/l_poly.h b/src/bspc/l_poly.h new file mode 100644 index 0000000..cb7e336 --- /dev/null +++ b/src/bspc/l_poly.h @@ -0,0 +1,136 @@ +/* +=========================================================================== + +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_poly.h +// Function: +// Programmer: id Sofware +// Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-04 +// Tab Size: 3 +//=========================================================================== + +//a winding gives the bounding points of a convex polygon +typedef struct +{ + int numpoints; + vec3_t p[4]; //variable sized +} winding_t; + +#define MAX_POINTS_ON_WINDING 96 + +//you can define on_epsilon in the makefile as tighter +#ifndef ON_EPSILON +#define ON_EPSILON 0.1 +#endif +//winding errors +#define WE_NONE 0 +#define WE_NOTENOUGHPOINTS 1 +#define WE_SMALLAREA 2 +#define WE_POINTBOGUSRANGE 3 +#define WE_POINTOFFPLANE 4 +#define WE_DEGENERATEEDGE 5 +#define WE_NONCONVEX 6 + +//allocates a winding +winding_t *AllocWinding( int points ); +//returns the area of the winding +vec_t WindingArea( winding_t *w ); +//gives the center of the winding +void WindingCenter( winding_t *w, vec3_t center ); +//clips the given winding to the given plane and gives the front +//and back part of the clipped winding +void ClipWindingEpsilon( winding_t *in, vec3_t normal, vec_t dist, + vec_t epsilon, winding_t **front, winding_t **back ); +//returns the fragment of the given winding that is on the front +//side of the cliping plane. The original is freed. +winding_t *ChopWinding( winding_t *in, vec3_t normal, vec_t dist ); +//returns a copy of the given winding +winding_t *CopyWinding( winding_t *w ); +//returns the reversed winding of the given one +winding_t *ReverseWinding( winding_t *w ); +//returns a base winding for the given plane +winding_t *BaseWindingForPlane( vec3_t normal, vec_t dist ); +//checks the winding for errors +void CheckWinding( winding_t *w ); +//returns the plane normal and dist the winding is in +void WindingPlane( winding_t *w, vec3_t normal, vec_t *dist ); +//removes colinear points from the winding +void RemoveColinearPoints( winding_t *w ); +//returns on which side of the plane the winding is situated +int WindingOnPlaneSide( winding_t *w, vec3_t normal, vec_t dist ); +//frees the winding +void FreeWinding( winding_t *w ); +//gets the bounds of the winding +void WindingBounds( winding_t *w, vec3_t mins, vec3_t maxs ); +//chops the winding with the given plane, the original winding is freed if clipped +void ChopWindingInPlace( winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon ); +//prints the winding points on STDOUT +void pw( winding_t *w ); +//try to merge the two windings which are in the given plane +//the original windings are undisturbed +//the merged winding is returned when merging was possible +//NULL is returned otherwise +winding_t *TryMergeWinding( winding_t *f1, winding_t *f2, vec3_t planenormal ); +//brute force winding merging... creates a convex winding out of +//the two whatsoever +winding_t *MergeWindings( winding_t *w1, winding_t *w2, vec3_t planenormal ); + +//#ifdef ME +void ResetWindings( void ); +//returns the amount of winding memory +int WindingMemory( void ); +int WindingPeakMemory( void ); +int ActiveWindings( void ); +//returns the winding error string +char *WindingErrorString( void ); +//returns one of the WE_ flags when the winding has errors +int WindingError( winding_t *w ); +//removes equal points from the winding +void RemoveEqualPoints( winding_t *w, float epsilon ); +//returns a winding with a point added at the given spot to the +//given winding, original winding is NOT freed +winding_t *AddWindingPoint( winding_t *w, vec3_t point, int spot ); +//returns true if the point is on one of the winding 'edges' +//when the point is on one of the edged the number of the first +//point of the edge is stored in 'spot' +int PointOnWinding( winding_t *w, vec3_t normal, float dist, vec3_t point, int *spot ); +//find a plane seperating the two windings +//true is returned when the windings area adjacent +//the seperating plane normal and distance area stored in 'normal' and 'dist' +//this plane will contain both the piece of common edge of the two windings +//and the vector 'dir' +// Gordon: points returns the 4 points from the two matching edges +int FindPlaneSeperatingWindings( winding_t *w1, winding_t *w2, vec3_t dir, vec3_t normal, float *dist, vec3_t* points ); +// +int WindingsNonConvex( winding_t *w1, winding_t *w2, + vec3_t normal1, vec3_t normal2, + float dist1, float dist2 ); +//#endif //ME + diff --git a/src/bspc/l_qfiles.c b/src/bspc/l_qfiles.c new file mode 100644 index 0000000..68c7ca6 --- /dev/null +++ b/src/bspc/l_qfiles.c @@ -0,0 +1,701 @@ +/* +=========================================================================== + +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_qfiles.h +// Function: - +// Programmer: Mr Elusive +// Last update: 1999-11-29 +// Tab Size: 3 +//=========================================================================== + +#if defined( WIN32 ) | defined( _WIN32 ) +#include +#include +#include +#else +#include +#include +#include +#endif + +#include "qbsp.h" + +//file extensions with their type +typedef struct qfile_exttype_s +{ + char *extension; + int type; +} qfile_exttyp_t; + +qfile_exttyp_t quakefiletypes[] = +{ + {QFILEEXT_UNKNOWN, QFILETYPE_UNKNOWN}, + {QFILEEXT_PAK, QFILETYPE_PAK}, + {QFILEEXT_PK3, QFILETYPE_PK3}, + {QFILEEXT_SIN, QFILETYPE_PAK}, + {QFILEEXT_BSP, QFILETYPE_BSP}, + {QFILEEXT_MAP, QFILETYPE_MAP}, + {QFILEEXT_MDL, QFILETYPE_MDL}, + {QFILEEXT_MD2, QFILETYPE_MD2}, + {QFILEEXT_MD3, QFILETYPE_MD3}, + {QFILEEXT_WAL, QFILETYPE_WAL}, + {QFILEEXT_WAV, QFILETYPE_WAV}, + {QFILEEXT_AAS, QFILETYPE_AAS}, + {NULL, 0} +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int QuakeFileExtensionType( char *extension ) { + int i; + + for ( i = 0; quakefiletypes[i].extension; i++ ) + { + if ( !stricmp( extension, quakefiletypes[i].extension ) ) { + return quakefiletypes[i].type; + } //end if + } //end for + return QFILETYPE_UNKNOWN; +} //end of the function QuakeFileExtensionType +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *QuakeFileTypeExtension( int type ) { + int i; + + for ( i = 0; quakefiletypes[i].extension; i++ ) + { + if ( quakefiletypes[i].type == type ) { + return quakefiletypes[i].extension; + } //end if + } //end for + return QFILEEXT_UNKNOWN; +} //end of the function QuakeFileExtension +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int QuakeFileType( char *filename ) { + char ext[_MAX_PATH] = "."; + + ExtractFileExtension( filename, ext + 1 ); + return QuakeFileExtensionType( ext ); +} //end of the function QuakeFileTypeFromFileName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *StringContains( char *str1, char *str2, int casesensitive ) { + int len, i, j; + + len = strlen( str1 ) - strlen( str2 ); + for ( i = 0; i <= len; i++, str1++ ) + { + 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 str1; + } + } //end for + return NULL; +} //end of the function StringContains +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int FileFilter( char *filter, char *filename, int casesensitive ) { + char buf[1024]; + char *ptr; + int i, found; + + while ( *filter ) + { + if ( *filter == '*' ) { + filter++; + for ( i = 0; *filter; i++ ) + { + if ( *filter == '*' || *filter == '?' ) { + break; + } + buf[i] = *filter; + filter++; + } //end for + buf[i] = '\0'; + if ( strlen( buf ) ) { + ptr = StringContains( filename, buf, casesensitive ); + if ( !ptr ) { + return false; + } + filename = ptr + strlen( buf ); + } //end if + } //end if + else if ( *filter == '?' ) { + filter++; + filename++; + } //end else if + else if ( *filter == '[' && *( filter + 1 ) == '[' ) { + filter++; + } //end if + else if ( *filter == '[' ) { + filter++; + found = false; + while ( *filter && !found ) + { + if ( *filter == ']' && *( filter + 1 ) != ']' ) { + break; + } + if ( *( filter + 1 ) == '-' && *( filter + 2 ) && ( *( filter + 2 ) != ']' || *( filter + 3 ) == ']' ) ) { + if ( casesensitive ) { + if ( *filename >= *filter && *filename <= *( filter + 2 ) ) { + found = true; + } + } //end if + else + { + if ( toupper( *filename ) >= toupper( *filter ) && + toupper( *filename ) <= toupper( *( filter + 2 ) ) ) { + found = true; + } + } //end else + filter += 3; + } //end if + else + { + if ( casesensitive ) { + if ( *filter == *filename ) { + found = true; + } + } //end if + else + { + if ( toupper( *filter ) == toupper( *filename ) ) { + found = true; + } + } //end else + filter++; + } //end else + } //end while + if ( !found ) { + return false; + } + while ( *filter ) + { + if ( *filter == ']' && *( filter + 1 ) != ']' ) { + break; + } + filter++; + } //end while + filter++; + filename++; + } //end else if + else + { + if ( casesensitive ) { + if ( *filter != *filename ) { + return false; + } + } //end if + else + { + if ( toupper( *filter ) != toupper( *filename ) ) { + return false; + } + } //end else + filter++; + filename++; + } //end else + } //end while + return true; +} //end of the function FileFilter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +quakefile_t *FindQuakeFilesInZip( char *zipfile, char *filter ) { + unzFile uf; + int err; + unz_global_info gi; + char filename_inzip[MAX_PATH]; + unz_file_info file_info; + int i; + quakefile_t *qfiles, *lastqf, *qf; + + uf = unzOpen( zipfile ); + err = unzGetGlobalInfo( uf, &gi ); + + if ( err != UNZ_OK ) { + return NULL; + } + + unzGoToFirstFile( uf ); + + qfiles = NULL; + lastqf = NULL; + for ( i = 0; i < gi.number_entry; i++ ) + { + err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof( filename_inzip ), NULL,0,NULL,0 ); + if ( err != UNZ_OK ) { + break; + } + + ConvertPath( filename_inzip ); + if ( FileFilter( filter, filename_inzip, false ) ) { + qf = GetClearedMemory( sizeof( quakefile_t ) ); + if ( !qf ) { + Error( "out of memory" ); + } + memset( qf, 0, sizeof( quakefile_t ) ); + strcpy( qf->pakfile, zipfile ); + strcpy( qf->filename, zipfile ); + strcpy( qf->origname, filename_inzip ); + qf->zipfile = true; + //memcpy( &buildBuffer[i].zipfileinfo, (unz_s*)uf, sizeof(unz_s)); + memcpy( &qf->zipinfo, (unz_s*)uf, sizeof( unz_s ) ); + qf->offset = 0; + qf->length = file_info.uncompressed_size; + qf->type = QuakeFileType( filename_inzip ); + //add the file ot the list + qf->next = NULL; + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; + } //end if + unzGoToNextFile( uf ); + } //end for + + unzClose( uf ); + + return qfiles; +} //end of the function FindQuakeFilesInZip +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +quakefile_t *FindQuakeFilesInPak( char *pakfile, char *filter ) { + FILE *fp; + dpackheader_t packheader; + dsinpackfile_t *packfiles; + dpackfile_t *idpackfiles; + quakefile_t *qfiles, *lastqf, *qf; + int numpackdirs, i; + + qfiles = NULL; + lastqf = NULL; + //open the pak file + fp = fopen( pakfile, "rb" ); + if ( !fp ) { + Warning( "can't open pak file %s", pakfile ); + return NULL; + } //end if + //read pak header, check for valid pak id and seek to the dir entries + if ( ( fread( &packheader, 1, sizeof( dpackheader_t ), fp ) != sizeof( dpackheader_t ) ) + || ( packheader.ident != IDPAKHEADER && packheader.ident != SINPAKHEADER ) + || ( fseek( fp, LittleLong( packheader.dirofs ), SEEK_SET ) ) + ) { + fclose( fp ); + Warning( "invalid pak file %s", pakfile ); + return NULL; + } //end if + //if it is a pak file from id software + if ( packheader.ident == IDPAKHEADER ) { + //number of dir entries in the pak file + numpackdirs = LittleLong( packheader.dirlen ) / sizeof( dpackfile_t ); + idpackfiles = (dpackfile_t *) GetClearedMemory( numpackdirs * sizeof( dpackfile_t ) ); + if ( !idpackfiles ) { + Error( "out of memory" ); + } + //read the dir entry + if ( fread( idpackfiles, sizeof( dpackfile_t ), numpackdirs, fp ) != numpackdirs ) { + fclose( fp ); + FreeMemory( idpackfiles ); + Warning( "can't read the Quake pak file dir entries from %s", pakfile ); + return NULL; + } //end if + fclose( fp ); + //convert to sin pack files + packfiles = (dsinpackfile_t *) GetClearedMemory( numpackdirs * sizeof( dsinpackfile_t ) ); + if ( !packfiles ) { + Error( "out of memory" ); + } + for ( i = 0; i < numpackdirs; i++ ) + { + strcpy( packfiles[i].name, idpackfiles[i].name ); + packfiles[i].filepos = LittleLong( idpackfiles[i].filepos ); + packfiles[i].filelen = LittleLong( idpackfiles[i].filelen ); + } //end for + FreeMemory( idpackfiles ); + } //end if + else //its a Sin pack file + { + //number of dir entries in the pak file + numpackdirs = LittleLong( packheader.dirlen ) / sizeof( dsinpackfile_t ); + packfiles = (dsinpackfile_t *) GetClearedMemory( numpackdirs * sizeof( dsinpackfile_t ) ); + if ( !packfiles ) { + Error( "out of memory" ); + } + //read the dir entry + if ( fread( packfiles, sizeof( dsinpackfile_t ), numpackdirs, fp ) != numpackdirs ) { + fclose( fp ); + FreeMemory( packfiles ); + Warning( "can't read the Sin pak file dir entries from %s", pakfile ); + return NULL; + } //end if + fclose( fp ); + for ( i = 0; i < numpackdirs; i++ ) + { + packfiles[i].filepos = LittleLong( packfiles[i].filepos ); + packfiles[i].filelen = LittleLong( packfiles[i].filelen ); + } //end for + } //end else + // + for ( i = 0; i < numpackdirs; i++ ) + { + ConvertPath( packfiles[i].name ); + if ( FileFilter( filter, packfiles[i].name, false ) ) { + qf = GetClearedMemory( sizeof( quakefile_t ) ); + if ( !qf ) { + Error( "out of memory" ); + } + memset( qf, 0, sizeof( quakefile_t ) ); + strcpy( qf->pakfile, pakfile ); + strcpy( qf->filename, pakfile ); + strcpy( qf->origname, packfiles[i].name ); + qf->zipfile = false; + qf->offset = packfiles[i].filepos; + qf->length = packfiles[i].filelen; + qf->type = QuakeFileType( packfiles[i].name ); + //add the file ot the list + qf->next = NULL; + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; + } //end if + } //end for + FreeMemory( packfiles ); + return qfiles; +} //end of the function FindQuakeFilesInPak +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +quakefile_t *FindQuakeFilesWithPakFilter( char *pakfilter, char *filter ) { +#if defined( WIN32 ) | defined( _WIN32 ) + WIN32_FIND_DATA filedata; + HWND handle; + struct _stat statbuf; +#else + glob_t globbuf; + struct stat statbuf; + int j; +#endif + quakefile_t *qfiles, *lastqf, *qf; + char pakfile[_MAX_PATH], filename[_MAX_PATH], *str; + int done; + + qfiles = NULL; + lastqf = NULL; + if ( pakfilter && strlen( pakfilter ) ) { +#if defined( WIN32 ) | defined( _WIN32 ) + handle = FindFirstFile( pakfilter, &filedata ); + done = ( handle == INVALID_HANDLE_VALUE ); + while ( !done ) + { + _splitpath( pakfilter, pakfile, NULL, NULL, NULL ); + _splitpath( pakfilter, NULL, &pakfile[strlen( pakfile )], NULL, NULL ); + AppendPathSeperator( pakfile, _MAX_PATH ); + strcat( pakfile, filedata.cFileName ); + _stat( pakfile, &statbuf ); +#else + glob( pakfilter, 0, NULL, &globbuf ); + for ( j = 0; j < globbuf.gl_pathc; j++ ) + { + strcpy( pakfile, globbuf.gl_pathv[j] ); + stat( pakfile, &statbuf ); +#endif + //if the file with .pak or .pk3 is a folder + if ( statbuf.st_mode & S_IFDIR ) { + strcpy( filename, pakfilter ); + AppendPathSeperator( filename, _MAX_PATH ); + strcat( filename, filter ); + qf = FindQuakeFilesWithPakFilter( NULL, filename ); + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; + while ( lastqf->next ) lastqf = lastqf->next; + } //end if + else + { +#if defined( WIN32 ) | defined( _WIN32 ) + str = StringContains( pakfile, ".pk3", false ); +#else + str = StringContains( pakfile, ".pk3", true ); +#endif + if ( str && str == pakfile + strlen( pakfile ) - strlen( ".pk3" ) ) { + qf = FindQuakeFilesInZip( pakfile, filter ); + } //end if + else + { + qf = FindQuakeFilesInPak( pakfile, filter ); + } //end else + // + if ( qf ) { + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; + while ( lastqf->next ) lastqf = lastqf->next; + } //end if + } //end else + // +#if defined( WIN32 ) | defined( _WIN32 ) + //find the next file + done = !FindNextFile( handle, &filedata ); + } //end while +#else + } //end for + globfree( &globbuf ); +#endif + } //end if + else + { +#if defined( WIN32 ) | defined( _WIN32 ) + handle = FindFirstFile( filter, &filedata ); + done = ( handle == INVALID_HANDLE_VALUE ); + while ( !done ) + { + _splitpath( filter, filename, NULL, NULL, NULL ); + _splitpath( filter, NULL, &filename[strlen( filename )], NULL, NULL ); + AppendPathSeperator( filename, _MAX_PATH ); + strcat( filename, filedata.cFileName ); +#else + glob( filter, 0, NULL, &globbuf ); + for ( j = 0; j < globbuf.gl_pathc; j++ ) + { + strcpy( filename, globbuf.gl_pathv[j] ); +#endif + // + qf = GetClearedMemory( sizeof( quakefile_t ) ); + if ( !qf ) { + Error( "out of memory" ); + } + memset( qf, 0, sizeof( quakefile_t ) ); + strcpy( qf->pakfile, "" ); + strcpy( qf->filename, filename ); + strcpy( qf->origname, filename ); + qf->offset = 0; + qf->length = 0; + qf->type = QuakeFileType( filename ); + //add the file ot the list + qf->next = NULL; + if ( lastqf ) { + lastqf->next = qf; + } else { qfiles = qf;} + lastqf = qf; +#if defined( WIN32 ) | defined( _WIN32 ) + //find the next file + done = !FindNextFile( handle, &filedata ); + } //end while +#else + } //end for + globfree( &globbuf ); +#endif + } //end else + return qfiles; +} //end of the function FindQuakeFilesWithPakFilter +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +quakefile_t *FindQuakeFiles( char *filter ) { + char *str; + char newfilter[_MAX_PATH]; + char pakfilter[_MAX_PATH]; + char filefilter[_MAX_PATH]; + + strcpy( newfilter, filter ); + ConvertPath( newfilter ); + strcpy( pakfilter, newfilter ); + + str = StringContains( pakfilter, ".pak", false ); + if ( !str ) { + str = StringContains( pakfilter, ".pk3", false ); + } + + if ( str ) { + str += strlen( ".pak" ); + if ( *str ) { + *str++ = '\0'; + while ( *str == '\\' || *str == '/' ) str++; + strcpy( filefilter, str ); + return FindQuakeFilesWithPakFilter( pakfilter, filefilter ); + } //end if + } //end else + return FindQuakeFilesWithPakFilter( NULL, newfilter ); +} //end of the function FindQuakeFiles +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int LoadQuakeFile( quakefile_t *qf, void **bufferptr ) { + FILE *fp; + void *buffer; + int length; + unzFile zf; + + if ( qf->zipfile ) { + //open the zip file + zf = unzOpen( qf->pakfile ); + //set the file pointer + qf->zipinfo.file = ( (unz_s *) zf )->file; + //open the Quake file in the zip file + unzOpenCurrentFile( &qf->zipinfo ); + //allocate memory for the buffer + length = qf->length; + buffer = GetMemory( length + 1 ); + //read the Quake file from the zip file + length = unzReadCurrentFile( &qf->zipinfo, buffer, length ); + //close the Quake file in the zip file + unzCloseCurrentFile( &qf->zipinfo ); + //close the zip file + unzClose( zf ); + + *bufferptr = buffer; + return length; + } //end if + else + { + fp = SafeOpenRead( qf->filename ); + if ( qf->offset ) { + fseek( fp, qf->offset, SEEK_SET ); + } + length = qf->length; + if ( !length ) { + length = Q_filelength( fp ); + } + buffer = GetMemory( length + 1 ); + ( (char *)buffer )[length] = 0; + SafeRead( fp, buffer, length ); + fclose( fp ); + + *bufferptr = buffer; + return length; + } //end else +} //end of the function LoadQuakeFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ReadQuakeFile( quakefile_t *qf, void *buffer, int offset, int length ) { + FILE *fp; + int read; + unzFile zf; + char tmpbuf[1024]; + + if ( qf->zipfile ) { + //open the zip file + zf = unzOpen( qf->pakfile ); + //set the file pointer + qf->zipinfo.file = ( (unz_s *) zf )->file; + //open the Quake file in the zip file + unzOpenCurrentFile( &qf->zipinfo ); + // + while ( offset > 0 ) + { + read = offset; + if ( read > sizeof( tmpbuf ) ) { + read = sizeof( tmpbuf ); + } + unzReadCurrentFile( &qf->zipinfo, tmpbuf, read ); + offset -= read; + } //end while + //read the Quake file from the zip file + length = unzReadCurrentFile( &qf->zipinfo, buffer, length ); + //close the Quake file in the zip file + unzCloseCurrentFile( &qf->zipinfo ); + //close the zip file + unzClose( zf ); + + return length; + } //end if + else + { + fp = SafeOpenRead( qf->filename ); + if ( qf->offset ) { + fseek( fp, qf->offset, SEEK_SET ); + } + if ( offset ) { + fseek( fp, offset, SEEK_CUR ); + } + SafeRead( fp, buffer, length ); + fclose( fp ); + + return length; + } //end else +} //end of the function ReadQuakeFile diff --git a/src/bspc/l_qfiles.h b/src/bspc/l_qfiles.h new file mode 100644 index 0000000..50a1c64 --- /dev/null +++ b/src/bspc/l_qfiles.h @@ -0,0 +1,106 @@ +/* +=========================================================================== + +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_qfiles.h +// Function: - +// Programmer: Mr Elusive +// Last update: 1999-12-01 +// Tab Size: 3 +//=========================================================================== + +#include "../qcommon/unzip.h" + +#define QFILETYPE_UNKNOWN 0x8000 +#define QFILETYPE_PAK 0x0001 +#define QFILETYPE_PK3 0x0002 +#define QFILETYPE_BSP 0x0004 +#define QFILETYPE_MAP 0x0008 +#define QFILETYPE_MDL 0x0010 +#define QFILETYPE_MD2 0x0020 +#define QFILETYPE_MD3 0x0040 +#define QFILETYPE_WAL 0x0080 +#define QFILETYPE_WAV 0x0100 +#define QFILETYPE_AAS 0x4000 + +#define QFILEEXT_UNKNOWN "" +#define QFILEEXT_PAK ".PAK" +#define QFILEEXT_PK3 ".PK3" +#define QFILEEXT_SIN ".SIN" +#define QFILEEXT_BSP ".BSP" +#define QFILEEXT_MAP ".MAP" +#define QFILEEXT_MDL ".MDL" +#define QFILEEXT_MD2 ".MD2" +#define QFILEEXT_MD3 ".MD3" +#define QFILEEXT_WAL ".WAL" +#define QFILEEXT_WAV ".WAV" +#define QFILEEXT_AAS ".AAS" + +//maximum path length +#ifndef _MAX_PATH + #define _MAX_PATH 1024 +#endif + +//for Sin packs +#define MAX_PAK_FILENAME_LENGTH 120 +#define SINPAKHEADER ( ( 'K' << 24 ) + ( 'A' << 16 ) + ( 'P' << 8 ) + 'S' ) + +typedef struct +{ + char name[MAX_PAK_FILENAME_LENGTH]; + int filepos, filelen; +} dsinpackfile_t; + +typedef struct quakefile_s +{ + char pakfile[_MAX_PATH]; + char filename[_MAX_PATH]; + char origname[_MAX_PATH]; + int zipfile; + int type; + int offset; + int length; + unz_s zipinfo; + struct quakefile_s *next; +} quakefile_t; + +//returns the file extension for the given type +char *QuakeFileTypeExtension( int type ); +//returns the file type for the given extension +int QuakeFileExtensionType( char *extension ); +//return the Quake file type for the given file +int QuakeFileType( char *filename ); +//returns true if the filename complies to the filter +int FileFilter( char *filter, char *filename, int casesensitive ); +//find Quake files using the given filter +quakefile_t *FindQuakeFiles( char *filter ); +//load the given Quake file, returns the length of the file +int LoadQuakeFile( quakefile_t *qf, void **bufferptr ); +//read part of a Quake file into the buffer +int ReadQuakeFile( quakefile_t *qf, void *buffer, int offset, int length ); diff --git a/src/bspc/l_threads.c b/src/bspc/l_threads.c new file mode 100644 index 0000000..2ebba71 --- /dev/null +++ b/src/bspc/l_threads.c @@ -0,0 +1,1528 @@ +/* +=========================================================================== + +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_threads.c +// Function: multi-threading +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1999-05-14 +// Tab Size: 3 +//=========================================================================== + +#include "l_cmd.h" +#include "l_threads.h" +#include "l_log.h" +#include "l_mem.h" + +#define MAX_THREADS 64 + +//#define THREAD_DEBUG + +int dispatch; +int workcount; +int oldf; +qboolean pacifier; +qboolean threaded; +void ( *workfunction )( int ); + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetThreadWork( void ) { + int r; + int f; + + ThreadLock(); + + if ( dispatch == workcount ) { + ThreadUnlock(); + return -1; + } + + f = 10 * dispatch / workcount; + if ( f != oldf ) { + oldf = f; + if ( pacifier ) { + printf( "%i...", f ); + } + } //end if + + r = dispatch; + dispatch++; + ThreadUnlock(); + + return r; +} //end of the function GetThreadWork +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadWorkerFunction( int threadnum ) { + int work; + + while ( 1 ) + { + work = GetThreadWork(); + if ( work == -1 ) { + break; + } +//printf ("thread %i, work %i\n", threadnum, work); + workfunction( work ); + } //end while +} //end of the function ThreadWorkerFunction +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOnIndividual( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + if ( numthreads == -1 ) { + ThreadSetDefault(); + } + workfunction = func; + RunThreadsOn( workcnt, showpacifier, ThreadWorkerFunction ); +} //end of the function RunThreadsOnIndividual + + +//=================================================================== +// +// WIN32 +// +//=================================================================== + +#if defined( WIN32 ) || defined( _WIN32 ) + +#define USED + +#include + +typedef struct thread_s +{ + HANDLE handle; + int threadid; + int id; + struct thread_s *next; +} thread_t; + +thread_t *firstthread; +thread_t *lastthread; +int currentnumthreads; +int currentthreadid; + +int numthreads = 1; +CRITICAL_SECTION crit; +HANDLE semaphore; +static int enter; +static int numwaitingthreads = 0; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetDefault( void ) { + SYSTEM_INFO info; + + if ( numthreads == -1 ) { // not set manually + GetSystemInfo( &info ); + numthreads = info.dwNumberOfProcessors; + if ( numthreads < 1 || numthreads > 32 ) { + numthreads = 1; + } + } //end if + qprintf( "%i threads\n", numthreads ); +} //end of the function ThreadSetDefault +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadLock( void ) { + if ( !threaded ) { + Error( "ThreadLock: !threaded" ); + return; + } //end if + EnterCriticalSection( &crit ); + if ( enter ) { + Error( "Recursive ThreadLock\n" ); + } + enter = 1; +} //end of the function ThreadLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadUnlock( void ) { + if ( !threaded ) { + Error( "ThreadUnlock: !threaded" ); + return; + } //end if + if ( !enter ) { + Error( "ThreadUnlock without lock\n" ); + } + enter = 0; + LeaveCriticalSection( &crit ); +} //end of the function ThreadUnlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupLock( void ) { + Log_Print( "Win32 multi-threading\n" ); + InitializeCriticalSection( &crit ); + threaded = true; //Stupid me... forgot this!!! + currentnumthreads = 0; + currentthreadid = 0; +} //end of the function ThreadInitLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownLock( void ) { + DeleteCriticalSection( &crit ); + threaded = false; //Stupid me... forgot this!!! +} //end of the function ThreadShutdownLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupSemaphore( void ) { + semaphore = CreateSemaphore( NULL, 0, 99999999, "bspc" ); +} //end of the function ThreadSetupSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownSemaphore( void ) { +} //end of the function ThreadShutdownSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreWait( void ) { + WaitForSingleObject( semaphore, INFINITE ); +} //end of the function ThreadSemaphoreWait +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreIncrease( int count ) { + ReleaseSemaphore( semaphore, count, NULL ); +} //end of the function ThreadSemaphoreIncrease +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + int threadid[MAX_THREADS]; + HANDLE threadhandle[MAX_THREADS]; + int i; + int start, end; + + Log_Print( "Win32 multi-threading\n" ); + start = I_FloatTime(); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = true; + + if ( numthreads == -1 ) { + ThreadSetDefault(); + } + + if ( numthreads < 1 || numthreads > MAX_THREADS ) { + numthreads = 1; + } + // + // run threads in parallel + // + InitializeCriticalSection( &crit ); + + numwaitingthreads = 0; + + if ( numthreads == 1 ) { // use same thread + func( 0 ); + } //end if + else + { +// printf("starting %d threads\n", numthreads); + for ( i = 0; i < numthreads; i++ ) + { + threadhandle[i] = CreateThread( + NULL, // LPSECURITY_ATTRIBUTES lpsa, + 0, // DWORD cbStack, + (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr, + (LPVOID)i, // LPVOID lpvThreadParm, + 0, // DWORD fdwCreate, + &threadid[i] ); +// printf("started thread %d\n", i); + } //end for + + for ( i = 0; i < numthreads; i++ ) + WaitForSingleObject( threadhandle[i], INFINITE ); + } //end else + DeleteCriticalSection( &crit ); + + threaded = false; + end = I_FloatTime(); + if ( pacifier ) { + printf( " (%i)\n", end - start ); + } +} //end of the function RunThreadsOn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddThread( void ( *func )(int) ) { + thread_t *thread; + + if ( numthreads == 1 ) { + if ( currentnumthreads >= numthreads ) { + return; + } + currentnumthreads++; + func( -1 ); + currentnumthreads--; + } //end if + else + { + ThreadLock(); + if ( currentnumthreads >= numthreads ) { + ThreadUnlock(); + return; + } //end if + //allocate new thread + thread = GetMemory( sizeof( thread_t ) ); + if ( !thread ) { + Error( "can't allocate memory for thread\n" ); + } + + // + thread->threadid = currentthreadid; + thread->handle = CreateThread( + NULL, // LPSECURITY_ATTRIBUTES lpsa, + 0, // DWORD cbStack, + (LPTHREAD_START_ROUTINE)func, // LPTHREAD_START_ROUTINE lpStartAddr, + (LPVOID) thread->threadid, // LPVOID lpvThreadParm, + 0, // DWORD fdwCreate, + &thread->id ); + + //add the thread to the end of the list + thread->next = NULL; + if ( lastthread ) { + lastthread->next = thread; + } else { firstthread = thread;} + lastthread = thread; + // +#ifdef THREAD_DEBUG + qprintf( "added thread with id %d\n", thread->threadid ); +#endif //THREAD_DEBUG + // + currentnumthreads++; + currentthreadid++; + // + ThreadUnlock(); + } //end else +} //end of the function AddThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveThread( int threadid ) { + thread_t *thread, *last; + + //if a single thread + if ( threadid == -1 ) { + return; + } + // + ThreadLock(); + last = NULL; + for ( thread = firstthread; thread; thread = thread->next ) + { + if ( thread->threadid == threadid ) { + if ( last ) { + last->next = thread->next; + } else { firstthread = thread->next;} + if ( !thread->next ) { + lastthread = last; + } + // + FreeMemory( thread ); + currentnumthreads--; +#ifdef THREAD_DEBUG + qprintf( "removed thread with id %d\n", threadid ); +#endif //THREAD_DEBUG + break; + } //end if + last = thread; + } //end if + if ( !thread ) { + Error( "couldn't find thread with id %d", threadid ); + } + ThreadUnlock(); +} //end of the function RemoveThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WaitForAllThreadsFinished( void ) { + HANDLE handle; + + ThreadLock(); + while ( firstthread ) + { + handle = firstthread->handle; + ThreadUnlock(); + + WaitForSingleObject( handle, INFINITE ); + + ThreadLock(); + } //end while + ThreadUnlock(); +} //end of the function WaitForAllThreadsFinished +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetNumThreads( void ) { + return currentnumthreads; +} //end of the function GetNumThreads + +#endif + + +//=================================================================== +// +// OSF1 +// +//=================================================================== + +#if defined( __osf__ ) + +#define USED + +#include + +typedef struct thread_s +{ + pthread_t thread; + int threadid; + int id; + struct thread_s *next; +} thread_t; + +thread_t *firstthread; +thread_t *lastthread; +int currentnumthreads; +int currentthreadid; + +int numthreads = 1; +pthread_mutex_t my_mutex; +pthread_attr_t attrib; +static int enter; +static int numwaitingthreads = 0; + + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetDefault( void ) { + if ( numthreads == -1 ) { // not set manually + numthreads = 1; + } //end if + qprintf( "%i threads\n", numthreads ); +} //end of the function ThreadSetDefault +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadLock( void ) { + if ( !threaded ) { + Error( "ThreadLock: !threaded" ); + return; + } //end if + if ( my_mutex ) { + pthread_mutex_lock( my_mutex ); + } //end if + if ( enter ) { + Error( "Recursive ThreadLock\n" ); + } + enter = 1; +} //end of the function ThreadLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadUnlock( void ) { + if ( !threaded ) { + Error( "ThreadUnlock: !threaded" ); + return; + } //end if + if ( !enter ) { + Error( "ThreadUnlock without lock\n" ); + } + enter = 0; + if ( my_mutex ) { + pthread_mutex_unlock( my_mutex ); + } //end if +} //end of the function ThreadUnlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupLock( void ) { + pthread_mutexattr_t mattrib; + + Log_Print( "pthread multi-threading\n" ); + + if ( !my_mutex ) { + my_mutex = GetMemory( sizeof( *my_mutex ) ); + if ( pthread_mutexattr_create( &mattrib ) == -1 ) { + Error( "pthread_mutex_attr_create failed" ); + } + if ( pthread_mutexattr_setkind_np( &mattrib, MUTEX_FAST_NP ) == -1 ) { + Error( "pthread_mutexattr_setkind_np failed" ); + } + if ( pthread_mutex_init( my_mutex, mattrib ) == -1 ) { + Error( "pthread_mutex_init failed" ); + } + } + + if ( pthread_attr_create( &attrib ) == -1 ) { + Error( "pthread_attr_create failed" ); + } + if ( pthread_attr_setstacksize( &attrib, 0x100000 ) == -1 ) { + Error( "pthread_attr_setstacksize failed" ); + } + + threaded = true; + currentnumthreads = 0; + currentthreadid = 0; +} //end of the function ThreadInitLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownLock( void ) { + threaded = false; +} //end of the function ThreadShutdownLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + int i; + pthread_t work_threads[MAX_THREADS]; + pthread_addr_t status; + pthread_attr_t attrib; + pthread_mutexattr_t mattrib; + int start, end; + + Log_Print( "pthread multi-threading\n" ); + + start = I_FloatTime(); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = true; + + if ( numthreads < 1 || numthreads > MAX_THREADS ) { + numthreads = 1; + } + + if ( pacifier ) { + setbuf( stdout, NULL ); + } + + if ( !my_mutex ) { + my_mutex = GetMemory( sizeof( *my_mutex ) ); + if ( pthread_mutexattr_create( &mattrib ) == -1 ) { + Error( "pthread_mutex_attr_create failed" ); + } + if ( pthread_mutexattr_setkind_np( &mattrib, MUTEX_FAST_NP ) == -1 ) { + Error( "pthread_mutexattr_setkind_np failed" ); + } + if ( pthread_mutex_init( my_mutex, mattrib ) == -1 ) { + Error( "pthread_mutex_init failed" ); + } + } + + if ( pthread_attr_create( &attrib ) == -1 ) { + Error( "pthread_attr_create failed" ); + } + if ( pthread_attr_setstacksize( &attrib, 0x100000 ) == -1 ) { + Error( "pthread_attr_setstacksize failed" ); + } + + for ( i = 0 ; i < numthreads ; i++ ) + { + if ( pthread_create( &work_threads[i], attrib + , (pthread_startroutine_t)func, (pthread_addr_t)i ) == -1 ) { + Error( "pthread_create failed" ); + } + } + + for ( i = 0 ; i < numthreads ; i++ ) + { + if ( pthread_join( work_threads[i], &status ) == -1 ) { + Error( "pthread_join failed" ); + } + } + + threaded = false; + + end = I_FloatTime(); + if ( pacifier ) { + printf( " (%i)\n", end - start ); + } +} //end of the function RunThreadsOn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddThread( void ( *func )(int) ) { + thread_t *thread; + + if ( numthreads == 1 ) { + if ( currentnumthreads >= numthreads ) { + return; + } + currentnumthreads++; + func( -1 ); + currentnumthreads--; + } //end if + else + { + ThreadLock(); + if ( currentnumthreads >= numthreads ) { + ThreadUnlock(); + return; + } //end if + //allocate new thread + thread = GetMemory( sizeof( thread_t ) ); + if ( !thread ) { + Error( "can't allocate memory for thread\n" ); + } + // + thread->threadid = currentthreadid; + + if ( pthread_create( &thread->thread, attrib, (pthread_startroutine_t)func, (pthread_addr_t)thread->threadid ) == -1 ) { + Error( "pthread_create failed" ); + } + + //add the thread to the end of the list + thread->next = NULL; + if ( lastthread ) { + lastthread->next = thread; + } else { firstthread = thread;} + lastthread = thread; + // +#ifdef THREAD_DEBUG + qprintf( "added thread with id %d\n", thread->threadid ); +#endif //THREAD_DEBUG + // + currentnumthreads++; + currentthreadid++; + // + ThreadUnlock(); + } //end else +} //end of the function AddThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveThread( int threadid ) { + thread_t *thread, *last; + + //if a single thread + if ( threadid == -1 ) { + return; + } + // + ThreadLock(); + last = NULL; + for ( thread = firstthread; thread; thread = thread->next ) + { + if ( thread->threadid == threadid ) { + if ( last ) { + last->next = thread->next; + } else { firstthread = thread->next;} + if ( !thread->next ) { + lastthread = last; + } + // + FreeMemory( thread ); + currentnumthreads--; +#ifdef THREAD_DEBUG + qprintf( "removed thread with id %d\n", threadid ); +#endif //THREAD_DEBUG + break; + } //end if + last = thread; + } //end if + if ( !thread ) { + Error( "couldn't find thread with id %d", threadid ); + } + ThreadUnlock(); +} //end of the function RemoveThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WaitForAllThreadsFinished( void ) { + pthread_t *thread; + pthread_addr_t status; + + ThreadLock(); + while ( firstthread ) + { + thread = &firstthread->thread; + ThreadUnlock(); + + if ( pthread_join( *thread, &status ) == -1 ) { + Error( "pthread_join failed" ); + } + + ThreadLock(); + } //end while + ThreadUnlock(); +} //end of the function WaitForAllThreadsFinished +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetNumThreads( void ) { + return currentnumthreads; +} //end of the function GetNumThreads + +#endif + +//=================================================================== +// +// LINUX +// +//=================================================================== + +#if defined( LINUX ) + +#define USED + +#include +#include + +typedef struct thread_s +{ + pthread_t thread; + int threadid; + int id; + struct thread_s *next; +} thread_t; + +thread_t *firstthread; +thread_t *lastthread; +int currentnumthreads; +int currentthreadid; + +int numthreads = 1; +pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_attr_t attrib; +sem_t semaphore; +static int enter; +static int numwaitingthreads = 0; + + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetDefault( void ) { + if ( numthreads == -1 ) { // not set manually + numthreads = 1; + } //end if + qprintf( "%i threads\n", numthreads ); +} //end of the function ThreadSetDefault +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadLock( void ) { + if ( !threaded ) { + Error( "ThreadLock: !threaded" ); + return; + } //end if + pthread_mutex_lock( &my_mutex ); + if ( enter ) { + Error( "Recursive ThreadLock\n" ); + } + enter = 1; +} //end of the function ThreadLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadUnlock( void ) { + if ( !threaded ) { + Error( "ThreadUnlock: !threaded" ); + return; + } //end if + if ( !enter ) { + Error( "ThreadUnlock without lock\n" ); + } + enter = 0; + pthread_mutex_unlock( &my_mutex ); +} //end of the function ThreadUnlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupLock( void ) { + pthread_mutexattr_t mattrib; + + Log_Print( "pthread multi-threading\n" ); + + threaded = true; + currentnumthreads = 0; + currentthreadid = 0; +} //end of the function ThreadInitLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownLock( void ) { + threaded = false; +} //end of the function ThreadShutdownLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupSemaphore( void ) { + sem_init( &semaphore, 0, 0 ); +} //end of the function ThreadSetupSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownSemaphore( void ) { + sem_destroy( &semaphore ); +} //end of the function ThreadShutdownSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreWait( void ) { + sem_wait( &semaphore ); +} //end of the function ThreadSemaphoreWait +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreIncrease( int count ) { + int i; + + for ( i = 0; i < count; i++ ) + { + sem_post( &semaphore ); + } //end for +} //end of the function ThreadSemaphoreIncrease +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + int i; + pthread_t work_threads[MAX_THREADS]; + void *pthread_return; + pthread_attr_t attrib; + pthread_mutexattr_t mattrib; + int start, end; + + Log_Print( "pthread multi-threading\n" ); + + start = I_FloatTime(); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = true; + + if ( numthreads < 1 || numthreads > MAX_THREADS ) { + numthreads = 1; + } + + if ( pacifier ) { + setbuf( stdout, NULL ); + } + + for ( i = 0 ; i < numthreads ; i++ ) + { + if ( pthread_create( &work_threads[i], NULL, (void *)func, (void *)i ) == -1 ) { + Error( "pthread_create failed" ); + } + } + + for ( i = 0 ; i < numthreads ; i++ ) + { + if ( pthread_join( work_threads[i], &pthread_return ) == -1 ) { + Error( "pthread_join failed" ); + } + } + + threaded = false; + + end = I_FloatTime(); + if ( pacifier ) { + printf( " (%i)\n", end - start ); + } +} //end of the function RunThreadsOn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddThread( void ( *func )(int) ) { + thread_t *thread; + + if ( numthreads == 1 ) { + if ( currentnumthreads >= numthreads ) { + return; + } + currentnumthreads++; + func( -1 ); + currentnumthreads--; + } //end if + else + { + ThreadLock(); + if ( currentnumthreads >= numthreads ) { + ThreadUnlock(); + return; + } //end if + //allocate new thread + thread = GetMemory( sizeof( thread_t ) ); + if ( !thread ) { + Error( "can't allocate memory for thread\n" ); + } + // + thread->threadid = currentthreadid; + + if ( pthread_create( &thread->thread, NULL, (void *)func, (void *)thread->threadid ) == -1 ) { + Error( "pthread_create failed" ); + } + + //add the thread to the end of the list + thread->next = NULL; + if ( lastthread ) { + lastthread->next = thread; + } else { firstthread = thread;} + lastthread = thread; + // +#ifdef THREAD_DEBUG + qprintf( "added thread with id %d\n", thread->threadid ); +#endif //THREAD_DEBUG + // + currentnumthreads++; + currentthreadid++; + // + ThreadUnlock(); + } //end else +} //end of the function AddThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveThread( int threadid ) { + thread_t *thread, *last; + + //if a single thread + if ( threadid == -1 ) { + return; + } + // + ThreadLock(); + last = NULL; + for ( thread = firstthread; thread; thread = thread->next ) + { + if ( thread->threadid == threadid ) { + if ( last ) { + last->next = thread->next; + } else { firstthread = thread->next;} + if ( !thread->next ) { + lastthread = last; + } + // + FreeMemory( thread ); + currentnumthreads--; +#ifdef THREAD_DEBUG + qprintf( "removed thread with id %d\n", threadid ); +#endif //THREAD_DEBUG + break; + } //end if + last = thread; + } //end if + if ( !thread ) { + Error( "couldn't find thread with id %d", threadid ); + } + ThreadUnlock(); +} //end of the function RemoveThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WaitForAllThreadsFinished( void ) { + pthread_t *thread; + void *pthread_return; + + ThreadLock(); + while ( firstthread ) + { + thread = &firstthread->thread; + ThreadUnlock(); + + if ( pthread_join( *thread, &pthread_return ) == -1 ) { + Error( "pthread_join failed" ); + } + + ThreadLock(); + } //end while + ThreadUnlock(); +} //end of the function WaitForAllThreadsFinished +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetNumThreads( void ) { + return currentnumthreads; +} //end of the function GetNumThreads + +#endif //LINUX + + +//=================================================================== +// +// IRIX +// +//=================================================================== + +#ifdef _MIPS_ISA + +#define USED + +#include +#include +#include +#include + +typedef struct thread_s +{ + int threadid; + int id; + struct thread_s *next; +} thread_t; + +thread_t *firstthread; +thread_t *lastthread; +int currentnumthreads; +int currentthreadid; + +int numthreads = 1; +static int enter; +static int numwaitingthreads = 0; + +abilock_t lck; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetDefault( void ) { + if ( numthreads == -1 ) { + numthreads = prctl( PR_MAXPPROCS ); + } + printf( "%i threads\n", numthreads ); +//@@ + usconfig( CONF_INITUSERS, numthreads ); +} //end of the function ThreadSetDefault +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadLock( void ) { + spin_lock( &lck ); +} //end of the function ThreadLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadUnlock( void ) { + release_lock( &lck ); +} //end of the function ThreadUnlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupLock( void ) { + init_lock( &lck ); + + Log_Print( "IRIX multi-threading\n" ); + + threaded = true; + currentnumthreads = 0; + currentthreadid = 0; +} //end of the function ThreadInitLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownLock( void ) { + threaded = false; +} //end of the function ThreadShutdownLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + int i; + int pid[MAX_THREADS]; + int start, end; + + start = I_FloatTime(); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + threaded = true; + + if ( numthreads < 1 || numthreads > MAX_THREADS ) { + numthreads = 1; + } + + if ( pacifier ) { + setbuf( stdout, NULL ); + } + + init_lock( &lck ); + + for ( i = 0 ; i < numthreads - 1 ; i++ ) + { + pid[i] = sprocsp( ( void( * ) ( void *, size_t ) )func, PR_SALL, (void *)i + , NULL, 0x100000 ); +// pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i +// , NULL, 0x80000); + if ( pid[i] == -1 ) { + perror( "sproc" ); + Error( "sproc failed" ); + } + } + + func( i ); + + for ( i = 0 ; i < numthreads - 1 ; i++ ) + wait( NULL ); + + threaded = false; + + end = I_FloatTime(); + if ( pacifier ) { + printf( " (%i)\n", end - start ); + } +} //end of the function RunThreadsOn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddThread( void ( *func )(int) ) { + thread_t *thread; + + if ( numthreads == 1 ) { + if ( currentnumthreads >= numthreads ) { + return; + } + currentnumthreads++; + func( -1 ); + currentnumthreads--; + } //end if + else + { + ThreadLock(); + if ( currentnumthreads >= numthreads ) { + ThreadUnlock(); + return; + } //end if + //allocate new thread + thread = GetMemory( sizeof( thread_t ) ); + if ( !thread ) { + Error( "can't allocate memory for thread\n" ); + } + // + thread->threadid = currentthreadid; + + thread->id = sprocsp( ( void( * ) ( void *, size_t ) )func, PR_SALL, (void *)thread->threadid, NULL, 0x100000 ); + if ( thread->id == -1 ) { + perror( "sproc" ); + Error( "sproc failed" ); + } + + //add the thread to the end of the list + thread->next = NULL; + if ( lastthread ) { + lastthread->next = thread; + } else { firstthread = thread;} + lastthread = thread; + // +#ifdef THREAD_DEBUG + qprintf( "added thread with id %d\n", thread->threadid ); +#endif //THREAD_DEBUG + // + currentnumthreads++; + currentthreadid++; + // + ThreadUnlock(); + } //end else +} //end of the function AddThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveThread( int threadid ) { + thread_t *thread, *last; + + //if a single thread + if ( threadid == -1 ) { + return; + } + // + ThreadLock(); + last = NULL; + for ( thread = firstthread; thread; thread = thread->next ) + { + if ( thread->threadid == threadid ) { + if ( last ) { + last->next = thread->next; + } else { firstthread = thread->next;} + if ( !thread->next ) { + lastthread = last; + } + // + FreeMemory( thread ); + currentnumthreads--; +#ifdef THREAD_DEBUG + qprintf( "removed thread with id %d\n", threadid ); +#endif //THREAD_DEBUG + break; + } //end if + last = thread; + } //end if + if ( !thread ) { + Error( "couldn't find thread with id %d", threadid ); + } + ThreadUnlock(); +} //end of the function RemoveThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WaitForAllThreadsFinished( void ) { + ThreadLock(); + while ( firstthread ) + { + ThreadUnlock(); + + //wait (NULL); + + ThreadLock(); + } //end while + ThreadUnlock(); +} //end of the function WaitForAllThreadsFinished +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetNumThreads( void ) { + return currentnumthreads; +} //end of the function GetNumThreads + +#endif //_MIPS_ISA + + +//======================================================================= +// +// SINGLE THREAD +// +//======================================================================= + +#ifndef USED + +int numthreads = 1; +int currentnumthreads = 0; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetDefault( void ) { + numthreads = 1; +} //end of the function ThreadSetDefault +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadLock( void ) { +} //end of the function ThreadLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadUnlock( void ) { +} //end of the function ThreadUnlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupLock( void ) { + Log_Print( "no multi-threading\n" ); +} //end of the function ThreadInitLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownLock( void ) { +} //end of the function ThreadShutdownLock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSetupSemaphore( void ) { +} //end of the function ThreadSetupSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadShutdownSemaphore( void ) { +} //end of the function ThreadShutdownSemaphore +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreWait( void ) { +} //end of the function ThreadSemaphoreWait +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ThreadSemaphoreIncrease( int count ) { +} //end of the function ThreadSemaphoreIncrease +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )(int) ) { + int start, end; + + Log_Print( "no multi-threading\n" ); + dispatch = 0; + workcount = workcnt; + oldf = -1; + pacifier = showpacifier; + start = I_FloatTime(); +#ifdef NeXT + if ( pacifier ) { + setbuf( stdout, NULL ); + } +#endif + func( 0 ); + + end = I_FloatTime(); + if ( pacifier ) { + printf( " (%i)\n", end - start ); + } +} //end of the function RunThreadsOn +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddThread( void ( *func )(int) ) { + if ( currentnumthreads >= numthreads ) { + return; + } + currentnumthreads++; + func( -1 ); + currentnumthreads--; +} //end of the function AddThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemoveThread( int threadid ) { +} //end of the function RemoveThread +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WaitForAllThreadsFinished( void ) { +} //end of the function WaitForAllThreadsFinished +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int GetNumThreads( void ) { + return currentnumthreads; +} //end of the function GetNumThreads + +#endif //USED diff --git a/src/bspc/l_threads.h b/src/bspc/l_threads.h new file mode 100644 index 0000000..eef673f --- /dev/null +++ b/src/bspc/l_threads.h @@ -0,0 +1,52 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +extern int numthreads; + +void ThreadSetDefault( void ); +int GetThreadWork( void ); +void RunThreadsOnIndividual( int workcnt, qboolean showpacifier, void ( *func )( int ) ); +void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ); + +//mutex +void ThreadSetupLock( void ); +void ThreadShutdownLock( void ); +void ThreadLock( void ); +void ThreadUnlock( void ); +//semaphore +void ThreadSetupSemaphore( void ); +void ThreadShutdownSemaphore( void ); +void ThreadSemaphoreWait( void ); +void ThreadSemaphoreIncrease( int count ); +//add/remove threads +void AddThread( void ( *func )( int ) ); +void RemoveThread( int threadid ); +void WaitForAllThreadsFinished( void ); +int GetNumThreads( void ); + diff --git a/src/bspc/l_utils.c b/src/bspc/l_utils.c new file mode 100644 index 0000000..4691fa0 --- /dev/null +++ b/src/bspc/l_utils.c @@ -0,0 +1,262 @@ +/* +=========================================================================== + +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_utils.c +// Function: several utils +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +//#ifndef BOTLIB +//#define BOTLIB +//#endif //BOTLIB + +#ifdef BOTLIB +#include "q_shared.h" +#include "qfiles.h" +#include "botlib.h" +#include "l_log.h" +#include "l_libvar.h" +#include "l_memory.h" +//#include "l_utils.h" +#include "be_interface.h" +#else //BOTLIB +#include "qbsp.h" +#include "l_mem.h" +#endif //BOTLIB + +#ifdef BOTLIB +//======================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//======================================================================== +void Vector2Angles( vec3_t value1, vec3_t angles ) { + float forward; + float yaw, pitch; + + if ( value1[1] == 0 && value1[0] == 0 ) { + yaw = 0; + if ( value1[2] > 0 ) { + pitch = 90; + } else { pitch = 270;} + } //end if + else + { + yaw = (int) ( atan2( value1[1], value1[0] ) * 180 / M_PI ); + if ( yaw < 0 ) { + yaw += 360; + } + + forward = sqrt( value1[0] * value1[0] + value1[1] * value1[1] ); + pitch = (int) ( atan2( value1[2], forward ) * 180 / M_PI ); + if ( pitch < 0 ) { + pitch += 360; + } + } //end else + + angles[PITCH] = -pitch; + angles[YAW] = yaw; + angles[ROLL] = 0; +} //end of the function Vector2Angles +#endif //BOTLIB +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ConvertPath( char *path ) { + while ( *path ) + { + if ( *path == '/' || *path == '\\' ) { + *path = PATHSEPERATOR_CHAR; + } + path++; + } //end while +} //end of the function ConvertPath +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AppendPathSeperator( char *path, int length ) { + int pathlen = strlen( path ); + + if ( strlen( path ) && length - pathlen > 1 && path[pathlen - 1] != '/' && path[pathlen - 1] != '\\' ) { + path[pathlen] = PATHSEPERATOR_CHAR; + path[pathlen + 1] = '\0'; + } //end if +} //end of the function AppenPathSeperator +//=========================================================================== +// returns pointer to file handle +// sets offset to and length of 'filename' in the pak file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean FindFileInPak( char *pakfile, char *filename, foundfile_t *file ) { + FILE *fp; + dpackheader_t packheader; + dpackfile_t *packfiles; + int numdirs, i; + char path[MAX_PATH]; + + //open the pak file + fp = fopen( pakfile, "rb" ); + if ( !fp ) { + return false; + } //end if + //read pak header, check for valid pak id and seek to the dir entries + if ( ( fread( &packheader, 1, sizeof( dpackheader_t ), fp ) != sizeof( dpackheader_t ) ) + || ( packheader.ident != IDPAKHEADER ) + || ( fseek( fp, LittleLong( packheader.dirofs ), SEEK_SET ) ) + ) { + fclose( fp ); + return false; + } //end if + //number of dir entries in the pak file + numdirs = LittleLong( packheader.dirlen ) / sizeof( dpackfile_t ); + packfiles = (dpackfile_t *) GetMemory( numdirs * sizeof( dpackfile_t ) ); + //read the dir entry + if ( fread( packfiles, sizeof( dpackfile_t ), numdirs, fp ) != numdirs ) { + fclose( fp ); + FreeMemory( packfiles ); + return false; + } //end if + fclose( fp ); + // + strcpy( path, filename ); + ConvertPath( path ); + //find the dir entry in the pak file + for ( i = 0; i < numdirs; i++ ) + { + //convert the dir entry name + ConvertPath( packfiles[i].name ); + //compare the dir entry name with the filename + if ( Q_strcasecmp( packfiles[i].name, path ) == 0 ) { + strcpy( file->filename, pakfile ); + file->offset = LittleLong( packfiles[i].filepos ); + file->length = LittleLong( packfiles[i].filelen ); + FreeMemory( packfiles ); + return true; + } //end if + } //end for + FreeMemory( packfiles ); + return false; +} //end of the function FindFileInPak +//=========================================================================== +// find a Quake2 file +// returns full path in 'filename' +// sets offset and length of the file +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean FindQuakeFile2( char *basedir, char *gamedir, char *filename, foundfile_t *file ) { + int dir, i; + //NOTE: 3 is necessary (LCC bug???) + char gamedirs[3][MAX_PATH] = {"","",""}; + char filedir[MAX_PATH] = ""; + + // + if ( gamedir ) { + strncpy( gamedirs[0], gamedir, MAX_PATH ); + } + strncpy( gamedirs[1], "baseq2", MAX_PATH ); + // + //find the file in the two game directories + for ( dir = 0; dir < 2; dir++ ) + { + //check if the file is in a directory + filedir[0] = 0; + if ( basedir && strlen( basedir ) ) { + strncpy( filedir, basedir, MAX_PATH ); + AppendPathSeperator( filedir, MAX_PATH ); + } //end if + if ( strlen( gamedirs[dir] ) ) { + strncat( filedir, gamedirs[dir], MAX_PATH - strlen( filedir ) ); + AppendPathSeperator( filedir, MAX_PATH ); + } //end if + strncat( filedir, filename, MAX_PATH - strlen( filedir ) ); + ConvertPath( filedir ); + Log_Write( "accessing %s", filedir ); + if ( !access( filedir, 0x04 ) ) { + strcpy( file->filename, filedir ); + file->length = 0; + file->offset = 0; + return true; + } //end if + //check if the file is in a pak?.pak + for ( i = 0; i < 10; i++ ) + { + filedir[0] = 0; + if ( basedir && strlen( basedir ) ) { + strncpy( filedir, basedir, MAX_PATH ); + AppendPathSeperator( filedir, MAX_PATH ); + } //end if + if ( strlen( gamedirs[dir] ) ) { + strncat( filedir, gamedirs[dir], MAX_PATH - strlen( filedir ) ); + AppendPathSeperator( filedir, MAX_PATH ); + } //end if + sprintf( &filedir[strlen( filedir )], "pak%d.pak\0", i ); + if ( !access( filedir, 0x04 ) ) { + Log_Write( "searching %s in %s", filename, filedir ); + if ( FindFileInPak( filedir, filename, file ) ) { + return true; + } + } //end if + } //end for + } //end for + file->offset = 0; + file->length = 0; + return false; +} //end of the function FindQuakeFile2 +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef BOTLIB +qboolean FindQuakeFile( char *filename, foundfile_t *file ) { + return FindQuakeFile2( LibVarGetString( "basedir" ), + LibVarGetString( "gamedir" ), filename, file ); +} //end of the function FindQuakeFile +#else //BOTLIB +qboolean FindQuakeFile( char *basedir, char *gamedir, char *filename, foundfile_t *file ) { + return FindQuakeFile2( basedir, gamedir, filename, file ); +} //end of the function FindQuakeFile +#endif //BOTLIB diff --git a/src/bspc/l_utils.h b/src/bspc/l_utils.h new file mode 100644 index 0000000..03a276b --- /dev/null +++ b/src/bspc/l_utils.h @@ -0,0 +1,94 @@ +/* +=========================================================================== + +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_utils.h +// Function: several utils +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +#ifndef MAX_PATH + #define MAX_PATH 64 +#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 + +//random in the range [0, 1] +#define random() ( ( rand() & 0x7fff ) / ( (float)0x7fff ) ) +//random in the range [-1, 1] +#define crandom() ( 2.0 * ( random() - 0.5 ) ) +//min and max +#define Maximum( x,y ) ( x > y ? x : y ) +#define Minimum( x,y ) ( x < y ? x : y ) +//absolute value +#define FloatAbs( x ) ( *(float *) &( ( *(int *) &( x ) ) & 0x7FFFFFFF ) ) +#define IntAbs( x ) ( ~( x ) ) +//coordinates +#define _X 0 +#define _Y 1 +#define _Z 2 + +typedef struct foundfile_s +{ + int offset; + int length; + char filename[MAX_PATH]; //screw LCC, array must be at end of struct +} foundfile_t; + +void Vector2Angles( vec3_t value1, vec3_t angles ); +//set the correct path seperators +void ConvertPath( char *path ); +//append a path seperator to the given path not exceeding the length +void AppendPathSeperator( char *path, int length ); +//find a file in a pak file +qboolean FindFileInPak( char *pakfile, char *filename, foundfile_t *file ); +//find a quake file +#ifdef BOTLIB +qboolean FindQuakeFile( char *filename, foundfile_t *file ); +#else //BOTLIB +qboolean FindQuakeFile( char *basedir, char *gamedir, char *filename, foundfile_t *file ); +#endif //BOTLIB + + + diff --git a/src/bspc/leakfile.c b/src/bspc/leakfile.c new file mode 100644 index 0000000..d0dfd6a --- /dev/null +++ b/src/bspc/leakfile.c @@ -0,0 +1,108 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "qbsp.h" + +/* +============================================================================== + +LEAF FILE GENERATION + +Save out name.line for qe3 to read +============================================================================== +*/ + + +/* +============= +LeakFile + +Finds the shortest possible chain of portals +that leads from the outside leaf to a specifically +occupied leaf +============= +*/ +void LeakFile( tree_t *tree ) { + vec3_t mid; + FILE *linefile; + char filename[1024]; + node_t *node; + int count; + + if ( !tree->outside_node.occupied ) { + return; + } + + qprintf( "--- LeakFile ---\n" ); + + // + // write the points to the file + // + sprintf( filename, "%s.lin", source ); + qprintf( "%s\n", filename ); + linefile = fopen( filename, "w" ); + if ( !linefile ) { + Error( "Couldn't open %s\n", filename ); + } + + count = 0; + node = &tree->outside_node; + while ( node->occupied > 1 ) + { + int next; + portal_t *p, *nextportal; + node_t *nextnode; + int s; + + // find the best portal exit + next = node->occupied; + for ( p = node->portals ; p ; p = p->next[!s] ) + { + s = ( p->nodes[0] == node ); + if ( p->nodes[s]->occupied + && p->nodes[s]->occupied < next ) { + nextportal = p; + nextnode = p->nodes[s]; + next = nextnode->occupied; + } + } + node = nextnode; + WindingCenter( nextportal->winding, mid ); + fprintf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] ); + count++; + } + // add the occupant center + GetVectorForKey( node->occupant, "origin", mid ); + + fprintf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] ); + qprintf( "%5i point linefile\n", count + 1 ); + + fclose( linefile ); +} + diff --git a/src/bspc/map.c b/src/bspc/map.c new file mode 100644 index 0000000..2e5486a --- /dev/null +++ b/src/bspc/map.c @@ -0,0 +1,1505 @@ +/* +=========================================================================== + +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: map.c +// Function: map loading and writing +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-03 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_bsp_q3.h" +#include "l_mem.h" +#include "..\botlib\aasfile.h" //aas_bbox_t +#include "aas_store.h" //AAS_MAX_BBOXES +#include "aas_cfg.h" + +#define Sign( x ) ( x < 0 ? 1 : 0 ) + +int nummapbrushes; +mapbrush_t mapbrushes[MAX_MAPFILE_BRUSHES]; + +int nummapbrushsides; +side_t brushsides[MAX_MAPFILE_BRUSHSIDES]; +brush_texture_t side_brushtextures[MAX_MAPFILE_BRUSHSIDES]; + +int nummapplanes; +plane_t mapplanes[MAX_MAPFILE_PLANES]; +int mapplaneusers[MAX_MAPFILE_PLANES]; + +#define PLANE_HASHES 1024 +plane_t *planehash[PLANE_HASHES]; +vec3_t map_mins, map_maxs; + +#ifdef SIN +textureref_t side_newrefs[MAX_MAPFILE_BRUSHSIDES]; +#endif + +map_texinfo_t map_texinfo[MAX_MAPFILE_TEXINFO]; +int map_numtexinfo; +int loadedmaptype; //loaded map type + +// undefine to make plane finding use linear sort +#define USE_HASHING + +int c_boxbevels; +int c_edgebevels; +int c_areaportals; +int c_clipbrushes; +int c_squattbrushes; +int c_writtenbrushes; + +/* +============================================================================= + +PLANE FINDING + +============================================================================= +*/ + + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int PlaneSignBits( vec3_t normal ) { + int i, signbits; + + signbits = 0; + for ( i = 2; i >= 0; i-- ) + { + signbits = ( signbits << 1 ) + Sign( normal[i] ); + } //end for + return signbits; +} //end of the function PlaneSignBits +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int PlaneTypeForNormal( vec3_t normal ) { + vec_t ax, ay, az; + +// NOTE: should these have an epsilon around 1.0? + if ( normal[0] == 1.0 || normal[0] == -1.0 ) { + return PLANE_X; + } + if ( normal[1] == 1.0 || normal[1] == -1.0 ) { + return PLANE_Y; + } + if ( normal[2] == 1.0 || normal[2] == -1.0 ) { + return PLANE_Z; + } + + ax = fabs( normal[0] ); + ay = fabs( normal[1] ); + az = fabs( normal[2] ); + + if ( ax >= ay && ax >= az ) { + return PLANE_ANYX; + } + if ( ay >= ax && ay >= az ) { + return PLANE_ANYY; + } + return PLANE_ANYZ; +} //end of the function PlaneTypeForNormal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +// MrE: use the same epsilons as q3map! +#define NORMAL_EPSILON 0.00001f +#define DIST_EPSILON 0.01f + +qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ) { +#if 1 + if ( + fabs( p->normal[0] - normal[0] ) < NORMAL_EPSILON + && fabs( p->normal[1] - normal[1] ) < NORMAL_EPSILON + && fabs( p->normal[2] - normal[2] ) < NORMAL_EPSILON + && fabs( p->dist - dist ) < DIST_EPSILON ) { + return true; + } +#else + if ( p->normal[0] == normal[0] + && p->normal[1] == normal[1] + && p->normal[2] == normal[2] + && p->dist == dist ) { + return true; + } +#endif + return false; +} //end of the function PlaneEqual +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddPlaneToHash( plane_t *p ) { + int hash; + + hash = ( PLANE_HASHES - 1 ) & (int)fabs( p->dist ); + + p->hash_chain = planehash[hash]; + planehash[hash] = p; +} //end of the function AddPlaneToHash +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int CreateNewFloatPlane( vec3_t normal, vec_t dist ) { + plane_t *p, temp; + + if ( VectorLength( normal ) < 0.5 ) { + Error( "FloatPlane: bad normal" ); + } + // create a new plane + if ( nummapplanes + 2 > MAX_MAPFILE_PLANES ) { + Error( "MAX_MAPFILE_PLANES" ); + } + + p = &mapplanes[nummapplanes]; + VectorCopy( normal, p->normal ); + p->dist = dist; + p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal ); + p->signbits = PlaneSignBits( p->normal ); + + VectorSubtract( vec3_origin, normal, ( p + 1 )->normal ); + ( p + 1 )->dist = -dist; + ( p + 1 )->signbits = PlaneSignBits( ( p + 1 )->normal ); + + nummapplanes += 2; + + // allways put axial planes facing positive first + if ( p->type < 3 ) { + if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) { + // flip order + temp = *p; + *p = *( p + 1 ); + *( p + 1 ) = temp; + + AddPlaneToHash( p ); + AddPlaneToHash( p + 1 ); + return nummapplanes - 1; + } + } + + AddPlaneToHash( p ); + AddPlaneToHash( p + 1 ); + return nummapplanes - 2; +} //end of the function CreateNewFloatPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SnapVector( vec3_t normal ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) + { + if ( fabs( normal[i] - 1 ) < NORMAL_EPSILON ) { + VectorClear( normal ); + normal[i] = 1; + break; + } + if ( fabs( normal[i] - -1 ) < NORMAL_EPSILON ) { + VectorClear( normal ); + normal[i] = -1; + break; + } + } +} //end of the function SnapVector +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SnapPlane( vec3_t normal, vec_t *dist ) { + SnapVector( normal ); + +/* if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON) + *dist = Q_rint(*dist); +*/ +} //end of the function SnapPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifndef USE_HASHING +int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t* points ) { + int i; + plane_t *p; + + SnapPlane( normal, &dist ); + for ( i = 0, p = mapplanes; i < nummapplanes; i++, p++ ) { + + if ( PlaneEqual( p, normal, dist ) ) { + mapplaneusers[i]++; + return i; + } + } + + i = CreateNewFloatPlane( normal, dist ); + mapplaneusers[i]++; + + return i; +} + +#else +int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t* points ) { + plane_t *p; + int hash, i, j, h; + + SnapPlane( normal, &dist ); + + hash = ( PLANE_HASHES - 1 ) & (int)fabs( dist ); + + // search the border bins as well + for ( i = -1; i <= 1; i++ ) { + + h = ( hash + i ) & ( PLANE_HASHES - 1 ); + for ( p = planehash[h]; p; p = p->hash_chain ) { + if ( !PlaneEqual( p, normal, dist ) ) { + continue; + } + + // Gordon: old styleeee + mapplaneusers[p - mapplanes]++; + return p - mapplanes; + + + for ( j = 0; j < numPoints; j++ ) { + vec_t d = DotProduct( points[j], normal ) - dist; + if ( fabs( d ) > DIST_EPSILON ) { + break; + } + } + + if ( j == numPoints ) { + mapplaneusers[p - mapplanes]++; + return p - mapplanes; + } else { + qprintf( "Not all points on plane matched\n" ); + } + } + } + + i = CreateNewFloatPlane( normal, dist ); + mapplaneusers[i]++; + + return i; +} +#endif +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int PlaneFromPoints( int *p0, int *p1, int *p2 ) { + vec3_t t1, t2, normal; + vec_t dist; + vec3_t points[3]; + + VectorCopy( p0, points[0] ); + VectorCopy( p1, points[1] ); + VectorCopy( p2, points[2] ); + + VectorSubtract( p0, p1, t1 ); + VectorSubtract( p2, p1, t2 ); + CrossProduct( t1, t2, normal ); + VectorNormalize( normal ); + + dist = DotProduct( p0, normal ); + + return FindFloatPlane( normal, dist, 3, points ); +} //end of the function PlaneFromPoints +//=========================================================================== +// Adds any additional planes necessary to allow the brush to be expanded +// against axial bounding boxes +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== + +#ifdef MRE_ET + +void AddBrushBevels( mapbrush_t *b ) { + int axis, dir; + int i, j, k, l, order; + side_t sidetemp; + brush_texture_t tdtemp; +#ifdef SIN + textureref_t trtemp; +#endif + side_t *s, *s2; + vec3_t normal; + float dist; + winding_t *w, *w2; + vec3_t vec, vec2; + float d, minBack; + + // + // add the axial planes + // + order = 0; + for ( axis = 0; axis < 3; axis++ ) { + for ( dir = -1; dir <= 1; dir += 2, order++ ) { + // see if the plane is allready present + for ( i = 0, s = b->original_sides; i < b->numsides; i++, s++ ) { + if ( dir > 0 ) { + if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) { + break; + } + } else { + if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) { + break; + } + } + } + + if ( i == b->numsides ) { + // add a new side + if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + nummapbrushsides++; + b->numsides++; + VectorClear( normal ); + normal[axis] = dir; + if ( dir == 1 ) { + dist = b->maxs[axis]; + } else { + dist = -b->mins[axis]; + } + s->planenum = FindFloatPlane( normal, dist, 0, NULL ); + s->texinfo = b->original_sides[0].texinfo; +#ifdef SIN + s->lightinfo = b->original_sides[0].lightinfo; +#endif + s->contents = b->original_sides[0].contents; + s->flags |= SFL_BEVEL; + c_boxbevels++; + } + + // if the plane is not in it canonical order, swap it + if ( i != order ) { + sidetemp = b->original_sides[order]; + b->original_sides[order] = b->original_sides[i]; + b->original_sides[i] = sidetemp; + + j = b->original_sides - brushsides; + tdtemp = side_brushtextures[j + order]; + side_brushtextures[j + order] = side_brushtextures[j + i]; + side_brushtextures[j + i] = tdtemp; + +#ifdef SIN + trtemp = side_newrefs[j + order]; + side_newrefs[j + order] = side_newrefs[j + i]; + side_newrefs[j + i] = trtemp; +#endif + } + } + } + + // + // add the edge bevels + // + if ( b->numsides == 6 ) { + return; // pure axial + } + + // test the non-axial plane edges + for ( i = 6; i < b->numsides; i++ ) { + s = b->original_sides + i; + w = s->winding; + if ( !w ) { + continue; + } + for ( j = 0; j < w->numpoints; j++ ) { + k = ( j + 1 ) % w->numpoints; + VectorSubtract( w->p[j], w->p[k], vec ); + if ( VectorNormalize( vec ) < 0.5f ) { + continue; + } + SnapVector( vec ); + for ( k = 0; k < 3; k++ ) { + if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) { + break; // axial + } + } + if ( k != 3 ) { + continue; // only test non-axial edges + } + + // try the six possible slanted axials from this edge + for ( axis = 0; axis < 3; axis++ ) { + for ( dir = -1; dir <= 1; dir += 2 ) { + // construct a plane + VectorClear( vec2 ); + vec2[axis] = dir; + CrossProduct( vec, vec2, normal ); + if ( VectorNormalize( normal ) < 0.5f ) { + continue; + } + dist = DotProduct( w->p[j], normal ); + + // if all the points on all the sides are + // behind this plane, it is a proper edge bevel + for ( k = 0; k < b->numsides; k++ ) { + + // if this plane has allready been used, skip it + if ( PlaneEqual( &mapplanes[b->original_sides[k].planenum], normal, dist ) ) { + break; + } + + w2 = b->original_sides[k].winding; + if ( !w2 ) { + continue; + } + minBack = 0.0f; + for ( l = 0; l < w2->numpoints; l++ ) { + d = DotProduct( w2->p[l], normal ) - dist; + if ( d > 0.01f ) { + break; // point in front + } + if ( d < minBack ) { + minBack = d; + } + } + // if some point was at the front + if ( l != w2->numpoints ) { + break; + } + + // if no points at the back then the winding is on the bevel plane + if ( minBack > -0.1f ) { + break; + } + } + + if ( k != b->numsides ) { + continue; // wasn't part of the outer hull + } + // add this plane + if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + nummapbrushsides++; + s2 = &b->original_sides[b->numsides]; + s2->planenum = FindFloatPlane( normal, dist, 0, NULL ); + s2->texinfo = b->original_sides[0].texinfo; +#ifdef SIN + s2->lightinfo = b->original_sides[0].lightinfo; +#endif + s2->contents = b->original_sides[0].contents; + s2->flags |= SFL_BEVEL; + c_edgebevels++; + b->numsides++; + } + } + } + } +} + +#else + +void AddBrushBevels( mapbrush_t *b ) { + int axis, dir; + int i, j, k, l, order; + side_t sidetemp; + brush_texture_t tdtemp; +#ifdef SIN + textureref_t trtemp; +#endif + side_t *s, *s2; + vec3_t normal; + float dist; + winding_t *w, *w2; + vec3_t vec, vec2; + float d; + + // + // add the axial planes + // + order = 0; + for ( axis = 0 ; axis < 3 ; axis++ ) + { + for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) + { + // see if the plane is allready present + for ( i = 0, s = b->original_sides ; i < b->numsides ; i++,s++ ) + { + if ( mapplanes[s->planenum].normal[axis] == dir ) { + break; + } + } + + if ( i == b->numsides ) { // add a new side + if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + nummapbrushsides++; + b->numsides++; + VectorClear( normal ); + normal[axis] = dir; + if ( dir == 1 ) { + dist = b->maxs[axis]; + } else { + dist = -b->mins[axis]; + } + s->planenum = FindFloatPlane( normal, dist, 0, NULL ); + s->texinfo = b->original_sides[0].texinfo; +#ifdef SIN + s->lightinfo = b->original_sides[0].lightinfo; +#endif + s->contents = b->original_sides[0].contents; + s->flags |= SFL_BEVEL; + c_boxbevels++; + } + + // if the plane is not in it canonical order, swap it + if ( i != order ) { + sidetemp = b->original_sides[order]; + b->original_sides[order] = b->original_sides[i]; + b->original_sides[i] = sidetemp; + + j = b->original_sides - brushsides; + tdtemp = side_brushtextures[j + order]; + side_brushtextures[j + order] = side_brushtextures[j + i]; + side_brushtextures[j + i] = tdtemp; + +#ifdef SIN + trtemp = side_newrefs[j + order]; + side_newrefs[j + order] = side_newrefs[j + i]; + side_newrefs[j + i] = trtemp; +#endif + } + } + } + + // + // add the edge bevels + // + if ( b->numsides == 6 ) { + return; // pure axial + + } + // test the non-axial plane edges + for ( i = 6 ; i < b->numsides ; i++ ) + { + s = b->original_sides + i; + w = s->winding; + if ( !w ) { + continue; + } + for ( j = 0 ; j < w->numpoints ; j++ ) + { + k = ( j + 1 ) % w->numpoints; + VectorSubtract( w->p[j], w->p[k], vec ); + if ( VectorNormalize( vec ) < 0.5 ) { + continue; + } + SnapVector( vec ); + for ( k = 0 ; k < 3 ; k++ ) + if ( vec[k] == -1 || vec[k] == 1 ) { + break; + } // axial + if ( k != 3 ) { + continue; // only test non-axial edges + + } + // try the six possible slanted axials from this edge + for ( axis = 0 ; axis < 3 ; axis++ ) + { + for ( dir = -1 ; dir <= 1 ; dir += 2 ) + { + // construct a plane + VectorClear( vec2 ); + vec2[axis] = dir; + CrossProduct( vec, vec2, normal ); + if ( VectorNormalize( normal ) < 0.5 ) { + continue; + } + dist = DotProduct( w->p[j], normal ); + + // if all the points on all the sides are + // behind this plane, it is a proper edge bevel + for ( k = 0 ; k < b->numsides ; k++ ) + { + // if this plane has allready been used, skip it + if ( PlaneEqual( &mapplanes[b->original_sides[k].planenum] + , normal, dist ) ) { + break; + } + + w2 = b->original_sides[k].winding; + if ( !w2 ) { + continue; + } + for ( l = 0 ; l < w2->numpoints ; l++ ) + { + d = DotProduct( w2->p[l], normal ) - dist; + if ( d > 0.1 ) { + break; // point in front + } + } + if ( l != w2->numpoints ) { + break; + } + } + + if ( k != b->numsides ) { + continue; // wasn't part of the outer hull + } + // add this plane + if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + nummapbrushsides++; + s2 = &b->original_sides[b->numsides]; + s2->planenum = FindFloatPlane( normal, dist, 0, NULL ); + s2->texinfo = b->original_sides[0].texinfo; +#ifdef SIN + s2->lightinfo = b->original_sides[0].lightinfo; +#endif + s2->contents = b->original_sides[0].contents; + s2->flags |= SFL_BEVEL; + c_edgebevels++; + b->numsides++; + } + } + } + } +} //end of the function AddBrushBevels + +#endif + +//=========================================================================== +// creates windigs for sides and mins / maxs for the brush +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean MakeBrushWindings( mapbrush_t *ob ) { + int i, j; + winding_t *w; + side_t *side; + plane_t *plane; + + ClearBounds( ob->mins, ob->maxs ); + + for ( i = 0; i < ob->numsides; i++ ) + { + plane = &mapplanes[ob->original_sides[i].planenum]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( j = 0; j < ob->numsides && w; j++ ) + { + if ( i == j ) { + continue; + } + if ( ob->original_sides[j].flags & SFL_BEVEL ) { + continue; + } + plane = &mapplanes[ob->original_sides[j].planenum ^ 1]; + ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON); + } + + side = &ob->original_sides[i]; + side->winding = w; + if ( w ) { + side->flags |= SFL_VISIBLE; + for ( j = 0; j < w->numpoints; j++ ) + AddPointToBounds( w->p[j], ob->mins, ob->maxs ); + } + } + + for ( i = 0; i < 3; i++ ) + { + //IDBUG: all the indexes into the mins and maxs were zero (not using i) + if ( ob->mins[i] < -MAX_MAP_BOUNDS || ob->maxs[i] > MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum ); + ob->numsides = 0; //remove the brush + break; + } //end if + if ( ob->mins[i] > MAX_MAP_BOUNDS || ob->maxs[i] < -MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum ); + ob->numsides = 0; //remove the brush + break; + } //end if + } //end for + return true; +} //end of the function MakeBrushWindings +//=========================================================================== +// FIXME: currently doesn't mark all bevels +// NOTE: when one brush bevel is found the remaining sides of the brush +// are bevels as well (when the brush isn't expanded for AAS :)) +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MarkBrushBevels( mapbrush_t *brush ) { + int i; + int we; + side_t *s; + + //check all the sides of the brush + for ( i = 0; i < brush->numsides; i++ ) + { + s = brush->original_sides + i; + //if the side has no winding + if ( !s->winding ) { + Log_Write( "MarkBrushBevels: brush %d no winding", brush->brushnum ); + s->flags |= SFL_BEVEL; + } //end if + //if the winding is tiny + else if ( WindingIsTiny( s->winding ) ) { + s->flags |= SFL_BEVEL; + Log_Write( "MarkBrushBevels: brush %d tiny winding", brush->brushnum ); + } //end else if + //if the winding has errors + else + { + we = WindingError( s->winding ); + if ( we == WE_NOTENOUGHPOINTS + || we == WE_SMALLAREA + || we == WE_POINTBOGUSRANGE +// || we == WE_NONCONVEX + ) { + Log_Write( "MarkBrushBevels: brush %d %s", brush->brushnum, WindingErrorString() ); + s->flags |= SFL_BEVEL; + } //end else if + } //end else + if ( s->flags & SFL_BEVEL ) { + s->flags &= ~SFL_VISIBLE; + //if the side has a valid plane + if ( s->planenum > 0 && s->planenum < nummapplanes ) { + //if it is an axial plane + if ( mapplanes[s->planenum].type < 3 ) { + c_boxbevels++; + } else { c_edgebevels++;} + } //end if + } //end if + } //end for +} //end of the function MarkBrushBevels +//=========================================================================== +// returns true if the map brush already exists +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int BrushExists( mapbrush_t *brush ) { + int i, s1, s2; + side_t *side1, *side2; + mapbrush_t *brush1, *brush2; + + for ( i = 0; i < nummapbrushes; i++ ) + { + brush1 = brush; + brush2 = &mapbrushes[i]; + //compare the brushes + if ( brush1->entitynum != brush2->entitynum ) { + continue; + } + //if (brush1->contents != brush2->contents) continue; + if ( brush1->numsides != brush2->numsides ) { + continue; + } + for ( s1 = 0; s1 < brush1->numsides; s1++ ) + { + side1 = brush1->original_sides + s1; + // + for ( s2 = 0; s2 < brush2->numsides; s2++ ) + { + side2 = brush2->original_sides + s2; + // + if ( ( side1->planenum & ~1 ) == ( side2->planenum & ~1 ) +// && side1->texinfo == side2->texinfo +// && side1->contents == side2->contents +// && side1->surf == side2->surf + ) { + break; + } + } //end if + if ( s2 >= brush2->numsides ) { + break; + } + } //end for + if ( s1 >= brush1->numsides ) { + return true; + } + } //end for + return false; +} //end of the function BrushExists +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteMapBrush( FILE *fp, mapbrush_t *brush, vec3_t origin ) { + int sn, rotate, shift[2], sv, tv, planenum, p1, i, j; + float scale[2], originshift[2], ang1, ang2, newdist; + vec3_t vecs[2], axis[2]; + map_texinfo_t *ti; + winding_t *w; + side_t *s; + plane_t *plane; + + if ( noliquids ) { + if ( brush->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + return true; + } //end if + } //end if + //if the brush has no contents + if ( !brush->contents ) { + return true; + } + //print the leading { + if ( fprintf( fp, " { //brush %d\n", brush->brushnum ) < 0 ) { + return false; + } + //write brush sides + for ( sn = 0; sn < brush->numsides; sn++ ) + { + s = brush->original_sides + sn; + //don't write out bevels + if ( !( s->flags & SFL_BEVEL ) ) { + //if the entity has an origin set + if ( origin[0] || origin[1] || origin[2] ) { + newdist = mapplanes[s->planenum].dist + DotProduct( mapplanes[s->planenum].normal, origin ); + planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist, 0, NULL ); + } else { + planenum = s->planenum; + } //end else + //always take the first plane, then flip the points if necesary + plane = &mapplanes[planenum & ~1]; + w = BaseWindingForPlane( plane->normal, plane->dist ); + // + for ( i = 0; i < 3; i++ ) + { + for ( j = 0; j < 3; j++ ) + { + if ( fabs( w->p[i][j] ) < 0.2 ) { + w->p[i][j] = 0; + } else if ( fabs( (int)w->p[i][j] - w->p[i][j] ) < 0.3 ) { + w->p[i][j] = (int) w->p[i][j]; + } + //w->p[i][j] = (int) (w->p[i][j] + 0.2); + } //end for + } //end for + //three non-colinear points to define the plane + if ( planenum & 1 ) { + p1 = 1; + } else { p1 = 0;} + if ( fprintf( fp," ( %5i %5i %5i ) ", (int)w->p[p1][0], (int)w->p[p1][1], (int)w->p[p1][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[!p1][0], (int)w->p[!p1][1], (int)w->p[!p1][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] ) < 0 ) { + return false; + } + //free the winding + FreeWinding( w ); + // + if ( s->texinfo == TEXINFO_NODE ) { + if ( brush->contents & CONTENTS_PLAYERCLIP ) { + //player clip + if ( loadedmaptype == MAPTYPE_SIN ) { + if ( fprintf( fp, "generic/misc/clip 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end if + else if ( loadedmaptype == MAPTYPE_QUAKE2 ) { //FIXME: don't always use e1u1 + if ( fprintf( fp, "e1u1/clip 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else + else if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + if ( fprintf( fp, "common/clipplayer 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else if + else + { + if ( fprintf( fp, "clip 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else + } //end if + else if ( brush->contents == CONTENTS_MONSTERCLIP ) { + //monster clip + if ( loadedmaptype == MAPTYPE_SIN ) { + if ( fprintf( fp, "generic/misc/monster 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end if + else if ( loadedmaptype == MAPTYPE_QUAKE2 ) { + if ( fprintf( fp, "e1u1/clip_mon 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else + else + { + if ( fprintf( fp, "common/clipmonster 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else + } //end else + else + { + if ( fprintf( fp, "clip 0 0 0 1 1" ) < 0 ) { + return false; + } + Log_Write( "brush->contents = %d\n", brush->contents ); + } //end else + } //end if +/* else if (loadedmaptype == MAPTYPE_SIN && s->texinfo == 0) + { + if (brush->contents & CONTENTS_DUMMYFENCE) + { + if (fprintf(fp, "generic/misc/fence 0 0 0 1 1") < 0) return false; + } //end if + else if (brush->contents & CONTENTS_MIST) + { + if (fprintf(fp, "generic/misc/volumetric_base 0 0 0 1 1") < 0) return false; + } //end if + else //unknown so far + { + if (fprintf(fp, "generic/misc/red 0 0 0 1 1") < 0) return false; + } //end else + } //end if*/ + else if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + //always use the same texture + if ( fprintf( fp, "common/caulk 0 0 0 1 1 1 0 0" ) < 0 ) { + return false; + } + } //end else if + else + { + //* + ti = &map_texinfo[s->texinfo]; + //the scaling of the texture + scale[0] = 1 / VectorNormalize2( ti->vecs[0], vecs[0] ); + scale[1] = 1 / VectorNormalize2( ti->vecs[1], vecs[1] ); + // + TextureAxisFromPlane( plane, axis[0], axis[1] ); + //calculate texture shift done by entity origin + originshift[0] = DotProduct( origin, axis[0] ); + originshift[1] = DotProduct( origin, axis[1] ); + //the texture shift without origin shift + shift[0] = ti->vecs[0][3] - originshift[0]; + shift[1] = ti->vecs[1][3] - originshift[1]; + // + if ( axis[0][0] ) { + sv = 0; + } else if ( axis[0][1] ) { + sv = 1; + } else { sv = 2;} + if ( axis[1][0] ) { + tv = 0; + } else if ( axis[1][1] ) { + tv = 1; + } else { tv = 2;} + //calculate rotation of texture + if ( vecs[0][tv] == 0 ) { + ang1 = vecs[0][sv] > 0 ? 90.0 : -90.0; + } else { ang1 = atan2( vecs[0][sv], vecs[0][tv] ) * 180 / Q_PI;} + if ( ang1 < 0 ) { + ang1 += 360; + } + if ( ang1 >= 360 ) { + ang1 -= 360; + } + if ( axis[0][tv] == 0 ) { + ang2 = axis[0][sv] > 0 ? 90.0 : -90.0; + } else { ang2 = atan2( axis[0][sv], axis[0][tv] ) * 180 / Q_PI;} + if ( ang2 < 0 ) { + ang2 += 360; + } + if ( ang2 >= 360 ) { + ang2 -= 360; + } + rotate = ang2 - ang1; + if ( rotate < 0 ) { + rotate += 360; + } + if ( rotate >= 360 ) { + rotate -= 360; + } + //write the texture info + if ( fprintf( fp, "%s %d %d %d", ti->texture, shift[0], shift[1], rotate ) < 0 ) { + return false; + } + if ( fabs( scale[0] - ( (int) scale[0] ) ) < 0.001 ) { + if ( fprintf( fp, " %d", (int) scale[0] ) < 0 ) { + return false; + } + } //end if + else + { + if ( fprintf( fp, " %4f", scale[0] ) < 0 ) { + return false; + } + } //end if + if ( fabs( scale[1] - ( (int) scale[1] ) ) < 0.001 ) { + if ( fprintf( fp, " %d", (int) scale[1] ) < 0 ) { + return false; + } + } //end if + else + { + if ( fprintf( fp, " %4f", scale[1] ) < 0 ) { + return false; + } + } //end else + //write the extra brush side info + if ( loadedmaptype == MAPTYPE_QUAKE2 ) { + if ( fprintf( fp, " %ld %ld %ld", s->contents, ti->flags, ti->value ) < 0 ) { + return false; + } + } //end if + //*/ + } //end else + if ( fprintf( fp, "\n" ) < 0 ) { + return false; + } + } //end if + } //end if + if ( fprintf( fp, " }\n" ) < 0 ) { + return false; + } + c_writtenbrushes++; + return true; +} //end of the function WriteMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteOriginBrush( FILE *fp, vec3_t origin ) { + vec3_t normal; + float dist; + int i, s; + winding_t *w; + + if ( fprintf( fp, " {\n" ) < 0 ) { + return false; + } + // + for ( i = 0; i < 3; i++ ) + { + for ( s = -1; s <= 1; s += 2 ) + { + // + VectorClear( normal ); + normal[i] = s; + dist = origin[i] * s + 16; + // + w = BaseWindingForPlane( normal, dist ); + //three non-colinear points to define the plane + if ( fprintf( fp," ( %5i %5i %5i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] ) < 0 ) { + return false; + } + if ( fprintf( fp,"( %5i %5i %5i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] ) < 0 ) { + return false; + } + //free the winding + FreeWinding( w ); + //write origin texture: + // CONTENTS_ORIGIN = 16777216 + // SURF_NODRAW = 128 + if ( loadedmaptype == MAPTYPE_SIN ) { + if ( fprintf( fp, "generic/misc/origin 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end if + else if ( loadedmaptype == MAPTYPE_HALFLIFE ) { + if ( fprintf( fp, "origin 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end if + else + { + if ( fprintf( fp, "e1u1/origin 0 0 0 1 1" ) < 0 ) { + return false; + } + } //end else + //Quake2 extra brush side info + if ( loadedmaptype == MAPTYPE_QUAKE2 ) { + //if (fprintf(fp, " 16777216 128 0") < 0) return false; + } //end if + if ( fprintf( fp, "\n" ) < 0 ) { + return false; + } + } //end for + } //end for + if ( fprintf( fp, " }\n" ) < 0 ) { + return false; + } + c_writtenbrushes++; + return true; +} //end of the function WriteOriginBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +mapbrush_t *GetAreaPortalBrush( entity_t *mapent ) { + int portalnum, bn; + mapbrush_t *brush; + + //the area portal number + portalnum = mapent->areaportalnum; + //find the area portal brush in the world brushes + for ( bn = 0; bn < nummapbrushes && portalnum; bn++ ) + { + brush = &mapbrushes[bn]; + //must be in world entity + if ( brush->entitynum == 0 ) { + if ( brush->contents & CONTENTS_AREAPORTAL ) { + portalnum--; + } //end if + } //end if + } //end for + if ( bn < nummapbrushes ) { + return brush; + } //end if + else + { + Log_Print( "area portal %d brush not found\n", mapent->areaportalnum ); + return NULL; + } //end else +} //end of the function GetAreaPortalBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WriteMapFileSafe( FILE *fp ) { + char key[1024], value[1024]; + int i, bn, entitybrushes; + epair_t *ep; + mapbrush_t *brush; + entity_t *mapent; + //vec3_t vec_origin = {0, 0, 0}; + + // + if ( fprintf( fp,"//=====================================================\n" + "//\n" + "// map file created with BSPC v1.6\n" + "//\n" + "// BSPC is created by Mr Elusive\n" + "//\n" ) < 0 ) { + return false; + } + if ( loadedmaptype == MAPTYPE_SIN ) { + if ( fprintf( fp, + "// generic/misc/red is used for unknown textures\n" ) < 0 ) { + return false; + } + } //end if + if ( fprintf( fp,"//\n" + "//=====================================================\n" ) < 0 ) { + return false; + } + //write out all the entities + for ( i = 0; i < num_entities; i++ ) + { + mapent = &entities[i]; + if ( !mapent->epairs ) { + continue; + } //end if + if ( fprintf( fp, "{\n" ) < 0 ) { + return false; + } + // + if ( loadedmaptype == MAPTYPE_QUAKE3 ) { + if ( !stricmp( ValueForKey( mapent, "classname" ), "light" ) ) { + SetKeyValue( mapent, "light", "10000" ); + } //end if + } //end if + //write epairs + for ( ep = mapent->epairs; ep; ep = ep->next ) + { + strcpy( key, ep->key ); + StripTrailing( key ); + strcpy( value, ep->value ); + StripTrailing( value ); + // + if ( loadedmaptype == MAPTYPE_QUAKE2 || + loadedmaptype == MAPTYPE_SIN ) { + //don't write an origin for BSP models + if ( mapent->modelnum >= 0 && !strcmp( key, "origin" ) ) { + continue; + } + } //end if + //don't write BSP model numbers + if ( mapent->modelnum >= 0 && !strcmp( key, "model" ) && value[0] == '*' ) { + continue; + } + // + if ( fprintf( fp, " \"%s\" \"%s\"\n", key, value ) < 0 ) { + return false; + } + } //end for + // + if ( ValueForKey( mapent, "origin" ) ) { + GetVectorForKey( mapent, "origin", mapent->origin ); + } else { mapent->origin[0] = mapent->origin[1] = mapent->origin[2] = 0;} + //if this is an area portal entity + if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { + brush = GetAreaPortalBrush( mapent ); + if ( !brush ) { + return false; + } + if ( !WriteMapBrush( fp, brush, mapent->origin ) ) { + return false; + } + } //end if + else + { + entitybrushes = false; + //write brushes + for ( bn = 0; bn < nummapbrushes; bn++ ) + { + brush = &mapbrushes[bn]; + //if the brush is part of this entity + if ( brush->entitynum == i ) { + //don't write out area portal brushes in the world + if ( !( ( brush->contents & CONTENTS_AREAPORTAL ) && brush->entitynum == 0 ) ) { + /* + if (!strcmp("func_door_rotating", ValueForKey(mapent, "classname"))) + { + AAS_PositionFuncRotatingBrush(mapent, brush); + if (!WriteMapBrush(fp, brush, vec_origin)) return false; + } //end if + else //*/ + { + if ( !WriteMapBrush( fp, brush, mapent->origin ) ) { + return false; + } + } //end else + entitybrushes = true; + } //end if + } //end if + } //end for + //if the entity had brushes + if ( entitybrushes ) { + //if the entity has an origin set + if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) { + if ( !WriteOriginBrush( fp, mapent->origin ) ) { + return false; + } + } //end if + } //end if + } //end else + if ( fprintf( fp, "}\n" ) < 0 ) { + return false; + } + } //end for + if ( fprintf( fp, "//total of %d brushes\n", c_writtenbrushes ) < 0 ) { + return false; + } + return true; +} //end of the function WriteMapFileSafe +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WriteMapFile( char *filename ) { + FILE *fp; + double start_time; + + c_writtenbrushes = 0; + //the time started + start_time = I_FloatTime(); + // + Log_Print( "writing %s\n", filename ); + fp = fopen( filename, "wb" ); + if ( !fp ) { + Log_Print( "can't open %s\n", filename ); + return; + } //end if + if ( !WriteMapFileSafe( fp ) ) { + fclose( fp ); + Log_Print( "error writing map file %s\n", filename ); + return; + } //end if + fclose( fp ); + //display creation time + Log_Print( "written %d brushes\n", c_writtenbrushes ); + Log_Print( "map file written in %5.0f seconds\n", I_FloatTime() - start_time ); +} //end of the function WriteMapFile +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMapInfo( void ) { + Log_Print( "\n" ); + Log_Print( "%6i brushes\n", nummapbrushes ); + Log_Print( "%6i brush sides\n", nummapbrushsides ); +// Log_Print("%6i clipbrushes\n", c_clipbrushes); +// Log_Print("%6i total sides\n", nummapbrushsides); +// Log_Print("%6i boxbevels\n", c_boxbevels); +// Log_Print("%6i edgebevels\n", c_edgebevels); +// Log_Print("%6i entities\n", num_entities); +// Log_Print("%6i planes\n", nummapplanes); +// Log_Print("%6i areaportals\n", c_areaportals); +// Log_Print("%6i squatt brushes\n", c_squattbrushes); +// Log_Print("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2], +// map_maxs[0],map_maxs[1],map_maxs[2]); +} //end of the function PrintMapInfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void ResetMapLoading( void ) { + int i; + epair_t *ep, *nextep; + + //free all map brush side windings + for ( i = 0; i < nummapbrushsides; i++ ) + { + if ( brushsides[i].winding ) { + FreeWinding( brushsides[i].winding ); + } //end for + } //end for + + //reset regular stuff + nummapbrushes = 0; + memset( mapbrushes, 0, MAX_MAPFILE_BRUSHES * sizeof( mapbrush_t ) ); + // + nummapbrushsides = 0; + memset( brushsides, 0, MAX_MAPFILE_BRUSHSIDES * sizeof( side_t ) ); + memset( side_brushtextures, 0, MAX_MAPFILE_BRUSHSIDES * sizeof( brush_texture_t ) ); + // + nummapplanes = 0; + memset( mapplanes, 0, MAX_MAPFILE_PLANES * sizeof( plane_t ) ); + // + memset( planehash, 0, PLANE_HASHES * sizeof( plane_t * ) ); + // + memset( map_texinfo, 0, MAX_MAPFILE_TEXINFO * sizeof( map_texinfo_t ) ); + map_numtexinfo = 0; + // + VectorClear( map_mins ); + VectorClear( map_maxs ); + // + c_boxbevels = 0; + c_edgebevels = 0; + c_areaportals = 0; + c_clipbrushes = 0; + c_writtenbrushes = 0; + //clear the entities + for ( i = 0; i < num_entities; i++ ) + { + for ( ep = entities[i].epairs; ep; ep = nextep ) + { + nextep = ep->next; + FreeMemory( ep->key ); + FreeMemory( ep->value ); + FreeMemory( ep ); + } //end for + } //end for + num_entities = 0; + memset( entities, 0, MAX_MAP_ENTITIES * sizeof( entity_t ) ); +} //end of the function ResetMapLoading +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +typedef struct +{ + int ident; + int version; +} idheader_t; + +int LoadMapFromBSP( struct quakefile_s *qf ) { + idheader_t idheader; + + if ( ReadQuakeFile( qf, &idheader, 0, sizeof( idheader_t ) ) != sizeof( idheader_t ) ) { + return false; + } //end if + + idheader.ident = LittleLong( idheader.ident ); + idheader.version = LittleLong( idheader.version ); + //Quake3 BSP file + if ( idheader.ident == Q3_BSP_IDENT && idheader.version == Q3_BSP_VERSION ) { + ResetMapLoading(); + Q3_LoadMapFromBSP( qf ); + Q3_FreeMaxBSP(); + } //end if + else + { + Error( "unknown BSP format %c%c%c%c, version %d\n", + ( idheader.ident & 0xFF ), + ( ( idheader.ident >> 8 ) & 0xFF ), + ( ( idheader.ident >> 16 ) & 0xFF ), + ( ( idheader.ident >> 24 ) & 0xFF ), idheader.version ); + return false; + } //end if + // + return true; +} //end of the function LoadMapFromBSP diff --git a/src/bspc/map_q1.c b/src/bspc/map_q1.c new file mode 100644 index 0000000..d699efb --- /dev/null +++ b/src/bspc/map_q1.c @@ -0,0 +1,1205 @@ +/* +=========================================================================== + +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: map_q1.c +// Function: map loading and writing +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-03 +// Tab Size: 3 +// NOTES: the recursive splitting of the huge brush sometimes still +// creates bad brushes +//=========================================================================== + +#include "qbsp.h" +#include "l_bsp_q1.h" +#include "aas_map.h" //AAS_CreateMapBrushes + +int q1_numbrushes; +int q1_numclipbrushes; + +//#define Q1_PRINT + +//=========================================================================== +// water, slime and lava brush textures names always start with a * +// followed by the type: "slime", "lava" or otherwise water +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q1_TextureContents( char *name ) { + if ( !Q_strcasecmp( name, "clip" ) ) { + return CONTENTS_SOLID; + } + if ( name[0] == '*' ) { + if ( !Q_strncasecmp( name + 1,"lava",4 ) ) { + return CONTENTS_LAVA; + } else if ( !Q_strncasecmp( name + 1,"slime",5 ) ) { + return CONTENTS_SLIME; + } else { return CONTENTS_WATER;} + } //end if + else if ( !Q_strncasecmp( name, "sky", 3 ) ) { + return CONTENTS_SOLID; + } else { return CONTENTS_SOLID;} +} //end of the function Q1_TextureContents +//=========================================================================== +// Generates two new brushes, leaving the original +// unchanged +// +// modified for Half-Life because there are quite a lot of tiny node leaves +// in the Half-Life bsps +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_SplitBrush( bspbrush_t *brush, int planenum, int nodenum, + bspbrush_t **front, bspbrush_t **back ) { + bspbrush_t *b[2]; + int i, j; + winding_t *w, *cw[2], *midwinding; + plane_t *plane, *plane2; + side_t *s, *cs; + float d, d_front, d_back; + + *front = *back = NULL; + plane = &mapplanes[planenum]; + + // check all points + d_front = d_back = 0; + for ( i = 0 ; i < brush->numsides ; i++ ) + { + w = brush->sides[i].winding; + if ( !w ) { + continue; + } + for ( j = 0 ; j < w->numpoints ; j++ ) + { + d = DotProduct( w->p[j], plane->normal ) - plane->dist; + if ( d > 0 && d > d_front ) { + d_front = d; + } + if ( d < 0 && d < d_back ) { + d_back = d; + } + } //end for + } //end for + + if ( d_front < 0.1 ) { // PLANESIDE_EPSILON) + // only on back + *back = CopyBrush( brush ); + Log_Print( "Q1_SplitBrush: only on back\n" ); + return; + } //end if + if ( d_back > -0.1 ) { // PLANESIDE_EPSILON) + // only on front + *front = CopyBrush( brush ); + Log_Print( "Q1_SplitBrush: only on front\n" ); + return; + } //end if + + // create a new winding from the split plane + + w = BaseWindingForPlane( plane->normal, plane->dist ); + for ( i = 0; i < brush->numsides && w; i++ ) + { + plane2 = &mapplanes[brush->sides[i].planenum ^ 1]; + ChopWindingInPlace( &w, plane2->normal, plane2->dist, 0 ); // PLANESIDE_EPSILON); + } //end for + + if ( !w || WindingIsTiny( w ) ) { // the brush isn't really split + int side; + + Log_Print( "Q1_SplitBrush: no split winding\n" ); + side = BrushMostlyOnSide( brush, plane ); + if ( side == PSIDE_FRONT ) { + *front = CopyBrush( brush ); + } + if ( side == PSIDE_BACK ) { + *back = CopyBrush( brush ); + } + return; + } + + if ( WindingIsHuge( w ) ) { + Log_Print( "Q1_SplitBrush: WARNING huge split winding\n" ); + } //end of + + midwinding = w; + + // split it for real + + for ( i = 0; i < 2; i++ ) + { + b[i] = AllocBrush( brush->numsides + 1 ); + b[i]->original = brush->original; + } //end for + + // split all the current windings + + for ( i = 0 ; i < brush->numsides ; i++ ) + { + s = &brush->sides[i]; + w = s->winding; + if ( !w ) { + continue; + } + ClipWindingEpsilon( w, plane->normal, plane->dist, + 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] ); + for ( j = 0 ; j < 2 ; j++ ) + { + if ( !cw[j] ) { + continue; + } +#if 0 + if ( WindingIsTiny( cw[j] ) ) { + FreeWinding( cw[j] ); + continue; + } +#endif + cs = &b[j]->sides[b[j]->numsides]; + b[j]->numsides++; + *cs = *s; +// cs->planenum = s->planenum; +// cs->texinfo = s->texinfo; +// cs->visible = s->visible; +// cs->original = s->original; + cs->winding = cw[j]; + cs->flags &= ~SFL_TESTED; + } //end for + } //end for + + + // see if we have valid polygons on both sides + + for ( i = 0 ; i < 2 ; i++ ) + { + BoundBrush( b[i] ); + for ( j = 0 ; j < 3 ; j++ ) + { + if ( b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096 ) { + Log_Print( "Q1_SplitBrush: bogus brush after clip\n" ); + break; + } //end if + } //end for + + if ( b[i]->numsides < 3 || j < 3 ) { + FreeBrush( b[i] ); + b[i] = NULL; + Log_Print( "Q1_SplitBrush: numsides < 3\n" ); + } //end if + } //end for + + if ( !( b[0] && b[1] ) ) { + if ( !b[0] && !b[1] ) { + Log_Print( "Q1_SplitBrush: split removed brush\n" ); + } else { + Log_Print( "Q1_SplitBrush: split not on both sides\n" ); + } + if ( b[0] ) { + FreeBrush( b[0] ); + *front = CopyBrush( brush ); + } //end if + if ( b[1] ) { + FreeBrush( b[1] ); + *back = CopyBrush( brush ); + } //end if + return; + } //end if + + // add the midwinding to both sides + for ( i = 0; i < 2; i++ ) + { + cs = &b[i]->sides[b[i]->numsides]; + b[i]->numsides++; + + cs->planenum = planenum ^ i ^ 1; + cs->texinfo = 0; + //store the node number in the surf to find the texinfo later on + cs->surf = nodenum; + // + cs->flags &= ~SFL_VISIBLE; + cs->flags &= ~SFL_TESTED; + cs->flags &= ~SFL_TEXTURED; + if ( i == 0 ) { + cs->winding = CopyWinding( midwinding ); + } else { + cs->winding = midwinding; + } + } //end for + + + { + vec_t v1; + int i; + + for ( i = 0 ; i < 2 ; i++ ) + { + v1 = BrushVolume( b[i] ); + if ( v1 < 1 ) { + FreeBrush( b[i] ); + b[i] = NULL; + Log_Print( "Q1_SplitBrush: tiny volume after clip\n" ); + } //end if + } //end for + } //*/ + + *front = b[0]; + *back = b[1]; +} //end of the function Q1_SplitBrush +//=========================================================================== +// returns true if the tree starting at nodenum has only solid leaves +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q1_SolidTree_r( int nodenum ) { + if ( nodenum < 0 ) { + switch ( q1_dleafs[( -nodenum ) - 1].contents ) + { + case Q1_CONTENTS_EMPTY: + { + return false; + } //end case + case Q1_CONTENTS_SOLID: +#ifdef HLCONTENTS + case Q1_CONTENTS_CLIP: +#endif HLCONTENTS + case Q1_CONTENTS_SKY: +#ifdef HLCONTENTS + case Q1_CONTENTS_TRANSLUCENT: +#endif HLCONTENTS + { + return true; + } //end case + case Q1_CONTENTS_WATER: + case Q1_CONTENTS_SLIME: + case Q1_CONTENTS_LAVA: +#ifdef HLCONTENTS + //these contents should not be found in the BSP + case Q1_CONTENTS_ORIGIN: + case Q1_CONTENTS_CURRENT_0: + case Q1_CONTENTS_CURRENT_90: + case Q1_CONTENTS_CURRENT_180: + case Q1_CONTENTS_CURRENT_270: + case Q1_CONTENTS_CURRENT_UP: + case Q1_CONTENTS_CURRENT_DOWN: +#endif HLCONTENTS + default: + { + return false; + } //end default + } //end switch + return false; + } //end if + if ( !Q1_SolidTree_r( q1_dnodes[nodenum].children[0] ) ) { + return false; + } + if ( !Q1_SolidTree_r( q1_dnodes[nodenum].children[1] ) ) { + return false; + } + return true; +} //end of the function Q1_SolidTree_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_CreateBrushes_r( bspbrush_t *brush, int nodenum ) { + int planenum; + bspbrush_t *front, *back; + q1_dleaf_t *leaf; + + //if it is a leaf + if ( nodenum < 0 ) { + leaf = &q1_dleafs[( -nodenum ) - 1]; + if ( leaf->contents != Q1_CONTENTS_EMPTY ) { +#ifdef Q1_PRINT + qprintf( "\r%5i", ++q1_numbrushes ); +#endif //Q1_PRINT + } //end if + switch ( leaf->contents ) + { + case Q1_CONTENTS_EMPTY: + { + FreeBrush( brush ); + return NULL; + } //end case + case Q1_CONTENTS_SOLID: +#ifdef HLCONTENTS + case Q1_CONTENTS_CLIP: +#endif HLCONTENTS + case Q1_CONTENTS_SKY: +#ifdef HLCONTENTS + case Q1_CONTENTS_TRANSLUCENT: +#endif HLCONTENTS + { + brush->side = CONTENTS_SOLID; + return brush; + } //end case + case Q1_CONTENTS_WATER: + { + brush->side = CONTENTS_WATER; + return brush; + } //end case + case Q1_CONTENTS_SLIME: + { + brush->side = CONTENTS_SLIME; + return brush; + } //end case + case Q1_CONTENTS_LAVA: + { + brush->side = CONTENTS_LAVA; + return brush; + } //end case +#ifdef HLCONTENTS + //these contents should not be found in the BSP + case Q1_CONTENTS_ORIGIN: + case Q1_CONTENTS_CURRENT_0: + case Q1_CONTENTS_CURRENT_90: + case Q1_CONTENTS_CURRENT_180: + case Q1_CONTENTS_CURRENT_270: + case Q1_CONTENTS_CURRENT_UP: + case Q1_CONTENTS_CURRENT_DOWN: + { + Error( "Q1_CreateBrushes_r: found contents %d in Half-Life BSP", leaf->contents ); + return NULL; + } //end case +#endif HLCONTENTS + default: + { + Error( "Q1_CreateBrushes_r: unknown contents %d in Half-Life BSP", leaf->contents ); + return NULL; + } //end default + } //end switch + return NULL; + } //end if + //if the rest of the tree is solid + /*if (Q1_SolidTree_r(nodenum)) + { + brush->side = CONTENTS_SOLID; + return brush; + } //end if*/ + // + planenum = q1_dnodes[nodenum].planenum; + planenum = FindFloatPlane( q1_dplanes[planenum].normal, q1_dplanes[planenum].dist, 0, NULL ); + //split the brush with the node plane + Q1_SplitBrush( brush, planenum, nodenum, &front, &back ); + //free the original brush + FreeBrush( brush ); + //every node must split the brush in two + if ( !front || !back ) { + Log_Print( "Q1_CreateBrushes_r: WARNING node not splitting brush\n" ); + //return NULL; + } //end if + //create brushes recursively + if ( front ) { + front = Q1_CreateBrushes_r( front, q1_dnodes[nodenum].children[0] ); + } + if ( back ) { + back = Q1_CreateBrushes_r( back, q1_dnodes[nodenum].children[1] ); + } + //link the brushes if possible and return them + if ( front ) { + for ( brush = front; brush->next; brush = brush->next ) ; + brush->next = back; + return front; + } //end if + else + { + return back; + } //end else +} //end of the function Q1_CreateBrushes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_CreateBrushesFromBSP( int modelnum ) { + bspbrush_t *brushlist; + bspbrush_t *brush; + q1_dnode_t *headnode; + vec3_t mins, maxs; + int i; + + // + headnode = &q1_dnodes[q1_dmodels[modelnum].headnode[0]]; + //get the mins and maxs of the world + VectorCopy( headnode->mins, mins ); + VectorCopy( headnode->maxs, maxs ); + //enlarge these mins and maxs + for ( i = 0; i < 3; i++ ) + { + mins[i] -= 8; + maxs[i] += 8; + } //end for + //NOTE: have to add the BSP tree mins and maxs to the MAP mins and maxs + AddPointToBounds( mins, map_mins, map_maxs ); + AddPointToBounds( maxs, map_mins, map_maxs ); + // + if ( !modelnum ) { + Log_Print( "brush size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", + map_mins[0], map_mins[1], map_mins[2], + map_maxs[0], map_maxs[1], map_maxs[2] ); + } //end if + //create one huge brush containing the whole world + brush = BrushFromBounds( mins, maxs ); + VectorCopy( mins, brush->mins ); + VectorCopy( maxs, brush->maxs ); + // +#ifdef Q1_PRINT + qprintf( "creating Quake brushes\n" ); + qprintf( "%5d brushes", q1_numbrushes = 0 ); +#endif //Q1_PRINT + //create the brushes + brushlist = Q1_CreateBrushes_r( brush, q1_dmodels[modelnum].headnode[0] ); + // +#ifdef Q1_PRINT + qprintf( "\n" ); +#endif //Q1_PRINT + //now we've got a list with brushes! + return brushlist; +} //end of the function Q1_CreateBrushesFromBSP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +q1_dleaf_t *Q1_PointInLeaf( int startnode, vec3_t point ) { + int nodenum; + vec_t dist; + q1_dnode_t *node; + q1_dplane_t *plane; + + nodenum = startnode; + while ( nodenum >= 0 ) + { + node = &q1_dnodes[nodenum]; + plane = &q1_dplanes[node->planenum]; + dist = DotProduct( point, plane->normal ) - plane->dist; + if ( dist > 0 ) { + nodenum = node->children[0]; + } else { + nodenum = node->children[1]; + } + } //end while + + return &q1_dleafs[-nodenum - 1]; +} //end of the function Q1_PointInLeaf +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q1_FaceArea( q1_dface_t *face ) { + int i; + float total; + vec_t *v; + vec3_t d1, d2, cross; + q1_dedge_t *edge; + + edge = &q1_dedges[face->firstedge]; + v = q1_dvertexes[edge->v[0]].point; + + total = 0; + for ( i = 1; i < face->numedges - 1; i++ ) + { + edge = &q1_dedges[face->firstedge + i]; + VectorSubtract( q1_dvertexes[edge->v[0]].point, v, d1 ); + VectorSubtract( q1_dvertexes[edge->v[1]].point, v, d2 ); + CrossProduct( d1, d2, cross ); + total += 0.5 * VectorLength( cross ); + } //end for + return total; +} //end of the function AAS_FaceArea +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_FacePlane( q1_dface_t *face, vec3_t normal, float *dist ) { + vec_t *v1, *v2, *v3; + vec3_t vec1, vec2; + int side, edgenum; + + edgenum = q1_dsurfedges[face->firstedge]; + side = edgenum < 0; + v1 = q1_dvertexes[q1_dedges[abs( edgenum )].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs( edgenum )].v[!side]].point; + edgenum = q1_dsurfedges[face->firstedge + 1]; + side = edgenum < 0; + v3 = q1_dvertexes[q1_dedges[abs( edgenum )].v[!side]].point; + // + VectorSubtract( v2, v1, vec1 ); + VectorSubtract( v3, v1, vec2 ); + + CrossProduct( vec1, vec2, normal ); + VectorNormalize( normal ); + *dist = DotProduct( v1, normal ); +} //end of the function Q1_FacePlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_MergeBrushes( bspbrush_t *brushlist, int modelnum ) { + int nummerges, merged; + bspbrush_t *b1, *b2, *tail, *newbrush, *newbrushlist; + bspbrush_t *lastb2; + + if ( !brushlist ) { + return NULL; + } + + if ( !modelnum ) { + qprintf( "%5d brushes merged", nummerges = 0 ); + } + do + { + for ( tail = brushlist; tail; tail = tail->next ) + { + if ( !tail->next ) { + break; + } + } //end for + merged = 0; + newbrushlist = NULL; + for ( b1 = brushlist; b1; b1 = brushlist ) + { + lastb2 = b1; + for ( b2 = b1->next; b2; b2 = b2->next ) + { + //can't merge brushes with different contents + if ( b1->side != b2->side ) { + newbrush = NULL; + } else { newbrush = TryMergeBrushes( b1, b2 );} + //if a merged brush is created + if ( newbrush ) { + //copy the brush contents + newbrush->side = b1->side; + //add the new brush to the end of the list + tail->next = newbrush; + //remove the second brush from the list + lastb2->next = b2->next; + //remove the first brush from the list + brushlist = brushlist->next; + //free the merged brushes + FreeBrush( b1 ); + FreeBrush( b2 ); + //get a new tail brush + for ( tail = brushlist; tail; tail = tail->next ) + { + if ( !tail->next ) { + break; + } + } //end for + merged++; + if ( !modelnum ) { + qprintf( "\r%5d", nummerges++ ); + } + break; + } //end if + lastb2 = b2; + } //end for + //if b1 can't be merged with any of the other brushes + if ( !b2 ) { + brushlist = brushlist->next; + //keep b1 + b1->next = newbrushlist; + newbrushlist = b1; + } //end else + } //end for + brushlist = newbrushlist; + } while ( merged ); + if ( !modelnum ) { + qprintf( "\n" ); + } + return newbrushlist; +} //end of the function Q1_MergeBrushes +//=========================================================================== +// returns the amount the face and the winding overlap +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float Q1_FaceOnWinding( q1_dface_t *face, winding_t *winding ) { + int i, edgenum, side; + float dist, area; + q1_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + winding_t *w; + + // + w = CopyWinding( winding ); + memcpy( &plane, &q1_dplanes[face->planenum], sizeof( q1_dplane_t ) ); + //check on which side of the plane the face is + if ( face->side ) { + VectorNegate( plane.normal, plane.normal ); + plane.dist = -plane.dist; + } //end if + for ( i = 0; i < face->numedges && w; i++ ) + { + //get the first and second vertex of the edge + edgenum = q1_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = q1_dvertexes[q1_dedges[abs( edgenum )].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs( edgenum )].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract( v1, v2, edgevec ); + CrossProduct( edgevec, plane.normal, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + ChopWindingInPlace( &w, normal, dist, 0.9 ); //CLIP_EPSILON + } //end for + if ( w ) { + area = WindingArea( w ); + FreeWinding( w ); + return area; + } //end if + return 0; +} //end of the function Q1_FaceOnWinding +//=========================================================================== +// returns a list with brushes created by splitting the given brush with +// planes that go through the face edges and are orthogonal to the face plane +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_SplitBrushWithFace( bspbrush_t *brush, q1_dface_t *face ) { + int i, edgenum, side, planenum, splits; + float dist; + q1_dplane_t plane; + vec_t *v1, *v2; + vec3_t normal, edgevec; + bspbrush_t *front, *back, *brushlist; + + memcpy( &plane, &q1_dplanes[face->planenum], sizeof( q1_dplane_t ) ); + //check on which side of the plane the face is + if ( face->side ) { + VectorNegate( plane.normal, plane.normal ); + plane.dist = -plane.dist; + } //end if + splits = 0; + brushlist = NULL; + for ( i = 0; i < face->numedges; i++ ) + { + //get the first and second vertex of the edge + edgenum = q1_dsurfedges[face->firstedge + i]; + side = edgenum > 0; + //if the face plane is flipped + v1 = q1_dvertexes[q1_dedges[abs( edgenum )].v[side]].point; + v2 = q1_dvertexes[q1_dedges[abs( edgenum )].v[!side]].point; + //create a plane through the edge vector, orthogonal to the face plane + //and with the normal vector pointing out of the face + VectorSubtract( v1, v2, edgevec ); + CrossProduct( edgevec, plane.normal, normal ); + VectorNormalize( normal ); + dist = DotProduct( normal, v1 ); + // + planenum = FindFloatPlane( normal, dist, 0, NULL ); + //split the current brush + SplitBrush( brush, planenum, &front, &back ); + //if there is a back brush just put it in the list + if ( back ) { + //copy the brush contents + back->side = brush->side; + // + back->next = brushlist; + brushlist = back; + splits++; + } //end if + if ( !front ) { + Log_Print( "Q1_SplitBrushWithFace: no new brush\n" ); + FreeBrushList( brushlist ); + return NULL; + } //end if + //copy the brush contents + front->side = brush->side; + //continue splitting the front brush + brush = front; + } //end for + if ( !splits ) { + FreeBrush( front ); + return NULL; + } //end if + front->next = brushlist; + brushlist = front; + return brushlist; +} //end of the function Q1_SplitBrushWithFace +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +bspbrush_t *Q1_TextureBrushes( bspbrush_t *brushlist, int modelnum ) { + float area, largestarea; + int i, n, texinfonum, sn, numbrushes; + int bestfacenum, sidenodenum; + side_t *side; + q1_dmiptexlump_t *miptexlump; + q1_miptex_t *miptex; + bspbrush_t *brush, *nextbrush, *prevbrush, *newbrushes, *brushlistend; + vec_t defaultvec[4] = {1, 0, 0, 0}; + + if ( !modelnum ) { + qprintf( "texturing brushes\n" ); + } + if ( !modelnum ) { + qprintf( "%5d brushes", numbrushes = 0 ); + } + //get a pointer to the last brush in the list + for ( brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next ) + { + if ( !brushlistend->next ) { + break; + } + } //end for + //there's no previous brush when at the start of the list + prevbrush = NULL; + //go over the brush list + for ( brush = brushlist; brush; brush = nextbrush ) + { + nextbrush = brush->next; + //find a texinfo for every brush side + for ( sn = 0; sn < brush->numsides; sn++ ) + { + side = &brush->sides[sn]; + // + if ( side->flags & SFL_TEXTURED ) { + continue; + } + //number of the node that created this brush side + sidenodenum = side->surf; //see midwinding in Q1_SplitBrush + //no face found yet + bestfacenum = -1; + //minimum face size + largestarea = 1; + //if optimizing the texture placement and not going for the + //least number of brushes + if ( !lessbrushes ) { + for ( i = 0; i < q1_numfaces; i++ ) + { + //the face must be in the same plane as the node plane that created + //this brush side + if ( q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum ) { + //get the area the face and the brush side overlap + area = Q1_FaceOnWinding( &q1_dfaces[i], side->winding ); + //if this face overlaps the brush side winding more than previous faces + if ( area > largestarea ) { + //if there already was a face for texturing this brush side with + //a different texture + if ( bestfacenum >= 0 && + ( q1_dfaces[bestfacenum].texinfo != q1_dfaces[i].texinfo ) ) { + //split the brush to fit the texture + newbrushes = Q1_SplitBrushWithFace( brush, &q1_dfaces[i] ); + //if new brushes where created + if ( newbrushes ) { + //remove the current brush from the list + if ( prevbrush ) { + prevbrush->next = brush->next; + } else { brushlist = brush->next;} + if ( brushlistend == brush ) { + brushlistend = prevbrush; + nextbrush = newbrushes; + } //end if + //add the new brushes to the end of the list + if ( brushlistend ) { + brushlistend->next = newbrushes; + } else { brushlist = newbrushes;} + //free the current brush + FreeBrush( brush ); + //don't forget about the prevbrush pointer at the bottom of + //the outer loop + brush = prevbrush; + //find the end of the list + for ( brushlistend = brushlist; brushlistend; brushlistend = brushlistend->next ) + { + if ( !brushlistend->next ) { + break; + } + } //end for + break; + } //end if + else + { + Log_Write( "brush %d: no real texture split", numbrushes ); + } //end else + } //end if + else + { + //best face for texturing this brush side + bestfacenum = i; + } //end else + } //end if + } //end if + } //end for + //if the brush was split the original brush is removed + //and we just continue with the next one in the list + if ( i < q1_numfaces ) { + break; + } + } //end if + else + { + //find the face with the largest overlap with this brush side + //for texturing the brush side + for ( i = 0; i < q1_numfaces; i++ ) + { + //the face must be in the same plane as the node plane that created + //this brush side + if ( q1_dfaces[i].planenum == q1_dnodes[sidenodenum].planenum ) { + //get the area the face and the brush side overlap + area = Q1_FaceOnWinding( &q1_dfaces[i], side->winding ); + //if this face overlaps the brush side winding more than previous faces + if ( area > largestarea ) { + largestarea = area; + bestfacenum = i; + } //end if + } //end if + } //end for + } //end else + //if a face was found for texturing this brush side + if ( bestfacenum >= 0 ) { + //set the MAP texinfo values + texinfonum = q1_dfaces[bestfacenum].texinfo; + for ( n = 0; n < 4; n++ ) + { + map_texinfo[texinfonum].vecs[0][n] = q1_texinfo[texinfonum].vecs[0][n]; + map_texinfo[texinfonum].vecs[1][n] = q1_texinfo[texinfonum].vecs[1][n]; + } //end for + //make sure the two vectors aren't of zero length otherwise use the default + //vector to prevent a divide by zero in the map writing + if ( VectorLength( map_texinfo[texinfonum].vecs[0] ) < 0.01 ) { + memcpy( map_texinfo[texinfonum].vecs[0], defaultvec, sizeof( defaultvec ) ); + } + if ( VectorLength( map_texinfo[texinfonum].vecs[1] ) < 0.01 ) { + memcpy( map_texinfo[texinfonum].vecs[1], defaultvec, sizeof( defaultvec ) ); + } + // + map_texinfo[texinfonum].flags = q1_texinfo[texinfonum].flags; + map_texinfo[texinfonum].value = 0; //Q1 and HL texinfos don't have a value + //the mip texture + miptexlump = (q1_dmiptexlump_t *) q1_dtexdata; + miptex = ( q1_miptex_t * )( (byte *)miptexlump + miptexlump->dataofs[q1_texinfo[texinfonum].miptex] ); + //get the mip texture name + strcpy( map_texinfo[texinfonum].texture, miptex->name ); + //no animations in Quake1 and Half-Life mip textures + map_texinfo[texinfonum].nexttexinfo = -1; + //store the texinfo number + side->texinfo = texinfonum; + // + if ( texinfonum > map_numtexinfo ) { + map_numtexinfo = texinfonum; + } + //this side is textured + side->flags |= SFL_TEXTURED; + } //end if + else + { + //no texture for this side + side->texinfo = TEXINFO_NODE; + //this side is textured + side->flags |= SFL_TEXTURED; + } //end if + } //end for + // + if ( !modelnum && prevbrush != brush ) { + qprintf( "\r%5d", ++numbrushes ); + } + //previous brush in the list + prevbrush = brush; + } //end for + if ( !modelnum ) { + qprintf( "\n" ); + } + //return the new list with brushes + return brushlist; +} //end of the function Q1_TextureBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_FixContentsTextures( bspbrush_t *brushlist ) { + int i, texinfonum; + bspbrush_t *brush; + + for ( brush = brushlist; brush; brush = brush->next ) + { + //only fix the textures of water, slime and lava brushes + if ( brush->side != CONTENTS_WATER && + brush->side != CONTENTS_SLIME && + brush->side != CONTENTS_LAVA ) { + continue; + } + // + for ( i = 0; i < brush->numsides; i++ ) + { + texinfonum = brush->sides[i].texinfo; + if ( Q1_TextureContents( map_texinfo[texinfonum].texture ) == brush->side ) { + break; + } + } //end for + //if no specific contents texture was found + if ( i >= brush->numsides ) { + texinfonum = -1; + for ( i = 0; i < map_numtexinfo; i++ ) + { + if ( Q1_TextureContents( map_texinfo[i].texture ) == brush->side ) { + texinfonum = i; + break; + } //end if + } //end for + } //end if + // + if ( texinfonum >= 0 ) { + //give all the brush sides this contents texture + for ( i = 0; i < brush->numsides; i++ ) + { + brush->sides[i].texinfo = texinfonum; + } //end for + } //end if + else {Log_Print( "brush contents %d with wrong textures\n", brush->side );} + // + } //end for + /* + for (brush = brushlist; brush; brush = brush->next) + { + //give all the brush sides this contents texture + for (i = 0; i < brush->numsides; i++) + { + if (Q1_TextureContents(map_texinfo[texinfonum].texture) != brush->side) + { + Error("brush contents %d with wrong contents textures %s\n", brush->side, + Q1_TextureContents(map_texinfo[texinfonum].texture)); + } //end if + } //end for + } //end for*/ +} //end of the function Q1_FixContentsTextures +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_BSPBrushToMapBrush( bspbrush_t *bspbrush, entity_t *mapent ) { + mapbrush_t *mapbrush; + side_t *side; + int i, besttexinfo; + + CheckBSPBrush( bspbrush ); + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "nummapbrushes == MAX_MAPFILE_BRUSHES" ); + } + + mapbrush = &mapbrushes[nummapbrushes]; + mapbrush->original_sides = &brushsides[nummapbrushsides]; + mapbrush->entitynum = mapent - entities; + mapbrush->brushnum = nummapbrushes - mapent->firstbrush; + mapbrush->leafnum = -1; + mapbrush->numsides = 0; + + besttexinfo = TEXINFO_NODE; + for ( i = 0; i < bspbrush->numsides; i++ ) + { + if ( !bspbrush->sides[i].winding ) { + continue; + } + // + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } + side = &brushsides[nummapbrushsides]; + //the contents of the bsp brush is stored in the side variable + side->contents = bspbrush->side; + side->surf = 0; + side->planenum = bspbrush->sides[i].planenum; + side->texinfo = bspbrush->sides[i].texinfo; + if ( side->texinfo != TEXINFO_NODE ) { + //this brush side is textured + side->flags |= SFL_TEXTURED; + besttexinfo = side->texinfo; + } //end if + // + nummapbrushsides++; + mapbrush->numsides++; + } //end for + // + if ( besttexinfo == TEXINFO_NODE ) { + mapbrush->numsides = 0; + q1_numclipbrushes++; + return; + } //end if + //set the texinfo for all the brush sides without texture + for ( i = 0; i < mapbrush->numsides; i++ ) + { + if ( mapbrush->original_sides[i].texinfo == TEXINFO_NODE ) { + mapbrush->original_sides[i].texinfo = besttexinfo; + } //end if + } //end for + //contents of the brush + mapbrush->contents = bspbrush->side; + // + if ( create_aas ) { + //create the AAS brushes from this brush, add brush bevels + AAS_CreateMapBrushes( mapbrush, mapent, true ); + return; + } //end if + //create windings for sides and bounds for brush + MakeBrushWindings( mapbrush ); + //add brush bevels + AddBrushBevels( mapbrush ); + //a new brush has been created + nummapbrushes++; + mapent->numbrushes++; +} //end of the function Q1_BSPBrushToMapBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_CreateMapBrushes( entity_t *mapent, int modelnum ) { + bspbrush_t *brushlist, *brush, *nextbrush; + int i; + + //create brushes from the model BSP tree + brushlist = Q1_CreateBrushesFromBSP( modelnum ); + //texture the brushes and split them when necesary + brushlist = Q1_TextureBrushes( brushlist, modelnum ); + //fix the contents textures of all brushes + Q1_FixContentsTextures( brushlist ); + // + if ( !nobrushmerge ) { + brushlist = Q1_MergeBrushes( brushlist, modelnum ); + //brushlist = Q1_MergeBrushes(brushlist, modelnum); + } //end if + // + if ( !modelnum ) { + qprintf( "converting brushes to map brushes\n" ); + } + if ( !modelnum ) { + qprintf( "%5d brushes", i = 0 ); + } + for ( brush = brushlist; brush; brush = nextbrush ) + { + nextbrush = brush->next; + Q1_BSPBrushToMapBrush( brush, mapent ); + brush->next = NULL; + FreeBrush( brush ); + if ( !modelnum ) { + qprintf( "\r%5d", ++i ); + } + } //end for + if ( !modelnum ) { + qprintf( "\n" ); + } +} //end of the function Q1_CreateMapBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_ResetMapLoading( void ) { +} //end of the function Q1_ResetMapLoading +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q1_LoadMapFromBSP( char *filename, int offset, int length ) { + int i, modelnum; + char *model, *classname; + + Log_Print( "-- Q1_LoadMapFromBSP --\n" ); + //the loaded map type + loadedmaptype = MAPTYPE_QUAKE1; + // + qprintf( "loading map from %s at %d\n", filename, offset ); + //load the Half-Life BSP file + Q1_LoadBSPFile( filename, offset, length ); + // + q1_numclipbrushes = 0; + //CreatePath(path); + //Q1_CreateQ2WALFiles(path); + //parse the entities from the BSP + Q1_ParseEntities(); + //clear the map mins and maxs + ClearBounds( map_mins, map_maxs ); + // + qprintf( "creating Quake1 brushes\n" ); + if ( lessbrushes ) { + qprintf( "creating minimum number of brushes\n" ); + } else { qprintf( "placing textures correctly\n" );} + // + for ( i = 0; i < num_entities; i++ ) + { + entities[i].firstbrush = nummapbrushes; + entities[i].numbrushes = 0; + // + classname = ValueForKey( &entities[i], "classname" ); + if ( classname && !strcmp( classname, "worldspawn" ) ) { + modelnum = 0; + } //end if + else + { + // + model = ValueForKey( &entities[i], "model" ); + if ( !model || *model != '*' ) { + continue; + } + model++; + modelnum = atoi( model ); + } //end else + //create map brushes for the entity + Q1_CreateMapBrushes( &entities[i], modelnum ); + } //end for + // + qprintf( "%5d map brushes\n", nummapbrushes ); + qprintf( "%5d clip brushes\n", q1_numclipbrushes ); +} //end of the function Q1_LoadMapFromBSP diff --git a/src/bspc/map_q2.c b/src/bspc/map_q2.c new file mode 100644 index 0000000..9d5815e --- /dev/null +++ b/src/bspc/map_q2.c @@ -0,0 +1,1149 @@ +/* +=========================================================================== + +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: map_q2.c +// Function: map loading and writing +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-03 +// Tab Size: 3 +//=========================================================================== + +//=========================================================================== +// ANSI, Area Navigational System Interface +// AAS, Area Awareness System +//=========================================================================== + +#include "qbsp.h" +#include "l_mem.h" +#include "..\botlib\aasfile.h" //aas_bbox_t +#include "aas_store.h" //AAS_MAX_BBOXES +#include "aas_cfg.h" +#include "aas_map.h" //AAS_CreateMapBrushes +#include "l_bsp_q2.h" + +#ifdef ME + +#define NODESTACKSIZE 1024 + +int nodestack[NODESTACKSIZE]; +int *nodestackptr; +int nodestacksize = 0; +int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; +int dbrushleafnums[MAX_MAPFILE_BRUSHES]; +int dplanes2mapplanes[MAX_MAPFILE_PLANES]; + +#endif //ME + +//==================================================================== + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_CreateMapTexinfo( void ) { + int i; + + for ( i = 0; i < numtexinfo; i++ ) + { + memcpy( map_texinfo[i].vecs, texinfo[i].vecs, sizeof( float ) * 2 * 4 ); + map_texinfo[i].flags = texinfo[i].flags; + map_texinfo[i].value = texinfo[i].value; + strcpy( map_texinfo[i].texture, texinfo[i].texture ); + map_texinfo[i].nexttexinfo = 0; + } //end for +} //end of the function Q2_CreateMapTexinfo + +/* +=========== +Q2_BrushContents +=========== +*/ +int Q2_BrushContents( mapbrush_t *b ) { + int contents; + side_t *s; + int i; + int trans; + + s = &b->original_sides[0]; + contents = s->contents; + trans = texinfo[s->texinfo].flags; + for ( i = 1; i < b->numsides; i++, s++ ) + { + s = &b->original_sides[i]; + trans |= texinfo[s->texinfo].flags; + if ( s->contents != contents ) { + Log_Print( "Entity %i, Brush %i: mixed face contents\n" + , b->entitynum, b->brushnum ); + Log_Print( "texture name = %s\n", texinfo[s->texinfo].texture ); + break; + } + } +/* + // if any side is translucent, mark the contents + // and change solid to window + if ( trans & (SURF_TRANS33|SURF_TRANS66) ) + { + contents |= CONTENTS_Q2TRANSLUCENT; + if (contents & CONTENTS_SOLID) + { + contents &= ~CONTENTS_SOLID; + contents |= CONTENTS_WINDOW; + } + } +*/ + return contents; +} + +#ifdef ME + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MakeAreaPortalBrush( mapbrush_t *brush ) { + int sn; + side_t *s; + + brush->contents = CONTENTS_AREAPORTAL; + + for ( sn = 0; sn < brush->numsides; sn++ ) + { + s = brush->original_sides + sn; + //make sure the surfaces are not hint or skip +// s->surf &= ~(SURF_HINT|SURF_SKIP); + // + s->texinfo = 0; + s->contents = CONTENTS_AREAPORTAL; + } //end for +} //end of the function MakeAreaPortalBrush +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DPlanes2MapPlanes( void ) { + int i; + + for ( i = 0; i < numplanes; i++ ) + { + dplanes2mapplanes[i] = FindFloatPlane( dplanes[i].normal, dplanes[i].dist, 0, NULL ); + } //end for +} //end of the function DPlanes2MapPlanes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MarkVisibleBrushSides( mapbrush_t *brush ) { + int n, i, planenum; + side_t *side; + dface_t *face; + // + for ( n = 0; n < brush->numsides; n++ ) + { + side = brush->original_sides + n; + //if this side is a bevel or the leaf number of the brush is unknown + if ( ( side->flags & SFL_BEVEL ) || brush->leafnum < 0 ) { + //this side is a valid splitter + side->flags |= SFL_VISIBLE; + continue; + } //end if + //assum this side will not be used as a splitter + side->flags &= ~SFL_VISIBLE; + //check if the side plane is used by a visible face + for ( i = 0; i < numfaces; i++ ) + { + face = &dfaces[i]; + planenum = dplanes2mapplanes[face->planenum]; + if ( ( planenum & ~1 ) == ( side->planenum & ~1 ) ) { + //this side is a valid splitter + side->flags |= SFL_VISIBLE; + } //end if + } //end for + } //end for +} //end of the function MarkVisibleBrushSides + +#endif //ME + +/* +================= +Q2_ParseBrush +================= +*/ +void Q2_ParseBrush( script_t *script, entity_t *mapent ) { + mapbrush_t *b; + int i, j, k; + int mt; + side_t *side, *s2; + int planenum; + brush_texture_t td; + int planepts[3][3]; + token_t token; + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "nummapbrushes == MAX_MAPFILE_BRUSHES" ); + } + + b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = num_entities - 1; + b->brushnum = nummapbrushes - mapent->firstbrush; + b->leafnum = -1; + + do + { + if ( !PS_ReadToken( script, &token ) ) { + break; + } + if ( !strcmp( token.string, "}" ) ) { + break; + } + + //IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could + // lead to out of bound indexing of the arrays + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } + side = &brushsides[nummapbrushsides]; + + //read the three point plane definition + for ( i = 0; i < 3; i++ ) + { + if ( i != 0 ) { + PS_ExpectTokenString( script, "(" ); + } + for ( j = 0; j < 3; j++ ) + { + PS_ExpectAnyToken( script, &token ); + planepts[i][j] = atof( token.string ); + } //end for + PS_ExpectTokenString( script, ")" ); + } //end for + + // + //read the texturedef + // + PS_ExpectAnyToken( script, &token ); + strcpy( td.name, token.string ); + + PS_ExpectAnyToken( script, &token ); + td.shift[0] = atol( token.string ); + PS_ExpectAnyToken( script, &token ); + td.shift[1] = atol( token.string ); + PS_ExpectAnyToken( script, &token ); + td.rotate = atol( token.string ); + PS_ExpectAnyToken( script, &token ); + td.scale[0] = atof( token.string ); + PS_ExpectAnyToken( script, &token ); + td.scale[1] = atof( token.string ); + + //find default flags and values + mt = FindMiptex( td.name ); + td.flags = textureref[mt].flags; + td.value = textureref[mt].value; + side->contents = textureref[mt].contents; + side->surf = td.flags = textureref[mt].flags; + + //check if there's a number available + if ( PS_CheckTokenType( script, TT_NUMBER, 0, &token ) ) { + side->contents = token.intvalue; + PS_ExpectTokenType( script, TT_NUMBER, 0, &token ); + side->surf = td.flags = token.intvalue; + PS_ExpectTokenType( script, TT_NUMBER, 0, &token ); + td.value = token.intvalue; + } + + // translucent objects are automatically classified as detail +// if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) +// side->contents |= CONTENTS_DETAIL; + if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + side->contents |= CONTENTS_DETAIL; + } + if ( fulldetail ) { + side->contents &= ~CONTENTS_DETAIL; + } + if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 ) + | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST ) ) ) { + side->contents |= CONTENTS_SOLID; + } + + // hints and skips are never detail, and have no content +// if (side->surf & (SURF_HINT|SURF_SKIP) ) +// { +// side->contents = 0; +// side->surf &= ~CONTENTS_DETAIL; +// } + +#ifdef ME + //for creating AAS... this side is textured + side->flags |= SFL_TEXTURED; +#endif //ME + // + // find the plane number + // + planenum = PlaneFromPoints( planepts[0], planepts[1], planepts[2] ); + if ( planenum == -1 ) { + Log_Print( "Entity %i, Brush %i: plane with no normal\n" + , b->entitynum, b->brushnum ); + continue; + } + + // + // see if the plane has been used already + // + for ( k = 0 ; k < b->numsides ; k++ ) + { + s2 = b->original_sides + k; + if ( s2->planenum == planenum ) { + Log_Print( "Entity %i, Brush %i: duplicate plane\n" + , b->entitynum, b->brushnum ); + break; + } + if ( s2->planenum == ( planenum ^ 1 ) ) { + Log_Print( "Entity %i, Brush %i: mirrored plane\n" + , b->entitynum, b->brushnum ); + break; + } + } + if ( k != b->numsides ) { + continue; // duplicated + + } + // + // keep this side + // + + side = b->original_sides + b->numsides; + side->planenum = planenum; + side->texinfo = TexinfoForBrushTexture( &mapplanes[planenum], + &td, vec3_origin ); + + // save the td off in case there is an origin brush and we + // have to recalculate the texinfo + side_brushtextures[nummapbrushsides] = td; + + nummapbrushsides++; + b->numsides++; + } while ( 1 ); + + // get the content for the entire brush + b->contents = Q2_BrushContents( b ); + +#ifdef ME + if ( BrushExists( b ) ) { + c_squattbrushes++; + b->numsides = 0; + return; + } //end if + + if ( create_aas ) { + //create AAS brushes, and add brush bevels + AAS_CreateMapBrushes( b, mapent, true ); + //NOTE: if we return here then duplicate plane errors occur for the non world entities + return; + } //end if +#endif //ME + + // allow detail brushes to be removed + if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) { + b->numsides = 0; + return; + } + + // allow water brushes to be removed + if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { + b->numsides = 0; + return; + } + + // create windings for sides and bounds for brush + MakeBrushWindings( b ); + + // brushes that will not be visible at all will never be + // used as bsp splitters + if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + c_clipbrushes++; + for ( i = 0 ; i < b->numsides ; i++ ) + b->original_sides[i].texinfo = TEXINFO_NODE; + } + + // + // origin brushes are removed, but they set + // the rotation origin for the rest of the brushes + // in the entity. After the entire entity is parsed, + // the planenums and texinfos will be adjusted for + // the origin brush + // + if ( b->contents & CONTENTS_ORIGIN ) { + char string[32]; + vec3_t origin; + + if ( num_entities == 1 ) { + Error( "Entity %i, Brush %i: origin brushes not allowed in world" + , b->entitynum, b->brushnum ); + return; + } + + VectorAdd( b->mins, b->maxs, origin ); + VectorScale( origin, 0.5, origin ); + + sprintf( string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] ); + SetKeyValue( &entities[b->entitynum], "origin", string ); + + VectorCopy( origin, entities[b->entitynum].origin ); + + // don't keep this brush + b->numsides = 0; + + return; + } + + AddBrushBevels( b ); + + nummapbrushes++; + mapent->numbrushes++; +} + +/* +================ +Q2_MoveBrushesToWorld + +Takes all of the brushes from the current entity and +adds them to the world's brush list. + +Used by func_group and func_areaportal +================ +*/ +void Q2_MoveBrushesToWorld( entity_t *mapent ) { + int newbrushes; + int worldbrushes; + mapbrush_t *temp; + int i; + + // this is pretty gross, because the brushes are expected to be + // in linear order for each entity + + newbrushes = mapent->numbrushes; + worldbrushes = entities[0].numbrushes; + + temp = GetMemory( newbrushes * sizeof( mapbrush_t ) ); + memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) ); + +#if 0 // let them keep their original brush numbers + for ( i = 0 ; i < newbrushes ; i++ ) + temp[i].entitynum = 0; +#endif + + // make space to move the brushes (overlapped copy) + memmove( mapbrushes + worldbrushes + newbrushes, + mapbrushes + worldbrushes, + sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) ); + + // copy the new brushes down + memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes ); + + // fix up indexes + entities[0].numbrushes += newbrushes; + for ( i = 1 ; i < num_entities ; i++ ) + entities[i].firstbrush += newbrushes; + FreeMemory( temp ); + + mapent->numbrushes = 0; +} + +/* +================ +Q2_ParseMapEntity +================ +*/ +qboolean Q2_ParseMapEntity( script_t *script ) { + entity_t *mapent; + epair_t *e; + side_t *s; + int i, j; + int startbrush, startsides; + vec_t newdist; + mapbrush_t *b; + token_t token; + + if ( !PS_ReadToken( script, &token ) ) { + return false; + } + + if ( strcmp( token.string, "{" ) ) { + Error( "ParseEntity: { not found" ); + } + + if ( num_entities == MAX_MAP_ENTITIES ) { + Error( "num_entities == MAX_MAP_ENTITIES" ); + } + + startbrush = nummapbrushes; + startsides = nummapbrushsides; + + mapent = &entities[num_entities]; + num_entities++; + memset( mapent, 0, sizeof( *mapent ) ); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; +// mapent->portalareas[0] = -1; +// mapent->portalareas[1] = -1; + + do + { + if ( !PS_ReadToken( script, &token ) ) { + Error( "ParseEntity: EOF without closing brace" ); + } //end if + if ( !strcmp( token.string, "}" ) ) { + break; + } + if ( !strcmp( token.string, "{" ) ) { + Q2_ParseBrush( script, mapent ); + } //end if + else + { + PS_UnreadLastToken( script ); + e = ParseEpair( script ); + e->next = mapent->epairs; + mapent->epairs = e; + } //end else + } while ( 1 ); + + GetVectorForKey( mapent, "origin", mapent->origin ); + + // + // if there was an origin brush, offset all of the planes and texinfo + // + if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) { + for ( i = 0 ; i < mapent->numbrushes ; i++ ) + { + b = &mapbrushes[mapent->firstbrush + i]; + for ( j = 0 ; j < b->numsides ; j++ ) + { + s = &b->original_sides[j]; + newdist = mapplanes[s->planenum].dist - + DotProduct( mapplanes[s->planenum].normal, mapent->origin ); + s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist, 0, NULL ); + s->texinfo = TexinfoForBrushTexture( &mapplanes[s->planenum], + &side_brushtextures[s - brushsides], mapent->origin ); + } + MakeBrushWindings( b ); + } + } + + // group entities are just for editor convenience + // toss all brushes into the world entity + if ( !strcmp( "func_group", ValueForKey( mapent, "classname" ) ) ) { + Q2_MoveBrushesToWorld( mapent ); + mapent->numbrushes = 0; + return true; + } + + // areaportal entities move their brushes, but don't eliminate + // the entity + if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { + char str[128]; + + if ( mapent->numbrushes != 1 ) { + Error( "Entity %i: func_areaportal can only be a single brush", num_entities - 1 ); + } + + b = &mapbrushes[nummapbrushes - 1]; + b->contents = CONTENTS_AREAPORTAL; + c_areaportals++; + mapent->areaportalnum = c_areaportals; + // set the portal number as "style" + sprintf( str, "%i", c_areaportals ); + SetKeyValue( mapent, "style", str ); + Q2_MoveBrushesToWorld( mapent ); + return true; + } + + return true; +} + +//=================================================================== + +/* +================ +LoadMapFile +================ +*/ +void Q2_LoadMapFile( char *filename ) { + int i; + script_t *script; + + Log_Print( "-- Q2_LoadMapFile --\n" ); +#ifdef ME + //loaded map type + loadedmaptype = MAPTYPE_QUAKE2; + //reset the map loading + ResetMapLoading(); +#endif //ME + + script = LoadScriptFile( filename ); + if ( !script ) { + Log_Print( "couldn't open %s\n", filename ); + return; + } //end if + //white spaces and escape characters inside a string are not allowed + SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | + SCFL_NOSTRINGESCAPECHARS | + SCFL_PRIMITIVE ); + + nummapbrushsides = 0; + num_entities = 0; + + while ( Q2_ParseMapEntity( script ) ) + { + } + + ClearBounds( map_mins, map_maxs ); + for ( i = 0 ; i < entities[0].numbrushes ; i++ ) + { + if ( mapbrushes[i].mins[0] > 4096 ) { + continue; // no valid points + } + AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); + AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); + } //end for + + PrintMapInfo(); + + //free the script + FreeScript( script ); +// TestExpandBrushes (); + // + Q2_CreateMapTexinfo(); +} //end of the function Q2_LoadMapFile + +#ifdef ME //Begin MAP loading from BSP file +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_SetLeafBrushesModelNumbers( int leafnum, int modelnum ) { + int i, brushnum; + dleaf_t *leaf; + + leaf = &dleafs[leafnum]; + for ( i = 0; i < leaf->numleafbrushes; i++ ) + { + brushnum = dleafbrushes[leaf->firstleafbrush + i]; + brushmodelnumbers[brushnum] = modelnum; + dbrushleafnums[brushnum] = leafnum; + } //end for +} //end of the function Q2_SetLeafBrushesModelNumbers +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_InitNodeStack( void ) { + nodestackptr = nodestack; + nodestacksize = 0; +} //end of the function Q2_InitNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_PushNodeStack( int num ) { + *nodestackptr = num; + nodestackptr++; + nodestacksize++; + // + if ( nodestackptr >= &nodestack[NODESTACKSIZE] ) { + Error( "Q2_PushNodeStack: stack overflow\n" ); + } //end if +} //end of the function Q2_PushNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Q2_PopNodeStack( void ) { + //if the stack is empty + if ( nodestackptr <= nodestack ) { + return -1; + } + //decrease stack pointer + nodestackptr--; + nodestacksize--; + //return the top value from the stack + return *nodestackptr; +} //end of the function Q2_PopNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_SetBrushModelNumbers( entity_t *mapent ) { + int n, pn; + int leafnum; + + // + Q2_InitNodeStack(); + //head node (root) of the bsp tree + n = dmodels[mapent->modelnum].headnode; + pn = 0; + + do + { + //if we are in a leaf (negative node number) + if ( n < 0 ) { + //number of the leaf + leafnum = ( -n ) - 1; + //set the brush numbers + Q2_SetLeafBrushesModelNumbers( leafnum, mapent->modelnum ); + //walk back into the tree to find a second child to continue with + for ( pn = Q2_PopNodeStack(); pn >= 0; n = pn, pn = Q2_PopNodeStack() ) + { + //if we took the first child at the parent node + if ( dnodes[pn].children[0] == n ) { + break; + } + } //end for + //if the stack wasn't empty (if not processed whole tree) + if ( pn >= 0 ) { + //push the parent node again + Q2_PushNodeStack( pn ); + //we proceed with the second child of the parent node + n = dnodes[pn].children[1]; + } //end if + } //end if + else + { + //push the current node onto the stack + Q2_PushNodeStack( n ); + //walk forward into the tree to the first child + n = dnodes[n].children[0]; + } //end else + } while ( pn >= 0 ); +} //end of the function Q2_SetBrushModelNumbers +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_BSPBrushToMapBrush( dbrush_t *bspbrush, entity_t *mapent ) { + mapbrush_t *b; + int i, k, n; + side_t *side, *s2; + int planenum; + dbrushside_t *bspbrushside; + dplane_t *bspplane; + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "nummapbrushes >= MAX_MAPFILE_BRUSHES" ); + } + + b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = mapent - entities; + b->brushnum = nummapbrushes - mapent->firstbrush; + b->leafnum = dbrushleafnums[bspbrush - dbrushes]; + + for ( n = 0; n < bspbrush->numsides; n++ ) + { + //pointer to the bsp brush side + bspbrushside = &dbrushsides[bspbrush->firstside + n]; + + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } //end if + //pointer to the map brush side + side = &brushsides[nummapbrushsides]; + //if the BSP brush side is textured + if ( brushsidetextured[bspbrush->firstside + n] ) { + side->flags |= SFL_TEXTURED; + } else { side->flags &= ~SFL_TEXTURED;} + //ME: can get side contents and surf directly from BSP file + side->contents = bspbrush->contents; + //if the texinfo is TEXINFO_NODE + if ( bspbrushside->texinfo < 0 ) { + side->surf = 0; + } else { side->surf = texinfo[bspbrushside->texinfo].flags;} + + // translucent objects are automatically classified as detail +// if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) +// side->contents |= CONTENTS_DETAIL; + if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + side->contents |= CONTENTS_DETAIL; + } + if ( fulldetail ) { + side->contents &= ~CONTENTS_DETAIL; + } + if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 ) + | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST ) ) ) { + side->contents |= CONTENTS_SOLID; + } + + // hints and skips are never detail, and have no content +// if (side->surf & (SURF_HINT|SURF_SKIP) ) +// { +// side->contents = 0; +// side->surf &= ~CONTENTS_DETAIL; +// } + + //ME: get a plane for this side + bspplane = &dplanes[bspbrushside->planenum]; + planenum = FindFloatPlane( bspplane->normal, bspplane->dist, 0, NULL ); + // + // see if the plane has been used already + // + //ME: this really shouldn't happen!!! + //ME: otherwise the bsp file is corrupted?? + //ME: still it seems to happen, maybe Johny Boy's + //ME: brush bevel adding is crappy ? + for ( k = 0; k < b->numsides; k++ ) + { + s2 = b->original_sides + k; +// if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999 +// && fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 ) + + if ( s2->planenum == planenum ) { + Log_Print( "Entity %i, Brush %i: duplicate plane\n" + , b->entitynum, b->brushnum ); + break; + } + if ( s2->planenum == ( planenum ^ 1 ) ) { + Log_Print( "Entity %i, Brush %i: mirrored plane\n" + , b->entitynum, b->brushnum ); + break; + } + } + if ( k != b->numsides ) { + continue; // duplicated + + } + // + // keep this side + // + //ME: reset pointer to side, why? hell I dunno (pointer is set above already) + side = b->original_sides + b->numsides; + //ME: store the plane number + side->planenum = planenum; + //ME: texinfo is already stored when bsp is loaded + //NOTE: check for TEXINFO_NODE, otherwise crash in Q2_BrushContents + if ( bspbrushside->texinfo < 0 ) { + side->texinfo = 0; + } else { side->texinfo = bspbrushside->texinfo;} + + // save the td off in case there is an origin brush and we + // have to recalculate the texinfo + // ME: don't need to recalculate because it's already done + // (for non-world entities) in the BSP file +// side_brushtextures[nummapbrushsides] = td; + + nummapbrushsides++; + b->numsides++; + } //end for + + // get the content for the entire brush + b->contents = bspbrush->contents; + Q2_BrushContents( b ); + + if ( BrushExists( b ) ) { + c_squattbrushes++; + b->numsides = 0; + return; + } //end if + + //if we're creating AAS + if ( create_aas ) { + //create the AAS brushes from this brush, don't add brush bevels + AAS_CreateMapBrushes( b, mapent, false ); + return; + } //end if + + // allow detail brushes to be removed + if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) { + b->numsides = 0; + return; + } //end if + + // allow water brushes to be removed + if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { + b->numsides = 0; + return; + } //end if + + // create windings for sides and bounds for brush + MakeBrushWindings( b ); + + //mark brushes without winding or with a tiny window as bevels + MarkBrushBevels( b ); + + // brushes that will not be visible at all will never be + // used as bsp splitters + if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + c_clipbrushes++; + for ( i = 0; i < b->numsides; i++ ) + b->original_sides[i].texinfo = TEXINFO_NODE; + } //end for + + // + // origin brushes are removed, but they set + // the rotation origin for the rest of the brushes + // in the entity. After the entire entity is parsed, + // the planenums and texinfos will be adjusted for + // the origin brush + // + //ME: not needed because the entities in the BSP file already + // have an origin set +// if (b->contents & CONTENTS_ORIGIN) +// { +// char string[32]; +// vec3_t origin; +// +// if (num_entities == 1) +// { +// Error ("Entity %i, Brush %i: origin brushes not allowed in world" +// , b->entitynum, b->brushnum); +// return; +// } +// +// VectorAdd (b->mins, b->maxs, origin); +// VectorScale (origin, 0.5, origin); +// +// sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); +// SetKeyValue (&entities[b->entitynum], "origin", string); +// +// VectorCopy (origin, entities[b->entitynum].origin); +// +// // don't keep this brush +// b->numsides = 0; +// +// return; +// } + + //ME: the bsp brushes already have bevels, so we won't try to + // add them again (especially since Johny Boy's bevel adding might + // be crappy) +// AddBrushBevels(b); + + nummapbrushes++; + mapent->numbrushes++; +} //end of the function Q2_BSPBrushToMapBrush +//=========================================================================== +//=========================================================================== +void Q2_ParseBSPBrushes( entity_t *mapent ) { + int i; + + //give all the brushes that belong to this entity the number of the + //BSP model used by this entity + Q2_SetBrushModelNumbers( mapent ); + //now parse all the brushes with the correct mapent->modelnum + for ( i = 0; i < numbrushes; i++ ) + { + if ( brushmodelnumbers[i] == mapent->modelnum ) { + Q2_BSPBrushToMapBrush( &dbrushes[i], mapent ); + } //end if + } //end for +} //end of the function Q2_ParseBSPBrushes +//=========================================================================== +//=========================================================================== +qboolean Q2_ParseBSPEntity( int entnum ) { + entity_t *mapent; + char *model; + int startbrush, startsides; + + startbrush = nummapbrushes; + startsides = nummapbrushsides; + + mapent = &entities[entnum]; //num_entities]; + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + mapent->modelnum = -1; //-1 = no model + + model = ValueForKey( mapent, "model" ); + if ( model && strlen( model ) ) { + if ( *model != '*' ) { + Error( "Q2_ParseBSPEntity: model number without leading *" ); + } //end if + //get the model number of this entity (skip the leading *) + mapent->modelnum = atoi( &model[1] ); + } //end if + + GetVectorForKey( mapent, "origin", mapent->origin ); + + //if this is the world entity it has model number zero + //the world entity has no model key + if ( !strcmp( "worldspawn", ValueForKey( mapent, "classname" ) ) ) { + mapent->modelnum = 0; + } //end if + //if the map entity has a BSP model (a modelnum of -1 is used for + //entities that aren't using a BSP model) + if ( mapent->modelnum >= 0 ) { + //parse the bsp brushes + Q2_ParseBSPBrushes( mapent ); + } //end if + // + //the origin of the entity is already taken into account + // + //func_group entities can't be in the bsp file + // + //check out the func_areaportal entities + if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { + c_areaportals++; + mapent->areaportalnum = c_areaportals; + return true; + } //end if + return true; +} //end of the function Q2_ParseBSPEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q2_LoadMapFromBSP( char *filename, int offset, int length ) { + int i; + + Log_Print( "-- Q2_LoadMapFromBSP --\n" ); + //loaded map type + loadedmaptype = MAPTYPE_QUAKE2; + + Log_Print( "Loading map from %s...\n", filename ); + //load the bsp file + Q2_LoadBSPFile( filename, offset, length ); + + //create an index from bsp planes to map planes + //DPlanes2MapPlanes(); + //clear brush model numbers + for ( i = 0; i < MAX_MAPFILE_BRUSHES; i++ ) + brushmodelnumbers[i] = -1; + + nummapbrushsides = 0; + num_entities = 0; + + Q2_ParseEntities(); + // + for ( i = 0; i < num_entities; i++ ) + { + Q2_ParseBSPEntity( i ); + } //end for + + //get the map mins and maxs from the world model + ClearBounds( map_mins, map_maxs ); + for ( i = 0; i < entities[0].numbrushes; i++ ) + { + if ( mapbrushes[i].mins[0] > 4096 ) { + continue; //no valid points + } + AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); + AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); + } //end for + + PrintMapInfo(); + // + Q2_CreateMapTexinfo(); +} //end of the function Q2_LoadMapFromBSP + +void Q2_ResetMapLoading( void ) { + //reset for map loading from bsp + memset( nodestack, 0, NODESTACKSIZE * sizeof( int ) ); + nodestackptr = NULL; + nodestacksize = 0; + memset( brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof( int ) ); +} //end of the function Q2_ResetMapLoading + +//End MAP loading from BSP file +#endif //ME + +//==================================================================== + +/* +================ +TestExpandBrushes + +Expands all the brush planes and saves a new map out +================ +*/ +void TestExpandBrushes( void ) { + FILE *f; + side_t *s; + int i, j, bn; + winding_t *w; + char *name = "expanded.map"; + mapbrush_t *brush; + vec_t dist; + + Log_Print( "writing %s\n", name ); + f = fopen( name, "wb" ); + if ( !f ) { + Error( "Can't write %s\n", name ); + } + + fprintf( f, "{\n\"classname\" \"worldspawn\"\n" ); + + for ( bn = 0 ; bn < nummapbrushes ; bn++ ) + { + brush = &mapbrushes[bn]; + fprintf( f, "{\n" ); + for ( i = 0 ; i < brush->numsides ; i++ ) + { + s = brush->original_sides + i; + dist = mapplanes[s->planenum].dist; + for ( j = 0 ; j < 3 ; j++ ) + dist += fabs( 16 * mapplanes[s->planenum].normal[j] ); + + w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist ); + + fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] ); + fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] ); + fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] ); + + fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture ); + FreeWinding( w ); + } + fprintf( f, "}\n" ); + } + fprintf( f, "}\n" ); + + fclose( f ); + + Error( "can't proceed after expanding brushes" ); +} //end of the function TestExpandBrushes + diff --git a/src/bspc/map_q3.c b/src/bspc/map_q3.c new file mode 100644 index 0000000..66864ef --- /dev/null +++ b/src/bspc/map_q3.c @@ -0,0 +1,679 @@ +/* +=========================================================================== + +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: map_q3.c +// Function: map loading +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1999-07-02 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_mem.h" +#include "..\botlib\aasfile.h" //aas_bbox_t +#include "aas_store.h" //AAS_MAX_BBOXES +#include "aas_cfg.h" +#include "aas_map.h" //AAS_CreateMapBrushes +#include "l_bsp_q3.h" +#include "..\qcommon\cm_patch.h" +#include "..\game\surfaceflags.h" + +#ifdef ME + +#define NODESTACKSIZE 1024 + +int nodestack[NODESTACKSIZE]; +int *nodestackptr; +int nodestacksize = 0; +int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; +int dbrushleafnums[MAX_MAPFILE_BRUSHES]; +int dplanes2mapplanes[MAX_MAPFILE_PLANES]; + +#endif //ME + +int numtexinfo; +texinfo_t texinfo[MAX_MAP_TEXINFO]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintContents( int contents ); + +int Q3_BrushContents( mapbrush_t *b ) { + int contents, i, mixed, hint; + side_t *s; + + s = &b->original_sides[0]; + contents = s->contents; + // + mixed = false; + hint = false; + for ( i = 1; i < b->numsides; i++ ) + { + s = &b->original_sides[i]; + if ( s->contents != contents ) { + mixed = true; + } + if ( s->surf & ( SURF_HINT | SURF_SKIP ) ) { + hint = true; + } + contents |= s->contents; + } //end for + // + if ( hint ) { + if ( contents ) { + Log_Write( "WARNING: hint brush with contents: " ); + PrintContents( contents ); + Log_Write( "\r\n" ); + // + Log_Write( "brush contents is: " ); + PrintContents( b->contents ); + Log_Write( "\r\n" ); + } //end if + return 0; + } //end if + //Log_Write("brush %d contents ", nummapbrushes); + //PrintContents(contents); + //Log_Write("\r\n"); + //remove ladder and fog contents + contents &= ~( /*CONTENTS_LADDER|*/ CONTENTS_FOG ); + // + if ( mixed ) { + Log_Write( "Entity %i, Brush %i: mixed face contents " + , b->entitynum, b->brushnum ); + PrintContents( contents ); + Log_Write( "\r\n" ); + // + Log_Write( "brush contents is: " ); + PrintContents( b->contents ); + Log_Write( "\r\n" ); + // + if ( contents & CONTENTS_DONOTENTER ) { + return CONTENTS_DONOTENTER; //Log_Print("mixed contents with donotenter\n"); + } + /* + Log_Print("contents:"); PrintContents(contents); + Log_Print("\ncontents:"); PrintContents(s->contents); + Log_Print("\n"); + Log_Print("texture name = %s\n", texinfo[s->texinfo].texture); + */ + //if liquid brush + if ( contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { + return ( contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ); + } //end if + if ( contents & CONTENTS_PLAYERCLIP ) { + return ( contents & CONTENTS_PLAYERCLIP ); + } + return ( contents & CONTENTS_SOLID ); + } //end if + /* + if (contents & CONTENTS_AREAPORTAL) + { + static int num; + Log_Write("Entity %i, Brush %i: area portal %d\r\n", b->entitynum, b->brushnum, num++); + } //end if*/ + if ( contents == ( contents & CONTENTS_STRUCTURAL ) ) { + //Log_Print("brush %i is only structural\n", b->brushnum); + contents = 0; + } //end if + if ( contents & CONTENTS_DONOTENTER ) { + Log_Print( "brush %i is a donotenter brush, c = %X\n", b->brushnum, contents ); + } //end if + return contents; +} //end of the function Q3_BrushContents +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_DPlanes2MapPlanes( void ) { + int i; + + for ( i = 0; i < q3_numplanes; i++ ) { + dplanes2mapplanes[i] = FindFloatPlane( q3_dplanes[i].normal, q3_dplanes[i].dist, 0, NULL ); + } +} +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_BSPBrushToMapBrush( q3_dbrush_t *bspbrush, entity_t *mapent ) { + mapbrush_t *b; + int i, k, n; + side_t *side /*, *s2*/; + q3_dbrushside_t *bspbrushside; + q3_dplane_t *bspplane; + int contentFlags = 0; + const char* classname; + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "nummapbrushes >= MAX_MAPFILE_BRUSHES" ); + } + + b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = mapent - entities; + b->brushnum = nummapbrushes - mapent->firstbrush; + b->leafnum = dbrushleafnums[bspbrush - q3_dbrushes]; + + classname = ValueForKey( &entities[b->entitynum], "classname" ); + + if ( !strcmp( "func_invisible_user", classname ) || !strcmp( "script_mover", classname ) || !strcmp( "trigger_objective_info", classname ) ) { + Log_Print( "Ignoring %s brush..\n", classname ); + b->numsides = 0; + b->contents = 0; + return; + } + + for ( n = 0; n < bspbrush->numSides; n++ ) { + //pointer to the bsp brush side + bspbrushside = &q3_dbrushsides[bspbrush->firstSide + n]; + + for ( k = n + 1; k < bspbrush->numSides; k++ ) { + if ( q3_dbrushsides[bspbrush->firstSide + k].planeNum == bspbrushside->planeNum ) { + break; + } + } + if ( k != bspbrush->numSides ) { + Log_Print( "\nEntity %i, Brush %i: duplicate plane\n", b->entitynum, b->brushnum ); + continue; // duplicated + } + + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } //end if + //pointer to the map brush side + side = &brushsides[nummapbrushsides]; + //if the BSP brush side is textured + if ( q3_dbrushsidetextured[bspbrush->firstSide + n] ) { + side->flags |= SFL_TEXTURED | SFL_VISIBLE; + } else { + side->flags &= ~SFL_TEXTURED; + } + + //NOTE: all Quake3 sides are assumed textured + //side->flags |= SFL_TEXTURED|SFL_VISIBLE; + // + + if ( bspbrushside->shaderNum < 0 ) { + side->contents = 0; + side->surf = 0; + } else { + side->contents = q3_dshaders[bspbrushside->shaderNum].contentFlags; + side->surf = q3_dshaders[bspbrushside->shaderNum].surfaceFlags; + +/* if (strstr(q3_dshaders[bspbrushside->shaderNum].shader, "common/hint")) { + //Log_Print("found hint side\n"); + side->surf |= SURF_HINT; + } //end if*/ + + // Ridah, mark ladder brushes + if ( ( q3_dshaders[bspbrushside->shaderNum].surfaceFlags & SURF_LADDER ) ) { + //Log_Print("found ladder side\n"); + side->contents |= CONTENTS_LADDER; + contentFlags |= CONTENTS_LADDER; + } //end if + // done. + + } //end else + // + +/* if (!(strstr(q3_dshaders[bspbrushside->shaderNum].shader, "common/slip"))) { + side->flags |= SFL_VISIBLE; + } else if (side->surf & SURF_NODRAW) { + side->flags |= SFL_TEXTURED | SFL_VISIBLE; + }*/ + + // hints and skips are never detail, and have no content + if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) { + side->contents = 0; + //Log_Print("found hint brush side\n"); + } + + //ME: get a plane for this side + bspplane = &q3_dplanes[bspbrushside->planeNum]; + if ( side->winding ) { + side->planenum = FindFloatPlane( bspplane->normal, bspplane->dist, side->winding->numpoints, side->winding->p ); + } else { + side->planenum = FindFloatPlane( bspplane->normal, bspplane->dist, 0, NULL ); + } + // + // see if the plane has been used already + // + //ME: this really shouldn't happen!!! + //ME: otherwise the bsp file is corrupted?? + //ME: still it seems to happen, maybe Johny Boy's + //ME: brush bevel adding is crappy ? +/* for (k = 0; k < b->numsides; k++) { + s2 = b->original_sides + k; + + if (s2->planenum == side->planenum) { + Log_Print("Entity %i, Brush %i: duplicate plane\n", b->entitynum, b->brushnum); + break; + } + if ( s2->planenum == (side->planenum^1) ) { + Log_Print("Entity %i, Brush %i: mirrored plane\n", b->entitynum, b->brushnum); + break; + } + } + + if (k != b->numsides) + continue; // duplicated*/ + + // + // keep this side + // + nummapbrushsides++; + b->numsides++; + } //end for + + // get the content for the entire brush + //Quake3 bsp brushes don't have a contents + b->contents = q3_dshaders[bspbrush->shaderNum].contentFlags | contentFlags; + // Ridah, Wolf has ladders (if we call Q3_BrushContents(), we'll get the solid area bug + b->contents &= ~( /*CONTENTS_LADDER|*/ CONTENTS_FOG | CONTENTS_STRUCTURAL ); + //b->contents = Q3_BrushContents(b); + // RF, only allow known contents + b->contents &= ( CONTENTS_SOLID | CONTENTS_LADDER | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER | CONTENTS_AREAPORTAL | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_DETAIL | CONTENTS_CLUSTERPORTAL ); + // + // Ridah, CONTENTS_MOSTERCLIP should prevent AAS from being created, but not clip players/AI in the game + if ( b->contents & CONTENTS_MONSTERCLIP ) { + b->contents |= CONTENTS_PLAYERCLIP; + } + + // RF, optimization, if the brush is one of the following kind, make it standard solid + //if (b->contents & (CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP)) b->contents = CONTENTS_SOLID; + +/* if (BrushExists(b)) + { + c_squattbrushes++; + b->numsides = 0; + return; + } //end if*/ + + //if we're creating AAS + if ( create_aas ) { + // create the AAS brushes from this brush, don't add brush bevels because the .bsp brush already has them + AAS_CreateMapBrushes( b, mapent, false ); + return; + } //end if + + // allow detail brushes to be removed + if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) { + b->numsides = 0; + return; + } //end if + + // allow water brushes to be removed + if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { + b->numsides = 0; + return; + } //end if + + + // create windings for sides and bounds for brush + MakeBrushWindings( b ); + + //mark brushes without winding or with a tiny window as bevels + MarkBrushBevels( b ); + + // brushes that will not be visible at all will never be + // used as bsp splitters + if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + c_clipbrushes++; + for ( i = 0; i < b->numsides; i++ ) + b->original_sides[i].texinfo = TEXINFO_NODE; + } //end for + + nummapbrushes++; + mapent->numbrushes++; +} //end of the function Q3_BSPBrushToMapBrush +//=========================================================================== +//=========================================================================== +void Q3_ParseBSPBrushes( entity_t *mapent ) { + int i; + + /* + //give all the brushes that belong to this entity the number of the + //BSP model used by this entity + Q3_SetBrushModelNumbers(mapent); + //now parse all the brushes with the correct mapent->modelnum + for (i = 0; i < q3_numbrushes; i++) + { + if (brushmodelnumbers[i] == mapent->modelnum) + { + Q3_BSPBrushToMapBrush(&q3_dbrushes[i], mapent); + } //end if + } //end for + */ + qprintf( "Parsing Entity: %5d\n", mapent - entities ); + for ( i = 0; i < q3_dmodels[mapent->modelnum].numBrushes; i++ ) + { + qprintf( "\rBrush: %5d/%5d", i, q3_dmodels[mapent->modelnum].numBrushes ); + Q3_BSPBrushToMapBrush( &q3_dbrushes[q3_dmodels[mapent->modelnum].firstBrush + i], mapent ); + } //end for +} //end of the function Q3_ParseBSPBrushes +//=========================================================================== +//=========================================================================== +qboolean Q3_ParseBSPEntity( int entnum ) { + entity_t *mapent; + char *model; + int startbrush, startsides; + +// RF, just for debugging + char target[1024]; + if ( !strcmp( "func_constructible", ValueForKey( &entities[entnum], "classname" ) ) ) { + strcpy( target, ValueForKey( &entities[entnum], "targetname" ) ); + model = model; + } + + startbrush = nummapbrushes; + startsides = nummapbrushsides; + + mapent = &entities[entnum]; //num_entities]; + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + mapent->modelnum = -1; //-1 = no BSP model + + model = ValueForKey( mapent, "model" ); + if ( model && strlen( model ) ) { + if ( *model == '*' ) { + //get the model number of this entity (skip the leading *) + mapent->modelnum = atoi( &model[1] ); + } //end if + } //end if + + GetVectorForKey( mapent, "origin", mapent->origin ); + + //if this is the world entity it has model number zero + //the world entity has no model key + if ( !strcmp( "worldspawn", ValueForKey( mapent, "classname" ) ) ) { + mapent->modelnum = 0; + } //end if + //if the map entity has a BSP model (a modelnum of -1 is used for + //entities that aren't using a BSP model) + if ( mapent->modelnum >= 0 ) { + //parse the bsp brushes + Q3_ParseBSPBrushes( mapent ); + } //end if + // + //the origin of the entity is already taken into account + // + //func_group entities can't be in the bsp file + // + //check out the func_areaportal entities + if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { + c_areaportals++; + mapent->areaportalnum = c_areaportals; + return true; + } //end if + return true; +} //end of the function Q3_ParseBSPEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define MAX_PATCH_VERTS 2048 + +void AAS_CreateCurveBrushes( void ) { + int i, j, n, planenum, numcurvebrushes = 0; + q3_dsurface_t *surface; + q3_drawVert_t *dv_p; + vec3_t points[MAX_PATCH_VERTS]; + int width, height, c; + patchCollide_t *pc; + facet_t *facet; + mapbrush_t *brush; + side_t *side; + entity_t *mapent; + winding_t *winding; + + qprintf( "nummapbrushsides = %d\n", nummapbrushsides ); + mapent = &entities[0]; + for ( i = 0; i < q3_numDrawSurfaces; i++ ) + { + surface = &q3_drawSurfaces[i]; + + // Gordon: only on patches, other surface types use this for other stuff (foliage for example) + if ( surface->surfaceType != MST_PATCH ) { + continue; + } + if ( !surface->patchWidth ) { + continue; + } + //if the curve is not solid + if ( !( q3_dshaders[surface->shaderNum].contentFlags & ( CONTENTS_SOLID | CONTENTS_PLAYERCLIP ) ) ) { + //Log_Print("skipped non-solid curve\n"); + continue; + } //end if + // + width = surface->patchWidth; + height = surface->patchHeight; + c = width * height; + if ( c > MAX_PATCH_VERTS ) { + Error( "ParseMesh: MAX_PATCH_VERTS" ); + } //end if + + dv_p = q3_drawVerts + surface->firstVert; + for ( j = 0 ; j < c ; j++, dv_p++ ) + { + points[j][0] = dv_p->xyz[0]; + points[j][1] = dv_p->xyz[1]; + points[j][2] = dv_p->xyz[2]; + } //end for + // create the internal facet structure + pc = CM_GeneratePatchCollide( width, height, points, false ); + // + for ( j = 0; j < pc->numFacets; j++ ) + { + facet = &pc->facets[j]; + // + brush = &mapbrushes[nummapbrushes]; + brush->original_sides = &brushsides[nummapbrushsides]; + brush->entitynum = 0; + brush->brushnum = nummapbrushes - mapent->firstbrush; + // + brush->numsides = facet->numBorders + 2; + nummapbrushsides += brush->numsides; + brush->contents = CONTENTS_SOLID; + // + //qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes); + qprintf( "\r%6d curve brushes", ++numcurvebrushes ); + // + planenum = FindFloatPlane( pc->planes[facet->surfacePlane].plane, pc->planes[facet->surfacePlane].plane[3], 0, NULL ); + // + side = &brush->original_sides[0]; + side->planenum = planenum; + side->contents = CONTENTS_SOLID; + side->flags |= SFL_TEXTURED | SFL_VISIBLE | SFL_CURVE; + side->surf = 0; + // + side = &brush->original_sides[1]; + if ( create_aas ) { + //the plane is expanded later so it's not a problem that + //these first two opposite sides are coplanar + side->planenum = planenum ^ 1; + } //end if + else + { + side->planenum = FindFloatPlane( mapplanes[planenum ^ 1].normal, mapplanes[planenum ^ 1].dist + 1, 0, NULL ); + side->flags |= SFL_TEXTURED | SFL_VISIBLE; + } //end else + side->contents = CONTENTS_SOLID; + side->flags |= SFL_CURVE; + side->surf = 0; + // + winding = BaseWindingForPlane( mapplanes[side->planenum].normal, mapplanes[side->planenum].dist ); + for ( n = 0; n < facet->numBorders; n++ ) + { + //never use the surface plane as a border + if ( facet->borderPlanes[n] == facet->surfacePlane ) { + continue; + } + // + side = &brush->original_sides[2 + n]; + side->planenum = FindFloatPlane( pc->planes[facet->borderPlanes[n]].plane, pc->planes[facet->borderPlanes[n]].plane[3], 0, NULL ); + if ( facet->borderInward[n] ) { + side->planenum ^= 1; + } + side->contents = CONTENTS_SOLID; + side->flags |= SFL_TEXTURED | SFL_CURVE; + side->surf = 0; + //chop the winding in place + if ( winding ) { + ChopWindingInPlace( &winding, mapplanes[side->planenum ^ 1].normal, mapplanes[side->planenum ^ 1].dist, 0.1 ); //CLIP_EPSILON); + } + } //end for + //VectorCopy(pc->bounds[0], brush->mins); + //VectorCopy(pc->bounds[1], brush->maxs); + if ( !winding ) { + Log_Print( "WARNING: AAS_CreateCurveBrushes: no winding\n" ); + brush->numsides = 0; + continue; + } //end if + brush->original_sides[0].winding = winding; + WindingBounds( winding, brush->mins, brush->maxs ); + for ( n = 0; n < 3; n++ ) + { + //IDBUG: all the indexes into the mins and maxs were zero (not using i) + if ( brush->mins[n] < -MAX_MAP_BOUNDS || brush->maxs[n] > MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: bounds out of range\n", brush->entitynum, brush->brushnum ); + Log_Print( "brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n] ); + brush->numsides = 0; //remove the brush + break; + } //end if + if ( brush->mins[n] > MAX_MAP_BOUNDS || brush->maxs[n] < -MAX_MAP_BOUNDS ) { + Log_Print( "entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum ); + Log_Print( "brush->mins[%d] = %f, brush->maxs[%d] = %f\n", n, brush->mins[n], n, brush->maxs[n] ); + brush->numsides = 0; //remove the brush + break; + } //end if + } //end for + if ( create_aas ) { + // add bevels here because we told CM_GeneratePatchCollide not to add them + AddBrushBevels( brush ); + // create the AAS brushes from this brush, don't add bevels + AAS_CreateMapBrushes( brush, mapent, false ); + } //end if + else + { + // create windings for sides and bounds for brush + MakeBrushWindings( brush ); + AddBrushBevels( brush ); + nummapbrushes++; + mapent->numbrushes++; + } //end else + } //end for + } //end for + //qprintf("\r%6d curve brushes", nummapbrushsides);//++numcurvebrushes); + qprintf( "\r%6d curve brushes\n", numcurvebrushes ); +} //end of the function AAS_CreateCurveBrushes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AAS_ExpandMapBrush( mapbrush_t *brush, vec3_t mins, vec3_t maxs ); + +void Q3_LoadMapFromBSP( struct quakefile_s *qf ) { + int i; + vec3_t mins = {-1,-1,-1}, maxs = {1, 1, 1}; + + Log_Print( "-- Q3_LoadMapFromBSP --\n" ); + //loaded map type + loadedmaptype = MAPTYPE_QUAKE3; + + Log_Print( "Loading map from %s...\n", qf->filename ); + //load the bsp file + Q3_LoadBSPFile( qf ); + + //create an index from bsp planes to map planes + //DPlanes2MapPlanes(); + //clear brush model numbers + for ( i = 0; i < MAX_MAPFILE_BRUSHES; i++ ) + brushmodelnumbers[i] = -1; + + nummapbrushsides = 0; + num_entities = 0; + + Q3_ParseEntities(); + + for ( i = 0; i < num_entities; i++ ) + { + Q3_ParseBSPEntity( i ); + } //end for + + if ( !noPatches ) { + AAS_CreateCurveBrushes(); + } + + //get the map mins and maxs from the world model + ClearBounds( map_mins, map_maxs ); + for ( i = 0; i < entities[0].numbrushes; i++ ) + { + if ( mapbrushes[i].numsides <= 0 ) { + continue; + } + //if (mapbrushes[i].mins[0] > 4096) + // continue; //no valid points + AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); + AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); + } + if ( writeaasmap ) { + char name[MAX_QPATH]; + strncpy( name, qf->filename, sizeof( name ) ); + StripExtension( name ); + strcat( name, "_aas.map" ); + WriteMapFile( name ); + } +} //end of the function Q3_LoadMapFromBSP +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Q3_ResetMapLoading( void ) { + //reset for map loading from bsp + memset( nodestack, 0, NODESTACKSIZE * sizeof( int ) ); + nodestackptr = NULL; + nodestacksize = 0; + memset( brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof( int ) ); +} //end of the function Q3_ResetMapLoading + diff --git a/src/bspc/map_sin.c b/src/bspc/map_sin.c new file mode 100644 index 0000000..9c04d7f --- /dev/null +++ b/src/bspc/map_sin.c @@ -0,0 +1,1201 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +//----------------------------------------------------------------------------- +// +// $Logfile:: /Wolf4/src/bspc/map_sin.c $ +// $Revision: 1.5 $ +// $Author: lordhavoc $ +// $Date: 2009-02-11 15:21:07 -0600 (Wed, 11 Feb 2009) $ + +#include "qbsp.h" +#include "l_bsp_sin.h" +#include "aas_map.h" //AAS_CreateMapBrushes + + +//==================================================================== + + +/* +=========== +Sin_BrushContents +=========== +*/ + +int Sin_BrushContents( mapbrush_t *b ) { + int contents; + side_t *s; + int i; +#ifdef SIN + float trans = 0; +#else + int trans; +#endif + + s = &b->original_sides[0]; + contents = s->contents; + +#ifdef SIN + trans = sin_texinfo[s->texinfo].translucence; +#else + trans = texinfo[s->texinfo].flags; +#endif + for ( i = 1 ; i < b->numsides ; i++, s++ ) + { + s = &b->original_sides[i]; +#ifdef SIN + trans += sin_texinfo[s->texinfo].translucence; +#else + trans |= texinfo[s->texinfo].flags; +#endif + if ( s->contents != contents ) { +#ifdef SIN + if ( + ( s->contents & CONTENTS_DETAIL && !( contents & CONTENTS_DETAIL ) ) || + ( !( s->contents & CONTENTS_DETAIL ) && contents & CONTENTS_DETAIL ) + ) { + s->contents |= CONTENTS_DETAIL; + contents |= CONTENTS_DETAIL; + continue; + } +#endif + printf( "Entity %i, Brush %i: mixed face contents\n" + , b->entitynum, b->brushnum ); + break; + } + } + + +#ifdef SIN + if ( contents & CONTENTS_FENCE ) { +// contents |= CONTENTS_TRANSLUCENT; + contents |= CONTENTS_DETAIL; + contents |= CONTENTS_DUMMYFENCE; + contents &= ~CONTENTS_SOLID; + contents &= ~CONTENTS_FENCE; + contents |= CONTENTS_WINDOW; + } +#endif + + // if any side is translucent, mark the contents + // and change solid to window +#ifdef SIN + if ( trans > 0 ) +#else + if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) +#endif + { + contents |= CONTENTS_Q2TRANSLUCENT; + if ( contents & CONTENTS_SOLID ) { + contents &= ~CONTENTS_SOLID; + contents |= CONTENTS_WINDOW; + } + } + + return contents; +} //*/ + + +//============================================================================ + + + +/* +================= +ParseBrush +================= +* / +void ParseBrush (entity_t *mapent) +{ + mapbrush_t *b; + int i,j, k; + int mt; + side_t *side, *s2; + int planenum; + brush_texture_t td; +#ifdef SIN + textureref_t newref; +#endif + int planepts[3][3]; + + if (nummapbrushes == MAX_MAP_BRUSHES) + Error ("nummapbrushes == MAX_MAP_BRUSHES"); + + b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = num_entities-1; + b->brushnum = nummapbrushes - mapent->firstbrush; + + do + { + if (!GetToken (true)) + break; + if (!strcmp (token, "}") ) + break; + + if (nummapbrushsides == MAX_MAP_BRUSHSIDES) + Error ("MAX_MAP_BRUSHSIDES"); + side = &brushsides[nummapbrushsides]; + + // read the three point plane definition + for (i=0 ; i<3 ; i++) + { + if (i != 0) + GetToken (true); + if (strcmp (token, "(") ) + Error ("parsing brush"); + + for (j=0 ; j<3 ; j++) + { + GetToken (false); + planepts[i][j] = atoi(token); + } + + GetToken (false); + if (strcmp (token, ")") ) + Error ("parsing brush"); + + } + + + // + // read the texturedef + // + GetToken (false); + strcpy (td.name, token); + + GetToken (false); + td.shift[0] = atoi(token); + GetToken (false); + td.shift[1] = atoi(token); + GetToken (false); +#ifdef SIN + td.rotate = atof(token); +#else + td.rotate = atoi(token); +#endif + GetToken (false); + td.scale[0] = atof(token); + GetToken (false); + td.scale[1] = atof(token); + + // find default flags and values + mt = FindMiptex (td.name); +#ifdef SIN + // clear out the masks on newref + memset(&newref,0,sizeof(newref)); + // copy over the name + strcpy( newref.name, td.name ); + + ParseSurfaceInfo( &newref ); + MergeRefs( &bsp_textureref[mt], &newref, &td.tref ); + side->contents = td.tref.contents; + side->surf = td.tref.flags; +#else + td.flags = textureref[mt].flags; + td.value = textureref[mt].value; + side->contents = textureref[mt].contents; + side->surf = td.flags = textureref[mt].flags; + + if (TokenAvailable()) + { + GetToken (false); + side->contents = atoi(token); + GetToken (false); + side->surf = td.flags = atoi(token); + GetToken (false); + td.value = atoi(token); + } +#endif + + // translucent objects are automatically classified as detail +#ifdef SIN + if ( td.tref.translucence > 0 ) +#else + if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) +#endif + side->contents |= CONTENTS_DETAIL; + if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) + side->contents |= CONTENTS_DETAIL; + if (fulldetail) + side->contents &= ~CONTENTS_DETAIL; + if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) + | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST) ) ) + side->contents |= CONTENTS_SOLID; + + // hints and skips are never detail, and have no content + if (side->surf & (SURF_HINT|SURF_SKIP) ) + { + side->contents = 0; +#ifndef SIN // I think this is a bug of some kind + side->surf &= ~CONTENTS_DETAIL; +#endif + } + + // + // find the plane number + // + planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); + if (planenum == -1) + { + printf ("Entity %i, Brush %i: plane with no normal\n" + , b->entitynum, b->brushnum); + continue; + } + + // + // see if the plane has been used already + // + for (k=0 ; knumsides ; k++) + { + s2 = b->original_sides + k; + if (s2->planenum == planenum) + { + printf ("Entity %i, Brush %i: duplicate plane\n" + , b->entitynum, b->brushnum); + break; + } + if ( s2->planenum == (planenum^1) ) + { + printf ("Entity %i, Brush %i: mirrored plane\n" + , b->entitynum, b->brushnum); + break; + } + } + if (k != b->numsides) + continue; // duplicated + + // + // keep this side + // + + side = b->original_sides + b->numsides; + side->planenum = planenum; +#ifdef SIN + side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], + &td, vec3_origin, &newref); + // + // save off lightinfo + // + side->lightinfo = LightinfoForBrushTexture ( &td ); +#else + side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], + &td, vec3_origin); + +#endif + + // save the td off in case there is an origin brush and we + // have to recalculate the texinfo + side_brushtextures[nummapbrushsides] = td; +#ifdef SIN + // save off the merged tref for animating textures + side_newrefs[nummapbrushsides] = newref; +#endif + + nummapbrushsides++; + b->numsides++; + } while (1); + + // get the content for the entire brush + b->contents = Sin_BrushContents (b); + + // allow detail brushes to be removed + if (nodetail && (b->contents & CONTENTS_DETAIL) ) + { + b->numsides = 0; + return; + } + + // allow water brushes to be removed + if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) + { + b->numsides = 0; + return; + } + + // create windings for sides and bounds for brush + MakeBrushWindings (b); + + // brushes that will not be visible at all will never be + // used as bsp splitters + if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) + { + c_clipbrushes++; + for (i=0 ; inumsides ; i++) + b->original_sides[i].texinfo = TEXINFO_NODE; + } + + // + // origin brushes are removed, but they set + // the rotation origin for the rest of the brushes + // in the entity. After the entire entity is parsed, + // the planenums and texinfos will be adjusted for + // the origin brush + // + if (b->contents & CONTENTS_ORIGIN) + { + char string[32]; + vec3_t origin; + + if (num_entities == 1) + { + Error ("Entity %i, Brush %i: origin brushes not allowed in world" + , b->entitynum, b->brushnum); + return; + } + + VectorAdd (b->mins, b->maxs, origin); + VectorScale (origin, 0.5, origin); + + sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (&entities[b->entitynum], "origin", string); + + VectorCopy (origin, entities[b->entitynum].origin); + + // don't keep this brush + b->numsides = 0; + + return; + } + + AddBrushBevels (b); + + nummapbrushes++; + mapent->numbrushes++; +} //*/ + +/* +================ +MoveBrushesToWorld + +Takes all of the brushes from the current entity and +adds them to the world's brush list. + +Used by func_group and func_areaportal +================ +* / +void MoveBrushesToWorld (entity_t *mapent) +{ + int newbrushes; + int worldbrushes; + mapbrush_t *temp; + int i; + + // this is pretty gross, because the brushes are expected to be + // in linear order for each entity + + newbrushes = mapent->numbrushes; + worldbrushes = entities[0].numbrushes; + + temp = malloc(newbrushes*sizeof(mapbrush_t)); + memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t)); + +#if 0 // let them keep their original brush numbers + for (i=0 ; inumbrushes = 0; +} //*/ + +/* +================ +ParseMapEntity +================ +* / +qboolean Sin_ParseMapEntity (void) +{ + entity_t *mapent; + epair_t *e; + side_t *s; + int i, j; + int startbrush, startsides; + vec_t newdist; + mapbrush_t *b; + + if (!GetToken (true)) + return false; + + if (strcmp (token, "{") ) + Error ("ParseEntity: { not found"); + + if (num_entities == MAX_MAP_ENTITIES) + Error ("num_entities == MAX_MAP_ENTITIES"); + + startbrush = nummapbrushes; + startsides = nummapbrushsides; + + mapent = &entities[num_entities]; + num_entities++; + memset (mapent, 0, sizeof(*mapent)); + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; +// mapent->portalareas[0] = -1; +// mapent->portalareas[1] = -1; + + do + { + if (!GetToken (true)) + Error ("ParseEntity: EOF without closing brace"); + if (!strcmp (token, "}") ) + break; + if (!strcmp (token, "{") ) + ParseBrush (mapent); + else + { + e = ParseEpair (); +#ifdef SIN + //HACK HACK HACK + // MED Gotta do this here + if ( !stricmp(e->key, "surfacefile") ) + { + if (!surfacefile[0]) + { + strcpy( surfacefile, e->value ); + } + printf ("--- ParseSurfaceFile ---\n"); + printf ("Surface script: %s\n", surfacefile); + if (!ParseSurfaceFile(surfacefile)) + { + Error ("Script file not found: %s\n", surfacefile); + } + } +#endif + e->next = mapent->epairs; + mapent->epairs = e; + } + } while (1); + +#ifdef SIN + if (!(strlen(ValueForKey(mapent, "origin"))) && ((num_entities-1) != 0)) + { + mapbrush_t *brush; + vec3_t origin; + char string[32]; + vec3_t mins, maxs; + int start, end; + // Calculate bounds + + start = mapent->firstbrush; + end = start + mapent->numbrushes; + ClearBounds (mins, maxs); + + for (j=start ; jnumsides) + continue; // not a real brush (origin brush) - shouldn't happen + AddPointToBounds (brush->mins, mins, maxs); + AddPointToBounds (brush->maxs, mins, maxs); + } + + // Set the origin to be the centroid of the entity. + VectorAdd ( mins, maxs, origin); + VectorScale( origin, 0.5f, origin ); + + sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue ( mapent, "origin", string); +// qprintf("Setting origin to %s\n",string); + } +#endif + + GetVectorForKey (mapent, "origin", mapent->origin); + +#ifdef SIN + if ( + (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) || + (!strcmp ("func_group", ValueForKey (mapent, "classname"))) || + (!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails) + ) + { + VectorClear( mapent->origin ); + } +#endif + + // + // if there was an origin brush, offset all of the planes and texinfo + // + if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) + { + for (i=0 ; inumbrushes ; i++) + { + b = &mapbrushes[mapent->firstbrush + i]; + for (j=0 ; jnumsides ; j++) + { + s = &b->original_sides[j]; + newdist = mapplanes[s->planenum].dist - + DotProduct (mapplanes[s->planenum].normal, mapent->origin); + s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist, 0, NULL); +#ifdef SIN + s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], + &side_brushtextures[s-brushsides], mapent->origin, &side_newrefs[s-brushsides]); + // + // save off lightinfo + // + s->lightinfo = LightinfoForBrushTexture ( &side_brushtextures[s-brushsides] ); +#else + s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], + &side_brushtextures[s-brushsides], mapent->origin); +#endif + } + MakeBrushWindings (b); + } + } + + // group entities are just for editor convenience + // toss all brushes into the world entity + if (!strcmp ("func_group", ValueForKey (mapent, "classname"))) + { + MoveBrushesToWorld (mapent); + mapent->numbrushes = 0; + mapent->wasdetail = true; + FreeValueKeys( mapent ); + return true; + } +#ifdef SIN + // detail entities are just for editor convenience + // toss all brushes into the world entity as detail brushes + if (!strcmp ("detail", ValueForKey (mapent, "classname")) && !entitydetails) + { + for (i=0 ; inumbrushes ; i++) + { + int j; + side_t * s; + b = &mapbrushes[mapent->firstbrush + i]; + if (nodetail) + { + b->numsides = 0; + continue; + } + if (!fulldetail) + { + // set the contents for the entire brush + b->contents |= CONTENTS_DETAIL; + // set the contents in the sides as well + for (j=0, s=b->original_sides ; jnumsides ; j++,s++) + { + s->contents |= CONTENTS_DETAIL; + } + } + else + { + // set the contents for the entire brush + b->contents |= CONTENTS_SOLID; + // set the contents in the sides as well + for (j=0, s=b->original_sides ; jnumsides ; j++,s++) + { + s->contents |= CONTENTS_SOLID; + } + } + } + MoveBrushesToWorld (mapent); + mapent->wasdetail = true; + FreeValueKeys( mapent ); + // kill off the entity + // num_entities--; + return true; + } +#endif + + // areaportal entities move their brushes, but don't eliminate + // the entity + if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) + { + char str[128]; + + if (mapent->numbrushes != 1) + Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); + + b = &mapbrushes[nummapbrushes-1]; + b->contents = CONTENTS_AREAPORTAL; + c_areaportals++; + mapent->areaportalnum = c_areaportals; + // set the portal number as "style" + sprintf (str, "%i", c_areaportals); + SetKeyValue (mapent, "style", str); + MoveBrushesToWorld (mapent); + return true; + } + + return true; +} //end of the function Sin_ParseMapEntity */ + +//=================================================================== + +/* +================ +LoadMapFile +================ +* / +void Sin_LoadMapFile (char *filename) +{ + int i; +#ifdef SIN + int num_detailsides=0; + int num_detailbrushes=0; + int num_worldsides=0; + int num_worldbrushes=0; + int j,k; +#endif + + qprintf ("--- LoadMapFile ---\n"); + + LoadScriptFile (filename); + + nummapbrushsides = 0; + num_entities = 0; + + while (ParseMapEntity ()) + { + } + + ClearBounds (map_mins, map_maxs); + for (i=0 ; i 4096) + continue; // no valid points + AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs); + AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs); + } +#ifdef SIN + for (j=0; jnumsides && b->contents & CONTENTS_DETAIL) + num_detailbrushes++; + else if (b->numsides) + num_worldbrushes++; + for (k=0, s=b->original_sides ; knumsides ; k++,s++) + { + if (s->contents & CONTENTS_DETAIL) + num_detailsides++; + else + num_worldsides++; + } + } + } +#endif + + qprintf ("%5i brushes\n", nummapbrushes); + qprintf ("%5i clipbrushes\n", c_clipbrushes); + qprintf ("%5i total sides\n", nummapbrushsides); + qprintf ("%5i boxbevels\n", c_boxbevels); + qprintf ("%5i edgebevels\n", c_edgebevels); + qprintf ("%5i entities\n", num_entities); + qprintf ("%5i planes\n", nummapplanes); + qprintf ("%5i areaportals\n", c_areaportals); + qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2], + map_maxs[0],map_maxs[1],map_maxs[2]); +#ifdef SIN + qprintf ("%5i detailbrushes\n", num_detailbrushes); + qprintf ("%5i worldbrushes\n", num_worldbrushes); + qprintf ("%5i detailsides\n", num_detailsides); + qprintf ("%5i worldsides\n", num_worldsides); +#endif + +} //end of the function Sin_LoadMap */ + + +#ifdef ME //Begin MAP loading from BSP file +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_CreateMapTexinfo( void ) { + int i; + vec_t defaultvec[4] = {1, 0, 0, 0}; + + memcpy( map_texinfo[0].vecs[0], defaultvec, sizeof( defaultvec ) ); + memcpy( map_texinfo[0].vecs[1], defaultvec, sizeof( defaultvec ) ); + map_texinfo[0].flags = 0; + map_texinfo[0].value = 0; + strcpy( map_texinfo[0].texture, "generic/misc/red" ); //no texture + map_texinfo[0].nexttexinfo = -1; + for ( i = 1; i < sin_numtexinfo; i++ ) + { + memcpy( map_texinfo[i].vecs, sin_texinfo[i].vecs, sizeof( float ) * 2 * 4 ); + map_texinfo[i].flags = sin_texinfo[i].flags; + map_texinfo[i].value = 0; + strcpy( map_texinfo[i].texture, sin_texinfo[i].texture ); + map_texinfo[i].nexttexinfo = -1; + } //end for +} //end of the function Sin_CreateMapTexinfo +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_SetLeafBrushesModelNumbers( int leafnum, int modelnum ) { + int i, brushnum; + sin_dleaf_t *leaf; + + leaf = &sin_dleafs[leafnum]; + for ( i = 0; i < leaf->numleafbrushes; i++ ) + { + brushnum = sin_dleafbrushes[leaf->firstleafbrush + i]; + brushmodelnumbers[brushnum] = modelnum; + dbrushleafnums[brushnum] = leafnum; + } //end for +} //end of the function Sin_SetLeafBrushesModelNumbers +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_InitNodeStack( void ) { + nodestackptr = nodestack; + nodestacksize = 0; +} //end of the function Sin_InitNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_PushNodeStack( int num ) { + *nodestackptr = num; + nodestackptr++; + nodestacksize++; + // + if ( nodestackptr >= &nodestack[NODESTACKSIZE] ) { + Error( "Sin_PushNodeStack: stack overflow\n" ); + } //end if +} //end of the function Sin_PushNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Sin_PopNodeStack( void ) { + //if the stack is empty + if ( nodestackptr <= nodestack ) { + return -1; + } + //decrease stack pointer + nodestackptr--; + nodestacksize--; + //return the top value from the stack + return *nodestackptr; +} //end of the function Sin_PopNodeStack +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_SetBrushModelNumbers( entity_t *mapent ) { + int n, pn; + int leafnum; + + // + Sin_InitNodeStack(); + //head node (root) of the bsp tree + n = sin_dmodels[mapent->modelnum].headnode; + pn = 0; + + do + { + //if we are in a leaf (negative node number) + if ( n < 0 ) { + //number of the leaf + leafnum = ( -n ) - 1; + //set the brush numbers + Sin_SetLeafBrushesModelNumbers( leafnum, mapent->modelnum ); + //walk back into the tree to find a second child to continue with + for ( pn = Sin_PopNodeStack(); pn >= 0; n = pn, pn = Sin_PopNodeStack() ) + { + //if we took the first child at the parent node + if ( sin_dnodes[pn].children[0] == n ) { + break; + } + } //end for + //if the stack wasn't empty (if not processed whole tree) + if ( pn >= 0 ) { + //push the parent node again + Sin_PushNodeStack( pn ); + //we proceed with the second child of the parent node + n = sin_dnodes[pn].children[1]; + } //end if + } //end if + else + { + //push the current node onto the stack + Sin_PushNodeStack( n ); + //walk forward into the tree to the first child + n = sin_dnodes[n].children[0]; + } //end else + } while ( pn >= 0 ); +} //end of the function Sin_SetBrushModelNumbers +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_BSPBrushToMapBrush( sin_dbrush_t *bspbrush, entity_t *mapent ) { + mapbrush_t *b; + int i, k, n; + side_t *side, *s2; + int planenum; + sin_dbrushside_t *bspbrushside; + sin_dplane_t *bspplane; + + if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { + Error( "nummapbrushes >= MAX_MAPFILE_BRUSHES" ); + } + + b = &mapbrushes[nummapbrushes]; + b->original_sides = &brushsides[nummapbrushsides]; + b->entitynum = mapent - entities; + b->brushnum = nummapbrushes - mapent->firstbrush; + b->leafnum = dbrushleafnums[bspbrush - sin_dbrushes]; + + for ( n = 0; n < bspbrush->numsides; n++ ) + { + //pointer to the bsp brush side + bspbrushside = &sin_dbrushsides[bspbrush->firstside + n]; + + if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { + Error( "MAX_MAPFILE_BRUSHSIDES" ); + } //end if + //pointer to the map brush side + side = &brushsides[nummapbrushsides]; + //if the BSP brush side is textured + if ( sin_dbrushsidetextured[bspbrush->firstside + n] ) { + side->flags |= SFL_TEXTURED; + } else { side->flags &= ~SFL_TEXTURED;} + //ME: can get side contents and surf directly from BSP file + side->contents = bspbrush->contents; + //if the texinfo is TEXINFO_NODE + if ( bspbrushside->texinfo < 0 ) { + side->surf = 0; + } else { side->surf = sin_texinfo[bspbrushside->texinfo].flags;} + + // translucent objects are automatically classified as detail +// if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) +// side->contents |= CONTENTS_DETAIL; + if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + side->contents |= CONTENTS_DETAIL; + } + if ( fulldetail ) { + side->contents &= ~CONTENTS_DETAIL; + } + if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 ) + | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST ) ) ) { + side->contents |= CONTENTS_SOLID; + } + + // hints and skips are never detail, and have no content + if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) { + side->contents = 0; + side->surf &= ~CONTENTS_DETAIL; + } + + //ME: get a plane for this side + bspplane = &sin_dplanes[bspbrushside->planenum]; + planenum = FindFloatPlane( bspplane->normal, bspplane->dist, 0, NULL ); + // + // see if the plane has been used already + // + //ME: this really shouldn't happen!!! + //ME: otherwise the bsp file is corrupted?? + //ME: still it seems to happen, maybe Johny Boy's + //ME: brush bevel adding is crappy ? + for ( k = 0; k < b->numsides; k++ ) + { + s2 = b->original_sides + k; + if ( s2->planenum == planenum ) { + Log_Print( "Entity %i, Brush %i: duplicate plane\n" + , b->entitynum, b->brushnum ); + break; + } + if ( s2->planenum == ( planenum ^ 1 ) ) { + Log_Print( "Entity %i, Brush %i: mirrored plane\n" + , b->entitynum, b->brushnum ); + break; + } + } + if ( k != b->numsides ) { + continue; // duplicated + + } + // + // keep this side + // + //ME: reset pointer to side, why? hell I dunno (pointer is set above already) + side = b->original_sides + b->numsides; + //ME: store the plane number + side->planenum = planenum; + //ME: texinfo is already stored when bsp is loaded + //NOTE: check for TEXINFO_NODE, otherwise crash in Sin_BrushContents + if ( bspbrushside->texinfo < 0 ) { + side->texinfo = 0; + } else { side->texinfo = bspbrushside->texinfo;} + + // save the td off in case there is an origin brush and we + // have to recalculate the texinfo + // ME: don't need to recalculate because it's already done + // (for non-world entities) in the BSP file +// side_brushtextures[nummapbrushsides] = td; + + nummapbrushsides++; + b->numsides++; + } //end for + + // get the content for the entire brush + b->contents = bspbrush->contents; + Sin_BrushContents( b ); + + if ( BrushExists( b ) ) { + c_squattbrushes++; + b->numsides = 0; + return; + } //end if + + //if we're creating AAS + if ( create_aas ) { + //create the AAS brushes from this brush, don't add brush bevels + AAS_CreateMapBrushes( b, mapent, false ); + return; + } //end if + + // allow detail brushes to be removed + if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) { + b->numsides = 0; + return; + } //end if + + // allow water brushes to be removed + if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { + b->numsides = 0; + return; + } //end if + + // create windings for sides and bounds for brush + MakeBrushWindings( b ); + + //mark brushes without winding or with a tiny window as bevels + MarkBrushBevels( b ); + + // brushes that will not be visible at all will never be + // used as bsp splitters + if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) { + c_clipbrushes++; + for ( i = 0; i < b->numsides; i++ ) + b->original_sides[i].texinfo = TEXINFO_NODE; + } //end for + + // + // origin brushes are removed, but they set + // the rotation origin for the rest of the brushes + // in the entity. After the entire entity is parsed, + // the planenums and texinfos will be adjusted for + // the origin brush + // + //ME: not needed because the entities in the BSP file already + // have an origin set +// if (b->contents & CONTENTS_ORIGIN) +// { +// char string[32]; +// vec3_t origin; +// +// if (num_entities == 1) +// { +// Error ("Entity %i, Brush %i: origin brushes not allowed in world" +// , b->entitynum, b->brushnum); +// return; +// } +// +// VectorAdd (b->mins, b->maxs, origin); +// VectorScale (origin, 0.5, origin); +// +// sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); +// SetKeyValue (&entities[b->entitynum], "origin", string); +// +// VectorCopy (origin, entities[b->entitynum].origin); +// +// // don't keep this brush +// b->numsides = 0; +// +// return; +// } + + //ME: the bsp brushes already have bevels, so we won't try to + // add them again (especially since Johny Boy's bevel adding might + // be crappy) +// AddBrushBevels(b); + + nummapbrushes++; + mapent->numbrushes++; +} //end of the function Sin_BSPBrushToMapBrush +//=========================================================================== +//=========================================================================== +void Sin_ParseBSPBrushes( entity_t *mapent ) { + int i, testnum = 0; + + //give all the brushes that belong to this entity the number of the + //BSP model used by this entity + Sin_SetBrushModelNumbers( mapent ); + //now parse all the brushes with the correct mapent->modelnum + for ( i = 0; i < sin_numbrushes; i++ ) + { + if ( brushmodelnumbers[i] == mapent->modelnum ) { + testnum++; + Sin_BSPBrushToMapBrush( &sin_dbrushes[i], mapent ); + } //end if + } //end for +} //end of the function Sin_ParseBSPBrushes +//=========================================================================== +//=========================================================================== +qboolean Sin_ParseBSPEntity( int entnum ) { + entity_t *mapent; + char *model; + int startbrush, startsides; + + startbrush = nummapbrushes; + startsides = nummapbrushsides; + + mapent = &entities[entnum]; //num_entities]; + mapent->firstbrush = nummapbrushes; + mapent->numbrushes = 0; + mapent->modelnum = -1; //-1 = no model + + model = ValueForKey( mapent, "model" ); + if ( model && *model == '*' ) { + mapent->modelnum = atoi( &model[1] ); + //Log_Print("model = %s\n", model); + //Log_Print("mapent->modelnum = %d\n", mapent->modelnum); + } //end if + + GetVectorForKey( mapent, "origin", mapent->origin ); + + //if this is the world entity it has model number zero + //the world entity has no model key + if ( !strcmp( "worldspawn", ValueForKey( mapent, "classname" ) ) ) { + mapent->modelnum = 0; + } //end if + //if the map entity has a BSP model (a modelnum of -1 is used for + //entities that aren't using a BSP model) + if ( mapent->modelnum >= 0 ) { + //parse the bsp brushes + Sin_ParseBSPBrushes( mapent ); + } //end if + // + //the origin of the entity is already taken into account + // + //func_group entities can't be in the bsp file + // + //check out the func_areaportal entities + if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { + c_areaportals++; + mapent->areaportalnum = c_areaportals; + return true; + } //end if + return true; +} //end of the function Sin_ParseBSPEntity +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Sin_LoadMapFromBSP( char *filename, int offset, int length ) { + int i; + + Log_Print( "-- Sin_LoadMapFromBSP --\n" ); + //loaded map type + loadedmaptype = MAPTYPE_SIN; + + Log_Print( "Loading map from %s...\n", filename ); + //load the bsp file + Sin_LoadBSPFile( filename, offset, length ); + + //create an index from bsp planes to map planes + //DPlanes2MapPlanes(); + //clear brush model numbers + for ( i = 0; i < MAX_MAPFILE_BRUSHES; i++ ) + brushmodelnumbers[i] = -1; + + nummapbrushsides = 0; + num_entities = 0; + + Sin_ParseEntities(); + // + for ( i = 0; i < num_entities; i++ ) + { + Sin_ParseBSPEntity( i ); + } //end for + + //get the map mins and maxs from the world model + ClearBounds( map_mins, map_maxs ); + for ( i = 0; i < entities[0].numbrushes; i++ ) + { + if ( mapbrushes[i].mins[0] > 4096 ) { + continue; //no valid points + } + AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); + AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); + } //end for + // + Sin_CreateMapTexinfo(); +} //end of the function Sin_LoadMapFromBSP + +void Sin_ResetMapLoading( void ) { + //reset for map loading from bsp + memset( nodestack, 0, NODESTACKSIZE * sizeof( int ) ); + nodestackptr = NULL; + nodestacksize = 0; + memset( brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof( int ) ); +} //end of the function Sin_ResetMapLoading + +//End MAP loading from BSP file + +#endif //ME diff --git a/src/bspc/nodraw.c b/src/bspc/nodraw.c new file mode 100644 index 0000000..e3a53c1 --- /dev/null +++ b/src/bspc/nodraw.c @@ -0,0 +1,50 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "qbsp.h" + +vec3_t draw_mins, draw_maxs; +qboolean drawflag; + +void Draw_ClearWindow( void ) { +} + +//============================================================ + +#define GLSERV_PORT 25001 + + +void GLS_BeginScene( void ) { +} + +void GLS_Winding( winding_t *w, int code ) { +} + +void GLS_EndScene( void ) { +} diff --git a/src/bspc/portals.c b/src/bspc/portals.c new file mode 100644 index 0000000..374f0bd --- /dev/null +++ b/src/bspc/portals.c @@ -0,0 +1,1308 @@ +/* +=========================================================================== + +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: Portals +// Function: partalizing a bsp tree, flooding through portals +// Programmer: id Software & Mr Elusive (MrElusive@worldentity.com) +// Last update: 1999-08-10 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_mem.h" + +int c_active_portals; +int c_peak_portals; +int c_boundary; +int c_boundary_sides; +int c_portalmemory; + +//portal_t *portallist = NULL; +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +portal_t *AllocPortal( void ) { + portal_t *p; + + p = GetMemory( sizeof( portal_t ) ); + memset( p, 0, sizeof( portal_t ) ); + + if ( numthreads == 1 ) { + c_active_portals++; + if ( c_active_portals > c_peak_portals ) { + c_peak_portals = c_active_portals; + } //end if + c_portalmemory += MemorySize( p ); + } //end if + +// p->nextportal = portallist; +// portallist = p; + + return p; +} //end of the function AllocPortal +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreePortal( portal_t *p ) { + if ( p->winding ) { + FreeWinding( p->winding ); + } + if ( numthreads == 1 ) { + c_active_portals--; + c_portalmemory -= MemorySize( p ); + } //end if + FreeMemory( p ); +} //end of the function FreePortal +//=========================================================================== +// Returns the single content bit of the +// strongest visible content present +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int VisibleContents( int contents ) { + int i; + + for ( i = 1 ; i <= LAST_VISIBLE_CONTENTS ; i <<= 1 ) + if ( contents & i ) { + return i; + } + + return 0; +} //end of the function VisibleContents +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int ClusterContents( node_t *node ) { + int c1, c2, c; + + if ( node->planenum == PLANENUM_LEAF ) { + return node->contents; + } + + c1 = ClusterContents( node->children[0] ); + c2 = ClusterContents( node->children[1] ); + c = c1 | c2; + + // a cluster may include some solid detail areas, but + // still be seen into + if ( !( c1 & CONTENTS_SOLID ) || !( c2 & CONTENTS_SOLID ) ) { + c &= ~CONTENTS_SOLID; + } + return c; +} //end of the function ClusterContents + +//=========================================================================== +// Returns true if the portal is empty or translucent, allowing +// the PVS calculation to see through it. +// The nodes on either side of the portal may actually be clusters, +// not leaves, so all contents should be ored together +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean Portal_VisFlood( portal_t *p ) { + int c1, c2; + + if ( !p->onnode ) { + return false; // to global outsideleaf + + } + c1 = ClusterContents( p->nodes[0] ); + c2 = ClusterContents( p->nodes[1] ); + + if ( !VisibleContents( c1 ^ c2 ) ) { + return true; + } + + if ( c1 & ( CONTENTS_Q2TRANSLUCENT | CONTENTS_DETAIL ) ) { + c1 = 0; + } + if ( c2 & ( CONTENTS_Q2TRANSLUCENT | CONTENTS_DETAIL ) ) { + c2 = 0; + } + + if ( ( c1 | c2 ) & CONTENTS_SOLID ) { + return false; // can't see through solid + + } + if ( !( c1 ^ c2 ) ) { + return true; // identical on both sides + + } + if ( !VisibleContents( c1 ^ c2 ) ) { + return true; + } + return false; +} //end of the function Portal_VisFlood +//=========================================================================== +// The entity flood determines which areas are +// "outside" on the map, which are then filled in. +// Flowing from side s to side !s +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean Portal_EntityFlood( portal_t *p, int s ) { + if ( p->nodes[0]->planenum != PLANENUM_LEAF + || p->nodes[1]->planenum != PLANENUM_LEAF ) { + Error( "Portal_EntityFlood: not a leaf" ); + } + + // can never cross to a solid + if ( ( p->nodes[0]->contents & CONTENTS_SOLID ) + || ( p->nodes[1]->contents & CONTENTS_SOLID ) ) { + return false; + } + + // can flood through everything else + return true; +} + + +//============================================================================= + +int c_tinyportals; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void AddPortalToNodes( portal_t *p, node_t *front, node_t *back ) { + if ( p->nodes[0] || p->nodes[1] ) { + Error( "AddPortalToNode: allready included" ); + } + + p->nodes[0] = front; + p->next[0] = front->portals; + front->portals = p; + + p->nodes[1] = back; + p->next[1] = back->portals; + back->portals = p; +} //end of the function AddPortalToNodes +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void RemovePortalFromNode( portal_t *portal, node_t *l ) { + portal_t **pp, *t; + + int s, i, n; + portal_t *p; + portal_t *portals[4096]; + +// remove reference to the current portal + pp = &l->portals; + while ( 1 ) + { + t = *pp; + if ( !t ) { + Error( "RemovePortalFromNode: portal not in leaf" ); + } + + if ( t == portal ) { + break; + } + + if ( t->nodes[0] == l ) { + pp = &t->next[0]; + } else if ( t->nodes[1] == l ) { + pp = &t->next[1]; + } else { + Error( "RemovePortalFromNode: portal not bounding leaf" ); + } + } + + if ( portal->nodes[0] == l ) { + *pp = portal->next[0]; + portal->nodes[0] = NULL; + } //end if + else if ( portal->nodes[1] == l ) { + *pp = portal->next[1]; + portal->nodes[1] = NULL; + } //end else if + else + { + Error( "RemovePortalFromNode: mislinked portal" ); + } //end else +//#ifdef ME + n = 0; + for ( p = l->portals; p; p = p->next[s] ) + { + for ( i = 0; i < n; i++ ) + { + if ( p == portals[i] ) { + Error( "RemovePortalFromNode: circular linked\n" ); + } + } //end for + if ( p->nodes[0] != l && p->nodes[1] != l ) { + Error( "RemovePortalFromNodes: portal does not belong to node\n" ); + } //end if + portals[n] = p; + s = ( p->nodes[1] == l ); +// if (++n >= 4096) Error("RemovePortalFromNode: more than 4096 portals\n"); + } //end for +//#endif +} //end of the function RemovePortalFromNode +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintPortal( portal_t *p ) { + int i; + winding_t *w; + + w = p->winding; + for ( i = 0 ; i < w->numpoints ; i++ ) + printf( "(%5.0f,%5.0f,%5.0f)\n",w->p[i][0] + , w->p[i][1], w->p[i][2] ); +} //end of the function PrintPortal +//=========================================================================== +// The created portals will face the global outside_node +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define SIDESPACE 8 + +void MakeHeadnodePortals( tree_t *tree ) { + vec3_t bounds[2]; + int i, j, n; + portal_t *p, *portals[6]; + plane_t bplanes[6], *pl; + node_t *node; + + node = tree->headnode; + +// pad with some space so there will never be null volume leaves + for ( i = 0 ; i < 3 ; i++ ) + { + bounds[0][i] = tree->mins[i] - SIDESPACE; + bounds[1][i] = tree->maxs[i] + SIDESPACE; + } + + tree->outside_node.planenum = PLANENUM_LEAF; + tree->outside_node.brushlist = NULL; + tree->outside_node.portals = NULL; + tree->outside_node.contents = 0; + + for ( i = 0 ; i < 3 ; i++ ) + for ( j = 0 ; j < 2 ; j++ ) + { + n = j * 3 + i; + + p = AllocPortal(); + portals[n] = p; + + pl = &bplanes[n]; + memset( pl, 0, sizeof( *pl ) ); + if ( j ) { + pl->normal[i] = -1; + pl->dist = -bounds[j][i]; + } else + { + pl->normal[i] = 1; + pl->dist = bounds[j][i]; + } + p->plane = *pl; + p->winding = BaseWindingForPlane( pl->normal, pl->dist ); + AddPortalToNodes( p, node, &tree->outside_node ); + } + +// clip the basewindings by all the other planes + for ( i = 0 ; i < 6 ; i++ ) + { + for ( j = 0 ; j < 6 ; j++ ) + { + if ( j == i ) { + continue; + } + ChopWindingInPlace( &portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON ); + } //end for + } //end for +} //end of the function MakeHeadNodePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#define BASE_WINDING_EPSILON 0.001 +#define SPLIT_WINDING_EPSILON 0.001 + +winding_t *BaseWindingForNode( node_t *node ) { + winding_t *w; + node_t *n; + plane_t *plane; + vec3_t normal; + vec_t dist; + + w = BaseWindingForPlane( mapplanes[node->planenum].normal + , mapplanes[node->planenum].dist ); + + // clip by all the parents + for ( n = node->parent ; n && w ; ) + { + plane = &mapplanes[n->planenum]; + + if ( n->children[0] == node ) { // take front + ChopWindingInPlace( &w, plane->normal, plane->dist, BASE_WINDING_EPSILON ); + } else + { // take back + VectorSubtract( vec3_origin, plane->normal, normal ); + dist = -plane->dist; + ChopWindingInPlace( &w, normal, dist, BASE_WINDING_EPSILON ); + } + node = n; + n = n->parent; + } + + return w; +} //end of the function BaseWindingForNode +//=========================================================================== +// create the new portal by taking the full plane winding for the cutting +// plane and clipping it by all of parents of this node +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean WindingIsTiny( winding_t *w ); + +void MakeNodePortal( node_t *node ) { + portal_t *new_portal, *p; + winding_t *w; + vec3_t normal; + float dist; + int side; + + w = BaseWindingForNode( node ); + + // clip the portal by all the other portals in the node + for ( p = node->portals; p && w; p = p->next[side] ) + { + if ( p->nodes[0] == node ) { + side = 0; + VectorCopy( p->plane.normal, normal ); + dist = p->plane.dist; + } //end if + else if ( p->nodes[1] == node ) { + side = 1; + VectorSubtract( vec3_origin, p->plane.normal, normal ); + dist = -p->plane.dist; + } //end else if + else + { + Error( "MakeNodePortal: mislinked portal" ); + } //end else + ChopWindingInPlace( &w, normal, dist, 0.1 ); + } //end for + + if ( !w ) { + return; + } //end if + + if ( WindingIsTiny( w ) ) { + c_tinyportals++; + FreeWinding( w ); + return; + } //end if + +#ifdef DEBUG +/* //NOTE: don't use this winding ok check + // all the invalid windings only have a degenerate edge + if (WindingError(w)) + { + Log_Print("MakeNodePortal: %s\n", WindingErrorString()); + FreeWinding(w); + return; + } //end if*/ +#endif //DEBUG + + + new_portal = AllocPortal(); + new_portal->plane = mapplanes[node->planenum]; + +#ifdef ME + new_portal->planenum = node->planenum; +#endif //ME + + new_portal->onnode = node; + new_portal->winding = w; + AddPortalToNodes( new_portal, node->children[0], node->children[1] ); +} //end of the function MakeNodePortal +//=========================================================================== +// Move or split the portals that bound node so that the node's +// children have portals instead of node. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SplitNodePortals( node_t *node ) { + portal_t *p, *next_portal, *new_portal; + node_t *f, *b, *other_node; + int side; + plane_t *plane; + winding_t *frontwinding, *backwinding; + + plane = &mapplanes[node->planenum]; + f = node->children[0]; + b = node->children[1]; + + for ( p = node->portals ; p ; p = next_portal ) + { + if ( p->nodes[0] == node ) { + side = 0; + } else if ( p->nodes[1] == node ) { + side = 1; + } else { Error( "SplitNodePortals: mislinked portal" );} + next_portal = p->next[side]; + + other_node = p->nodes[!side]; + RemovePortalFromNode( p, p->nodes[0] ); + RemovePortalFromNode( p, p->nodes[1] ); + +// +// cut the portal into two portals, one on each side of the cut plane +// + ClipWindingEpsilon( p->winding, plane->normal, plane->dist, + SPLIT_WINDING_EPSILON, &frontwinding, &backwinding ); + + if ( frontwinding && WindingIsTiny( frontwinding ) ) { + FreeWinding( frontwinding ); + frontwinding = NULL; + c_tinyportals++; + } //end if + + if ( backwinding && WindingIsTiny( backwinding ) ) { + FreeWinding( backwinding ); + backwinding = NULL; + c_tinyportals++; + } //end if + +#ifdef DEBUG +/* //NOTE: don't use this winding ok check + // all the invalid windings only have a degenerate edge + if (frontwinding && WindingError(frontwinding)) + { + Log_Print("SplitNodePortals: front %s\n", WindingErrorString()); + FreeWinding(frontwinding); + frontwinding = NULL; + } //end if + if (backwinding && WindingError(backwinding)) + { + Log_Print("SplitNodePortals: back %s\n", WindingErrorString()); + FreeWinding(backwinding); + backwinding = NULL; + } //end if*/ +#endif //DEBUG + + if ( !frontwinding && !backwinding ) { // tiny windings on both sides + continue; + } + + if ( !frontwinding ) { + FreeWinding( backwinding ); + if ( side == 0 ) { + AddPortalToNodes( p, b, other_node ); + } else { AddPortalToNodes( p, other_node, b );} + continue; + } + if ( !backwinding ) { + FreeWinding( frontwinding ); + if ( side == 0 ) { + AddPortalToNodes( p, f, other_node ); + } else { AddPortalToNodes( p, other_node, f );} + continue; + } + + // the winding is split + new_portal = AllocPortal(); + *new_portal = *p; + new_portal->winding = backwinding; + FreeWinding( p->winding ); + p->winding = frontwinding; + + if ( side == 0 ) { + AddPortalToNodes( p, f, other_node ); + AddPortalToNodes( new_portal, b, other_node ); + } //end if + else + { + AddPortalToNodes( p, other_node, f ); + AddPortalToNodes( new_portal, other_node, b ); + } //end else + } + + node->portals = NULL; +} //end of the function SplitNodePortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void CalcNodeBounds( node_t *node ) { + portal_t *p; + int s; + int i; + + // calc mins/maxs for both leaves and nodes + ClearBounds( node->mins, node->maxs ); + for ( p = node->portals ; p ; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); + for ( i = 0 ; i < p->winding->numpoints ; i++ ) + AddPointToBounds( p->winding->p[i], node->mins, node->maxs ); + } +} //end of the function CalcNodeBounds +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int c_numportalizednodes; + +void MakeTreePortals_r( node_t *node ) { + int i; + +#ifdef ME + qprintf( "\r%6d", ++c_numportalizednodes ); + if ( cancelconversion ) { + return; + } +#endif //ME + + CalcNodeBounds( node ); + if ( node->mins[0] >= node->maxs[0] ) { + Log_Print( "WARNING: node without a volume\n" ); + } + + for ( i = 0 ; i < 3 ; i++ ) + { + if ( node->mins[i] < -MAX_MAP_BOUNDS || node->maxs[i] > MAX_MAP_BOUNDS ) { + Log_Print( "WARNING: node with unbounded volume\n" ); + break; + } + } + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + MakeNodePortal( node ); + SplitNodePortals( node ); + + MakeTreePortals_r( node->children[0] ); + MakeTreePortals_r( node->children[1] ); +} //end of the function MakeTreePortals_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MakeTreePortals( tree_t *tree ) { + +#ifdef ME + Log_Print( "---- Node Portalization ----\n" ); + c_numportalizednodes = 0; + c_portalmemory = 0; + qprintf( "%6d nodes portalized", c_numportalizednodes ); +#endif //ME + + MakeHeadnodePortals( tree ); + MakeTreePortals_r( tree->headnode ); + +#ifdef ME + qprintf( "\n" ); + Log_Write( "\r%6d nodes portalized\r\n", c_numportalizednodes ); + Log_Print( "%6d tiny portals\r\n", c_tinyportals ); + Log_Print( "%6d KB of portal memory\r\n", c_portalmemory >> 10 ); + Log_Print( "%6i KB of winding memory\r\n", WindingMemory() >> 10 ); +#endif //ME +} //end of the function MakeTreePortals + +/* +========================================================= + +FLOOD ENTITIES + +========================================================= +*/ +//#define P_NODESTACK + +node_t *p_firstnode; +node_t *p_lastnode; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef P_NODESTACK +void P_AddNodeToList( node_t *node ) { + node->next = p_firstnode; + p_firstnode = node; + if ( !p_lastnode ) { + p_lastnode = node; + } +} //end of the function P_AddNodeToList +#else //it's a node queue +//add the node to the end of the node list +void P_AddNodeToList( node_t *node ) { + node->next = NULL; + if ( p_lastnode ) { + p_lastnode->next = node; + } else { p_firstnode = node;} + p_lastnode = node; +} //end of the function P_AddNodeToList +#endif //P_NODESTACK +//=========================================================================== +// get the first node from the front of the node list +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *P_NextNodeFromList( void ) { + node_t *node; + + node = p_firstnode; + if ( p_firstnode ) { + p_firstnode = p_firstnode->next; + } + if ( !p_firstnode ) { + p_lastnode = NULL; + } + return node; +} //end of the function P_NextNodeFromList +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FloodPortals( node_t *firstnode ) { + node_t *node; + portal_t *p; + int s; + + firstnode->occupied = 1; + P_AddNodeToList( firstnode ); + + for ( node = P_NextNodeFromList(); node; node = P_NextNodeFromList() ) + { + for ( p = node->portals; p; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); + //if the node at the other side of the portal is occupied already + if ( p->nodes[!s]->occupied ) { + continue; + } + //if it isn't possible to flood through this portal + if ( !Portal_EntityFlood( p, s ) ) { + continue; + } + // + p->nodes[!s]->occupied = node->occupied + 1; + // + P_AddNodeToList( p->nodes[!s] ); + } //end for + } //end for +} //end of the function FloodPortals +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int numrec; + +void FloodPortals_r( node_t *node, int dist ) { + portal_t *p; + int s; +// int i; + + Log_Print( "\r%6d", ++numrec ); + + if ( node->occupied ) { + Error( "FloodPortals_r: node already occupied\n" ); + } + if ( !node ) { + Error( "FloodPortals_r: NULL node\n" ); + } //end if*/ + node->occupied = dist; + + for ( p = node->portals; p; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); + //if the node at the other side of the portal is occupied already + if ( p->nodes[!s]->occupied ) { + continue; + } + //if it isn't possible to flood through this portal + if ( !Portal_EntityFlood( p, s ) ) { + continue; + } + //flood recursively through the current portal + FloodPortals_r( p->nodes[!s], dist + 1 ); + } //end for + Log_Print( "\r%6d", --numrec ); +} //end of the function FloodPortals_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean PlaceOccupant( node_t *headnode, vec3_t origin, entity_t *occupant ) { + node_t *node; + vec_t d; + plane_t *plane; + + //find the leaf to start in + node = headnode; + while ( node->planenum != PLANENUM_LEAF ) + { + if ( node->planenum < 0 || node->planenum > nummapplanes ) { + Error( "PlaceOccupant: invalid node->planenum\n" ); + } //end if + plane = &mapplanes[node->planenum]; + d = DotProduct( origin, plane->normal ) - plane->dist; + if ( d >= 0 ) { + node = node->children[0]; + } else { node = node->children[1];} + if ( !node ) { + Error( "PlaceOccupant: invalid child %d\n", d < 0 ); + } //end if + } //end while + //don't start in solid +// if (node->contents == CONTENTS_SOLID) + //ME: replaced because in LeafNode in brushbsp.c + // some nodes have contents solid with other contents + if ( node->contents & CONTENTS_SOLID ) { + return false; + } + //if the node is already occupied + if ( node->occupied ) { + return false; + } + + //place the occupant in the first leaf + node->occupant = occupant; + + numrec = 0; +// Log_Print("%6d recurses", numrec); +// FloodPortals_r(node, 1); +// Log_Print("\n"); + FloodPortals( node ); + + return true; +} //end of the function PlaceOccupant +//=========================================================================== +// Marks all nodes that can be reached by entites +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +qboolean FloodEntities( tree_t *tree ) { + int i; + int x, y; + vec3_t origin; + char *cl; + qboolean inside; + node_t *headnode; + + headnode = tree->headnode; + Log_Print( "------ FloodEntities -------\n" ); + inside = false; + tree->outside_node.occupied = 0; + + //start at entity 1 not the world ( = 0) + for ( i = 1; i < num_entities; i++ ) + { + GetVectorForKey( &entities[i], "origin", origin ); + if ( VectorCompare( origin, vec3_origin ) ) { + continue; + } + + cl = ValueForKey( &entities[i], "classname" ); + origin[2] += 1; //so objects on floor are ok + +// Log_Print("flooding from entity %d: %s\n", i, cl); + //nudge playerstart around if needed so clipping hulls allways + //have a valid point + if ( !strcmp( cl, "info_player_start" ) ) { + for ( x = -16; x <= 16; x += 16 ) + { + for ( y = -16; y <= 16; y += 16 ) + { + origin[0] += x; + origin[1] += y; + if ( PlaceOccupant( headnode, origin, &entities[i] ) ) { + inside = true; + x = 999; //stop for this info_player_start + break; + } //end if + origin[0] -= x; + origin[1] -= y; + } //end for + } //end for + } //end if + else + { + if ( PlaceOccupant( headnode, origin, &entities[i] ) ) { + inside = true; + } //end if + } //end else + } //end for + + if ( !inside ) { + Log_Print( "WARNING: no entities inside\n" ); + } //end if + else if ( tree->outside_node.occupied ) { + Log_Print( "WARNING: entity reached from outside\n" ); + } //end else if + + return (qboolean)( inside && !tree->outside_node.occupied ); +} //end of the function FloodEntities + +/* +========================================================= + +FILL OUTSIDE + +========================================================= +*/ + +int c_outside; +int c_inside; +int c_solid; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FillOutside_r( node_t *node ) { + if ( node->planenum != PLANENUM_LEAF ) { + FillOutside_r( node->children[0] ); + FillOutside_r( node->children[1] ); + return; + } //end if + // anything not reachable by an entity + // can be filled away (by setting solid contents) + if ( !node->occupied ) { + if ( !( node->contents & CONTENTS_SOLID ) ) { + c_outside++; + node->contents |= CONTENTS_SOLID; + } //end if + else + { + c_solid++; + } //end else + } //end if + else + { + c_inside++; + } //end else +} //end of the function FillOutside_r +//=========================================================================== +// Fill all nodes that can't be reached by entities +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FillOutside( node_t *headnode ) { + c_outside = 0; + c_inside = 0; + c_solid = 0; + Log_Print( "------- FillOutside --------\n" ); + FillOutside_r( headnode ); + Log_Print( "%5i solid leaves\n", c_solid ); + Log_Print( "%5i leaves filled\n", c_outside ); + Log_Print( "%5i inside leaves\n", c_inside ); +} //end of the function FillOutside + +/* +========================================================= + +FLOOD AREAS + +========================================================= +*/ + +int c_areas; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FloodAreas_r( node_t *node ) { + portal_t *p; + int s; + bspbrush_t *b; + entity_t *e; + + if ( node->contents == CONTENTS_AREAPORTAL ) { + // this node is part of an area portal + b = node->brushlist; + e = &entities[b->original->entitynum]; + + // if the current area has allready touched this + // portal, we are done + if ( e->portalareas[0] == c_areas || e->portalareas[1] == c_areas ) { + return; + } + + // note the current area as bounding the portal + if ( e->portalareas[1] ) { + Log_Print( "WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum ); + return; + } + if ( e->portalareas[0] ) { + e->portalareas[1] = c_areas; + } else { + e->portalareas[0] = c_areas; + } + + return; + } //end if + + if ( node->area ) { + return; // allready got it + } + node->area = c_areas; + + for ( p = node->portals ; p ; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); +#if 0 + if ( p->nodes[!s]->occupied ) { + continue; + } +#endif + if ( !Portal_EntityFlood( p, s ) ) { + continue; + } + + FloodAreas_r( p->nodes[!s] ); + } //end for +} //end of the function FloodAreas_r +//=========================================================================== +// Just decend the tree, and for each node that hasn't had an +// area set, flood fill out from there +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FindAreas_r( node_t *node ) { + if ( node->planenum != PLANENUM_LEAF ) { + FindAreas_r( node->children[0] ); + FindAreas_r( node->children[1] ); + return; + } + + if ( node->area ) { + return; // allready got it + + } + if ( node->contents & CONTENTS_SOLID ) { + return; + } + + if ( !node->occupied ) { + return; // not reachable by entities + + } + // area portals are allways only flooded into, never + // out of + if ( node->contents == CONTENTS_AREAPORTAL ) { + return; + } + + c_areas++; + FloodAreas_r( node ); +} //end of the function FindAreas_r +//=========================================================================== +// Just decend the tree, and for each node that hasn't had an +// area set, flood fill out from there +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SetAreaPortalAreas_r( node_t *node ) { + bspbrush_t *b; + entity_t *e; + + if ( node->planenum != PLANENUM_LEAF ) { + SetAreaPortalAreas_r( node->children[0] ); + SetAreaPortalAreas_r( node->children[1] ); + return; + } //end if + + if ( node->contents == CONTENTS_AREAPORTAL ) { + if ( node->area ) { + return; // allready set + + } + b = node->brushlist; + e = &entities[b->original->entitynum]; + node->area = e->portalareas[0]; + if ( !e->portalareas[1] ) { + Log_Print( "WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum ); + return; + } //end if + } //end if +} //end of the function SetAreaPortalAreas_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void EmitAreaPortals(node_t *headnode) +{ + int i, j; + entity_t *e; + dareaportal_t *dp; + + if (c_areas > MAX_MAP_AREAS) + Error ("MAX_MAP_AREAS"); + numareas = c_areas+1; + numareaportals = 1; // leave 0 as an error + + for (i=1 ; i<=c_areas ; i++) + { + dareas[i].firstareaportal = numareaportals; + for (j=0 ; jareaportalnum) + continue; + dp = &dareaportals[numareaportals]; + if (e->portalareas[0] == i) + { + dp->portalnum = e->areaportalnum; + dp->otherarea = e->portalareas[1]; + numareaportals++; + } //end if + else if (e->portalareas[1] == i) + { + dp->portalnum = e->areaportalnum; + dp->otherarea = e->portalareas[0]; + numareaportals++; + } //end else if + } //end for + dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal; + } //end for + + Log_Print("%5i numareas\n", numareas); + Log_Print("%5i numareaportals\n", numareaportals); +} //end of the function EmitAreaPortals +*/ +//=========================================================================== +// Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FloodAreas( tree_t *tree ) { + Log_Print( "--- FloodAreas ---\n" ); + FindAreas_r( tree->headnode ); + SetAreaPortalAreas_r( tree->headnode ); + Log_Print( "%5i areas\n", c_areas ); +} //end of the function FloodAreas +//=========================================================================== +// Finds a brush side to use for texturing the given portal +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FindPortalSide( portal_t *p ) { + int viscontents; + bspbrush_t *bb; + mapbrush_t *brush; + node_t *n; + int i,j; + int planenum; + side_t *side, *bestside; + float dot, bestdot; + plane_t *p1, *p2; + + // decide which content change is strongest + // solid > lava > water, etc + viscontents = VisibleContents( p->nodes[0]->contents ^ p->nodes[1]->contents ); + if ( !viscontents ) { + return; + } + + planenum = p->onnode->planenum; + bestside = NULL; + bestdot = 0; + + for ( j = 0 ; j < 2 ; j++ ) + { + n = p->nodes[j]; + p1 = &mapplanes[p->onnode->planenum]; + for ( bb = n->brushlist ; bb ; bb = bb->next ) + { + brush = bb->original; + if ( !( brush->contents & viscontents ) ) { + continue; + } + for ( i = 0 ; i < brush->numsides ; i++ ) + { + side = &brush->original_sides[i]; + if ( side->flags & SFL_BEVEL ) { + continue; + } + if ( side->texinfo == TEXINFO_NODE ) { + continue; // non-visible + } + if ( ( side->planenum & ~1 ) == planenum ) { // exact match + bestside = &brush->original_sides[i]; + goto gotit; + } //end if + // see how close the match is + p2 = &mapplanes[side->planenum & ~1]; + dot = DotProduct( p1->normal, p2->normal ); + if ( dot > bestdot ) { + bestdot = dot; + bestside = side; + } //end if + } //end for + } //end for + } //end for + +gotit: + if ( !bestside ) { + Log_Print( "WARNING: side not found for portal\n" ); + } + + p->sidefound = true; + p->side = bestside; +} //end of the function FindPortalSide +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MarkVisibleSides_r( node_t *node ) { + portal_t *p; + int s; + + if ( node->planenum != PLANENUM_LEAF ) { + MarkVisibleSides_r( node->children[0] ); + MarkVisibleSides_r( node->children[1] ); + return; + } //end if + + // empty leaves are never boundary leaves + if ( !node->contents ) { + return; + } + + // see if there is a visible face + for ( p = node->portals ; p ; p = p->next[!s] ) + { + s = ( p->nodes[0] == node ); + if ( !p->onnode ) { + continue; // edge of world + } + if ( !p->sidefound ) { + FindPortalSide( p ); + } + if ( p->side ) { + p->side->flags |= SFL_VISIBLE; + } + } //end for +} //end of the function MarkVisibleSides_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void MarkVisibleSides( tree_t *tree, int startbrush, int endbrush ) { + int i, j; + mapbrush_t *mb; + int numsides; + + Log_Print( "--- MarkVisibleSides ---\n" ); + + // clear all the visible flags + for ( i = startbrush ; i < endbrush ; i++ ) + { + mb = &mapbrushes[i]; + + numsides = mb->numsides; + for ( j = 0 ; j < numsides ; j++ ) + mb->original_sides[j].flags &= ~SFL_VISIBLE; + } + + // set visible flags on the sides that are used by portals + MarkVisibleSides_r( tree->headnode ); +} //end of the function MarkVisibleSides + diff --git a/src/bspc/prtfile.c b/src/bspc/prtfile.c new file mode 100644 index 0000000..65883b9 --- /dev/null +++ b/src/bspc/prtfile.c @@ -0,0 +1,287 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// NO LONGER USED +#if 0 +#include "qbsp.h" + +extern dleaf_t dleafs[MAX_MAP_LEAFS]; +/* +============================================================================== + +PORTAL FILE GENERATION + +Save out name.prt for qvis to read +============================================================================== +*/ + + +#define PORTALFILE "PRT1" + +FILE *pf; +int num_visclusters; // clusters the player can be in +int num_visportals; + +void WriteFloat2( FILE *f, vec_t v ) { + if ( fabs( v - Q_rint( v ) ) < 0.001 ) { + fprintf( f,"%i ",(int)Q_rint( v ) ); + } else { + fprintf( f,"%f ",v ); + } +} + +/* +================= +WritePortalFile_r +================= +*/ +void WritePortalFile_r( node_t *node ) { + int i, s; + portal_t *p; + winding_t *w; + vec3_t normal; + vec_t dist; + + // decision node + if ( node->planenum != PLANENUM_LEAF && !node->detail_seperator ) { + WritePortalFile_r( node->children[0] ); + WritePortalFile_r( node->children[1] ); + return; + } + + if ( node->contents & CONTENTS_SOLID ) { + return; + } + + for ( p = node->portals ; p ; p = p->next[s] ) + { + w = p->winding; + s = ( p->nodes[1] == node ); + if ( w && p->nodes[0] == node ) { + if ( !Portal_VisFlood( p ) ) { + continue; + } + // write out to the file + + // sometimes planes get turned around when they are very near + // the changeover point between different axis. interpret the + // plane the same way vis will, and flip the side orders if needed + // FIXME: is this still relevent? + WindingPlane( w, normal, &dist ); + if ( DotProduct( p->plane.normal, normal ) < 0.99 ) { // backwards... + fprintf( pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster ); + } else { + fprintf( pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster ); + } + for ( i = 0 ; i < w->numpoints ; i++ ) + { + fprintf( pf,"(" ); + WriteFloat2( pf, w->p[i][0] ); + WriteFloat2( pf, w->p[i][1] ); + WriteFloat2( pf, w->p[i][2] ); + fprintf( pf,") " ); + } + fprintf( pf,"\n" ); + } + } + +} + +/* +================ +FillLeafNumbers_r + +All of the leafs under node will have the same cluster +================ +*/ +void FillLeafNumbers_r( node_t *node, int num ) { + if ( node->planenum == PLANENUM_LEAF ) { + if ( node->contents & CONTENTS_SOLID ) { + node->cluster = -1; + } else { + node->cluster = num; + } + return; + } + node->cluster = num; + FillLeafNumbers_r( node->children[0], num ); + FillLeafNumbers_r( node->children[1], num ); +} + +/* +================ +NumberLeafs_r +================ +*/ +void NumberLeafs_r( node_t *node ) { + portal_t *p; + + if ( node->planenum != PLANENUM_LEAF && !node->detail_seperator ) { // decision node + node->cluster = -99; + NumberLeafs_r( node->children[0] ); + NumberLeafs_r( node->children[1] ); + return; + } + + // either a leaf or a detail cluster + + if ( node->contents & CONTENTS_SOLID ) { // solid block, viewpoint never inside + node->cluster = -1; + return; + } + + FillLeafNumbers_r( node, num_visclusters ); + num_visclusters++; + + // count the portals + for ( p = node->portals ; p ; ) + { + if ( p->nodes[0] == node ) { // only write out from first leaf + if ( Portal_VisFlood( p ) ) { + num_visportals++; + } + p = p->next[0]; + } else { + p = p->next[1]; + } + } + +} + + +/* +================ +CreateVisPortals_r +================ +*/ +void CreateVisPortals_r( node_t *node ) { + // stop as soon as we get to a detail_seperator, which + // means that everything below is in a single cluster + if ( node->planenum == PLANENUM_LEAF || node->detail_seperator ) { + return; + } + + MakeNodePortal( node ); + SplitNodePortals( node ); + + CreateVisPortals_r( node->children[0] ); + CreateVisPortals_r( node->children[1] ); +} + +/* +================ +FinishVisPortals_r +================ +*/ +void FinishVisPortals2_r( node_t *node ) { + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + MakeNodePortal( node ); + SplitNodePortals( node ); + + FinishVisPortals2_r( node->children[0] ); + FinishVisPortals2_r( node->children[1] ); +} + +void FinishVisPortals_r( node_t *node ) { + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + if ( node->detail_seperator ) { + FinishVisPortals2_r( node ); + return; + } + + FinishVisPortals_r( node->children[0] ); + FinishVisPortals_r( node->children[1] ); +} + + +int clusterleaf; +void SaveClusters_r( node_t *node ) { + if ( node->planenum == PLANENUM_LEAF ) { + dleafs[clusterleaf++].cluster = node->cluster; + return; + } + SaveClusters_r( node->children[0] ); + SaveClusters_r( node->children[1] ); +} + +/* +================ +WritePortalFile +================ +*/ +void WritePortalFile( tree_t *tree ) { + char filename[1024]; + node_t *headnode; + + qprintf( "--- WritePortalFile ---\n" ); + + headnode = tree->headnode; + num_visclusters = 0; + num_visportals = 0; + + Tree_FreePortals_r( headnode ); + + MakeHeadnodePortals( tree ); + + CreateVisPortals_r( headnode ); + +// set the cluster field in every leaf and count the total number of portals + + NumberLeafs_r( headnode ); + +// write the file + sprintf( filename, "%s.prt", source ); + printf( "writing %s\n", filename ); + pf = fopen( filename, "w" ); + if ( !pf ) { + Error( "Error opening %s", filename ); + } + + fprintf( pf, "%s\n", PORTALFILE ); + fprintf( pf, "%i\n", num_visclusters ); + fprintf( pf, "%i\n", num_visportals ); + + qprintf( "%5i visclusters\n", num_visclusters ); + qprintf( "%5i visportals\n", num_visportals ); + + WritePortalFile_r( headnode ); + + fclose( pf ); + + // we need to store the clusters out now because ordering + // issues made us do this after writebsp... + clusterleaf = 1; + SaveClusters_r( headnode ); +} +#endif diff --git a/src/bspc/q2files.h b/src/bspc/q2files.h new file mode 100644 index 0000000..f8e3f44 --- /dev/null +++ b/src/bspc/q2files.h @@ -0,0 +1,497 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +/* +======================================================================== + +The .pak files are just a linear collapse of a directory tree + +======================================================================== +*/ + +#define IDPAKHEADER ( ( 'K' << 24 ) + ( 'C' << 16 ) + ( 'A' << 8 ) + 'P' ) + +typedef struct +{ + char name[56]; + int filepos, filelen; +} dpackfile_t; + +typedef struct +{ + int ident; // == IDPAKHEADER + int dirofs; + int dirlen; +} dpackheader_t; + +#define MAX_FILES_IN_PACK 4096 + + +/* +======================================================================== + +PCX files are used for as many images as possible + +======================================================================== +*/ + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +/* +======================================================================== + +.MD2 triangle model file format + +======================================================================== +*/ + +#define IDALIASHEADER ( ( '2' << 24 ) + ( 'P' << 16 ) + ( 'D' << 8 ) + 'I' ) +#define ALIAS_VERSION 8 + +#define MAX_TRIANGLES 4096 +#define MAX_VERTS 2048 +#define MAX_FRAMES 512 +#define MAX_MD2SKINS 32 +#define MAX_SKINNAME 64 + +typedef struct +{ + short s; + short t; +} dstvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} dtriangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} dtrivertx_t; + +#define DTRIVERTX_V0 0 +#define DTRIVERTX_V1 1 +#define DTRIVERTX_V2 2 +#define DTRIVERTX_LNI 3 +#define DTRIVERTX_SIZE 4 + +typedef struct +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + dtrivertx_t verts[1]; // variable sized +} daliasframe_t; + + +// the glcmd format: +// a positive integer starts a tristrip command, followed by that many +// vertex structures. +// a negative integer starts a trifan command, followed by -x vertexes +// a zero indicates the end of the command list. +// a vertex consists of a floating point s, a floating point t, +// and an integer vertex index. + + +typedef struct +{ + int ident; + int version; + + int skinwidth; + int skinheight; + int framesize; // byte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + + int ofs_skins; // each skin is a MAX_SKINNAME string + int ofs_st; // byte offset from start for stverts + int ofs_tris; // offset for dtriangles + int ofs_frames; // offset for first frame + int ofs_glcmds; + int ofs_end; // end of file + +} dmdl_t; + +/* +======================================================================== + +.SP2 sprite file format + +======================================================================== +*/ + +#define IDSPRITEHEADER ( ( '2' << 24 ) + ( 'S' << 16 ) + ( 'D' << 8 ) + 'I' ) +// little-endian "IDS2" +#define SPRITE_VERSION 2 + +typedef struct +{ + int width, height; + int origin_x, origin_y; // raster coordinates inside pic + char name[MAX_SKINNAME]; // name of pcx file +} dsprframe_t; + +typedef struct { + int ident; + int version; + int numframes; + dsprframe_t frames[1]; // variable sized +} dsprite_t; + +/* +============================================================================== + + .WAL texture file format + +============================================================================== +*/ + + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[32]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored + char animname[32]; // next frame in animation chain + int flags; + int contents; + int value; +} miptex_t; + + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + +#define IDBSPHEADER ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' ) +// little-endian "IBSP" + +#define BSPVERSION 38 + + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define MAX_MAP_MODELS 1024 +#define MAX_MAP_BRUSHES 16384 +#define MAX_MAP_ENTITIES 4096 +#define MAX_MAP_ENTSTRING ( 128 * MAX_MAP_ENTITIES ) +#define MAX_MAP_TEXINFO 8192 + +#define MAX_MAP_AREAS 256 +#define MAX_MAP_AREAPORTALS 1024 +#define MAX_MAP_PLANES 65536 +#define MAX_MAP_NODES 65536 +#define MAX_MAP_BRUSHSIDES 65536 +#define MAX_MAP_LEAFS 65536 +#define MAX_MAP_VERTS 65536 +#define MAX_MAP_FACES 65536 +#define MAX_MAP_LEAFFACES 65536 +#define MAX_MAP_LEAFBRUSHES 65536 +#define MAX_MAP_PORTALS 65536 +#define MAX_MAP_EDGES 128000 +#define MAX_MAP_SURFEDGES 256000 +#define MAX_MAP_LIGHTING 0x320000 +#define MAX_MAP_VISIBILITY 0x280000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_VERTEXES 2 +#define LUMP_VISIBILITY 3 +#define LUMP_NODES 4 +#define LUMP_TEXINFO 5 +#define LUMP_FACES 6 +#define LUMP_LIGHTING 7 +#define LUMP_LEAFS 8 +#define LUMP_LEAFFACES 9 +#define LUMP_LEAFBRUSHES 10 +#define LUMP_EDGES 11 +#define LUMP_SURFEDGES 12 +#define LUMP_MODELS 13 +#define LUMP_BRUSHES 14 +#define LUMP_BRUSHSIDES 15 +#define LUMP_POP 16 +#define LUMP_AREAS 17 +#define LUMP_AREAPORTALS 18 +#define HEADER_LUMPS 19 + +typedef struct +{ + int ident; + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree +} dmodel_t; + + +typedef struct +{ + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +// planes (x&~1) and (x&~1)+1 are allways opposites + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// these definitions also need to be in q_shared.h! + +// lower bits are stronger, and will eat weaker brushes completely +#define CONTENTS_SOLID 1 // an eye is never valid in a solid +#define CONTENTS_WINDOW 2 // translucent, but not watery +#define CONTENTS_AUX 4 +#define CONTENTS_LAVA 8 +#define CONTENTS_SLIME 16 +#define CONTENTS_WATER 32 +#define CONTENTS_MIST 64 +#define LAST_VISIBLE_CONTENTS 64 + +// remaining contents are non-visible, and don't eat brushes + +#define CONTENTS_AREAPORTAL 0x8000 + +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 + +// currents can be added to any other contents, and may be mixed +#define CONTENTS_CURRENT_0 0x40000 +#define CONTENTS_CURRENT_90 0x80000 +#define CONTENTS_CURRENT_180 0x100000 +#define CONTENTS_CURRENT_270 0x200000 +#define CONTENTS_CURRENT_UP 0x400000 +#define CONTENTS_CURRENT_DOWN 0x800000 + +#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game +#define CONTENTS_DEADMONSTER 0x4000000 +#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs +//renamed because it's in conflict with the Q3A translucent contents +#define CONTENTS_Q2TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define CONTENTS_LADDER 0x20000000 + + + +#define SURF_LIGHT 0x1 // value will hold the light strength + +#if 0 + +#define SURF_SLICK 0x2 // effects game physics + +#define SURF_SKY 0x4 // don't draw, but add to skybox +#define SURF_WARP 0x8 // turbulent water warp +#define SURF_TRANS33 0x10 +#define SURF_TRANS66 0x20 +#define SURF_FLOWING 0x40 // scroll towards angle +#define SURF_NODRAW 0x80 // don't bother referencing the texture + +#define SURF_HINT 0x100 // make a primary bsp splitter +#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes + +#endif + + +typedef struct +{ + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + int value; // light emission, etc + char texture[32]; // texture name (textures/*.wal) + int nexttexinfo; // for animations, -1 = end of chain +} texinfo_t; + + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + unsigned short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + +typedef struct +{ + int contents; // OR of all brushes (not needed?) + + short cluster; + short area; + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; +} dleaf_t; + +typedef struct +{ + unsigned short planenum; // facing out of the leaf + short texinfo; +} dbrushside_t; + +typedef struct +{ + int firstside; + int numsides; + int contents; +} dbrush_t; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the visibility lump consists of a header with a count, then +// byte offsets for the PVS and PHS of each cluster, then the raw +// compressed bit vectors +#define DVIS_PVS 0 +#define DVIS_PHS 1 +typedef struct +{ + int numclusters; + int bitofs[8][2]; // bitofs[numclusters][2] +} dvis_t; + +// each area has a list of portals that lead into other areas +// when portals are closed, other areas may not be visible or +// hearable even if the vis info says that it should be +typedef struct +{ + int portalnum; + int otherarea; +} dareaportal_t; + +typedef struct +{ + int numareaportals; + int firstareaportal; +} darea_t; diff --git a/src/bspc/q3files.h b/src/bspc/q3files.h new file mode 100644 index 0000000..6b4d344 --- /dev/null +++ b/src/bspc/q3files.h @@ -0,0 +1,382 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#ifndef __QFILES_H__ +#define __QFILES_H__ + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +// surface geometry should not exceed these limits +#define SHADER_MAX_VERTEXES 1000 +#define SHADER_MAX_INDEXES ( 6 * SHADER_MAX_VERTEXES ) + + +// the maximum size of game reletive pathnames +#define MAX_QPATH 64 + + +/* +======================================================================== + +PCX files are used for 8 bit images + +======================================================================== +* + +typedef struct { + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +/* +======================================================================== + +TGA files are used for 24/32 bit images + +======================================================================== +* + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + + +*/ + +/* +======================================================================== + +.MD3 triangle model file format + +======================================================================== +*/ + +#define MD3_IDENT ( ( '3' << 24 ) + ( 'P' << 16 ) + ( 'D' << 8 ) + 'I' ) +#define MD3_VERSION 15 + +// limits +#define MD3_MAX_LODS 4 +#define MD3_MAX_TRIANGLES 8192 // per surface +#define MD3_MAX_VERTS 4096 // per surface +#define MD3_MAX_SHADERS 256 // per surface +#define MD3_MAX_FRAMES 1024 // per model +#define MD3_MAX_SURFACES 32 // per model +#define MD3_MAX_TAGS 16 // per frame + +// vertex scales +#define MD3_XYZ_SCALE ( 1.0 / 64 ) + +typedef struct md3Frame_s { + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + char name[16]; +} md3Frame_t; + +typedef struct md3Tag_s { + char name[MAX_QPATH]; // tag name + vec3_t origin; + vec3_t axis[3]; +} md3Tag_t; + +/* +** md3Surface_t +** +** CHUNK SIZE +** header sizeof( md3Surface_t ) +** shaders sizeof( md3Shader_t ) * numShaders +** triangles[0] sizeof( md3Triangle_t ) * numTriangles +** st sizeof( md3St_t ) * numVerts +** XyzNormals sizeof( md3XyzNormal_t ) * numVerts * numFrames +*/ + +typedef struct { + int ident; // + + char name[MAX_QPATH]; // polyset name + + int flags; + int numFrames; // all surfaces in a model should have the same + + int numShaders; // all surfaces in a model should have the same + int numVerts; + + int numTriangles; + int ofsTriangles; + + int ofsShaders; // offset from start of md3Surface_t + int ofsSt; // texture coords are common for all frames + int ofsXyzNormals; // numVerts * numFrames + + int ofsEnd; // next surface follows +} md3Surface_t; + +typedef struct { + char name[MAX_QPATH]; + int shaderIndex; // for in-game use +} md3Shader_t; + +typedef struct { + int indexes[3]; +} md3Triangle_t; + +typedef struct { + float st[2]; +} md3St_t; + +typedef struct { + short xyz[3]; + short normal; +} md3XyzNormal_t; + +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; // model name + + int flags; + + int numFrames; + int numTags; + int numSurfaces; + + int numSkins; + + int ofsFrames; // offset for first frame + int ofsTags; // numFrames * numTags + int ofsSurfaces; // first surface, others follow + + int ofsEnd; // end of file +} md3Header_t; + + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + + +#define Q3_BSP_IDENT ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' ) +// little-endian "IBSP" + +#define Q3_BSP_VERSION 47 + + +// there shouldn't be any problem with increasing these values at the +// expense of more memory allocation in the utilities +#define Q3_MAX_MAP_MODELS 0x400 +#define Q3_MAX_MAP_BRUSHES 0x8000 +#define Q3_MAX_MAP_ENTITIES 0x800 +#define Q3_MAX_MAP_ENTSTRING 0x10000 +#define Q3_MAX_MAP_SHADERS 0x400 + +#define Q3_MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match! +#define Q3_MAX_MAP_FOGS 0x100 +#define Q3_MAX_MAP_PLANES 0x40000 +#define Q3_MAX_MAP_NODES 0x10000 +#define Q3_MAX_MAP_BRUSHSIDES 0x100000 +#define Q3_MAX_MAP_LEAFS 0x10000 +#define Q3_MAX_MAP_LEAFFACES 0x10000 +#define Q3_MAX_MAP_LEAFBRUSHES 0x20000 +#define Q3_MAX_MAP_PORTALS 0x10000 +#define Q3_MAX_MAP_LIGHTING 0x400000 +#define Q3_MAX_MAP_LIGHTGRID 0x400000 +#define Q3_MAX_MAP_VISIBILITY 0x200000 + +#define Q3_MAX_MAP_DRAW_SURFS 0x20000 +#define Q3_MAX_MAP_DRAW_VERTS 0x80000 +#define Q3_MAX_MAP_DRAW_INDEXES 0x80000 + + +// key / value pair sizes in the entities lump +#define Q3_MAX_KEY 32 +#define Q3_MAX_VALUE 1024 + +// the editor uses these predefined yaw angles to orient entities up or down +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + +#define LIGHTMAP_WIDTH 128 +#define LIGHTMAP_HEIGHT 128 + + +//============================================================================= + + +typedef struct { + int fileofs, filelen; +} q3_lump_t; + +#define Q3_LUMP_ENTITIES 0 +#define Q3_LUMP_SHADERS 1 +#define Q3_LUMP_PLANES 2 +#define Q3_LUMP_NODES 3 +#define Q3_LUMP_LEAFS 4 +#define Q3_LUMP_LEAFSURFACES 5 +#define Q3_LUMP_LEAFBRUSHES 6 +#define Q3_LUMP_MODELS 7 +#define Q3_LUMP_BRUSHES 8 +#define Q3_LUMP_BRUSHSIDES 9 +#define Q3_LUMP_DRAWVERTS 10 +#define Q3_LUMP_DRAWINDEXES 11 +#define Q3_LUMP_FOGS 12 +#define Q3_LUMP_SURFACES 13 +#define Q3_LUMP_LIGHTMAPS 14 +#define Q3_LUMP_LIGHTGRID 15 +#define Q3_LUMP_VISIBILITY 16 +#define Q3_HEADER_LUMPS 17 + +typedef struct { + int ident; + int version; + + q3_lump_t lumps[Q3_HEADER_LUMPS]; +} q3_dheader_t; + +typedef struct { + float mins[3], maxs[3]; + int firstSurface, numSurfaces; + int firstBrush, numBrushes; +} q3_dmodel_t; + +typedef struct { + char shader[MAX_QPATH]; + int surfaceFlags; + int contentFlags; +} q3_dshader_t; + +// planes (x&~1) and (x&~1)+1 are allways opposites + +typedef struct { + float normal[3]; + float dist; +} q3_dplane_t; + +typedef struct { + int planeNum; + int children[2]; // negative numbers are -(leafs+1), not nodes + int mins[3]; // for frustom culling + int maxs[3]; +} q3_dnode_t; + +typedef struct { + int cluster; // -1 = opaque cluster (do I still store these?) + int area; + + int mins[3]; // for frustum culling + int maxs[3]; + + int firstLeafSurface; + int numLeafSurfaces; + + int firstLeafBrush; + int numLeafBrushes; +} q3_dleaf_t; + +typedef struct { + int planeNum; // positive plane side faces out of the leaf + int shaderNum; +} q3_dbrushside_t; + +typedef struct { + int firstSide; + int numSides; + int shaderNum; // the shader that determines the contents flags +} q3_dbrush_t; + +typedef struct { + char shader[MAX_QPATH]; + int brushNum; + int visibleSide; // the brush side that ray tests need to clip against (-1 == none) +} q3_dfog_t; + +typedef struct { + vec3_t xyz; + float st[2]; + float lightmap[2]; + vec3_t normal; + byte color[4]; +} q3_drawVert_t; + +typedef enum { + MST_BAD, + MST_PLANAR, + MST_PATCH, + MST_TRIANGLE_SOUP, + MST_FLARE, + MST_FOLIAGE +} q3_mapSurfaceType_t; + +typedef struct { + int shaderNum; + int fogNum; + int surfaceType; + + int firstVert; + int numVerts; + + int firstIndex; + int numIndexes; + + int lightmapNum; + int lightmapX, lightmapY; + int lightmapWidth, lightmapHeight; + + vec3_t lightmapOrigin; + vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds + + int patchWidth; + int patchHeight; +} q3_dsurface_t; + + +#endif diff --git a/src/bspc/qbsp.h b/src/bspc/qbsp.h new file mode 100644 index 0000000..0eb97b5 --- /dev/null +++ b/src/bspc/qbsp.h @@ -0,0 +1,465 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +#if defined( WIN32 ) || defined( _WIN32 ) +#include +#endif +#include +#include "l_cmd.h" +#include "l_math.h" +#include "l_poly.h" +#include "l_threads.h" +#include "../botlib/l_script.h" +#include "l_bsp_ent.h" +#include "q2files.h" +#include "l_mem.h" +#include "l_utils.h" +#include "l_log.h" +#include "l_qfiles.h" + +//Mr Elusive shit +#define ME +#define DEBUG +#define NODELIST +#define SIN +#define MRE_ET + +#define MAX_BRUSH_SIDES 128 //maximum number of sides per brush +#define CLIP_EPSILON 0.1 +//#define MAX_MAP_BOUNDS 65535 +#define MAX_MAP_BOUNDS ( 128 * 1024 ) // (SA) (9/17/01) new map dimensions (from Q3TA) +#define BOGUS_RANGE ( MAX_MAP_BOUNDS + 128 ) //somewhere outside the map + +#define TEXINFO_NODE -1 //side is allready on a node +#define PLANENUM_LEAF -1 //used for leaf nodes +#define MAXEDGES 20 //maximum number of face edges +#define MAX_NODE_BRUSHES 8 //maximum brushes in a node +//side flags +#define SFL_TESTED 1 +#define SFL_VISIBLE 2 +#define SFL_BEVEL 4 +#define SFL_TEXTURED 8 +#define SFL_CURVE 16 + +//map plane +typedef struct plane_s +{ + vec3_t normal; + vec_t dist; + byte /*int*/ type; + byte /*int*/ signbits; + struct plane_s *hash_chain; +} plane_t; +//brush texture +typedef struct +{ + vec_t shift[2]; + vec_t rotate; + vec_t scale[2]; + char name[32]; + int flags; + int value; +} brush_texture_t; +//brush side +typedef struct side_s +{ + int planenum; // map plane this side is in + int texinfo; // texture reference + winding_t *winding; // winding of this side + struct side_s *original; // bspbrush_t sides will reference the mapbrush_t sides + int lightinfo; // for SIN only + int contents; // from miptex + int surf; // from miptex + unsigned short flags; // side flags +} side_t; //sizeof(side_t) = 36 +//map brush +typedef struct mapbrush_s +{ + int entitynum; + int brushnum; + + int contents; +#ifdef ME + int expansionbbox; //bbox used for expansion of the brush + int leafnum; + int modelnum; +#endif + + vec3_t mins, maxs; + + int numsides; + side_t *original_sides; +} mapbrush_t; +//bsp face +typedef struct face_s +{ + struct face_s *next; // on node + + // the chain of faces off of a node can be merged or split, + // but each face_t along the way will remain in the chain + // until the entire tree is freed + struct face_s *merged; // if set, this face isn't valid anymore + struct face_s *split[2]; // if set, this face isn't valid anymore + + struct portal_s *portal; + int texinfo; +#ifdef SIN + int lightinfo; +#endif + int planenum; + int contents; // faces in different contents can't merge + int outputnumber; + winding_t *w; + int numpoints; + qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex + int vertexnums[MAXEDGES]; +} face_t; +//bsp brush +typedef struct bspbrush_s +{ + struct bspbrush_s *next; + vec3_t mins, maxs; + int side, testside; // side of node during construction + mapbrush_t *original; + int numsides; + side_t sides[6]; // variably sized +} bspbrush_t; //sizeof(bspbrush_t) = 44 + numsides * sizeof(side_t) +//bsp node +typedef struct node_s +{ + //both leafs and nodes + int planenum; // -1 = leaf node + struct node_s *parent; + vec3_t mins, maxs; // valid after portalization + bspbrush_t *volume; // one for each leaf/node + + // nodes only + qboolean detail_seperator; // a detail brush caused the split + side_t *side; // the side that created the node + struct node_s *children[2]; + face_t *faces; + + // leafs only + bspbrush_t *brushlist; // fragments of all brushes in this leaf + int contents; // OR of all brush contents + int occupied; // 1 or greater can reach entity + entity_t *occupant; // for leak file testing + int cluster; // for portalfile writing + int area; // for areaportals + struct portal_s *portals; // also on nodes during construction +#ifdef NODELIST + struct node_s *next; //next node in the nodelist +#endif +#ifdef ME + int expansionbboxes; //OR of all bboxes used for expansion of the brushes + int modelnum; +#endif +} node_t; //sizeof(node_t) = 80 bytes +//bsp portal +typedef struct portal_s +{ + plane_t plane; + node_t *onnode; // NULL = outside box + node_t *nodes[2]; // [0] = front side of plane + struct portal_s *next[2]; + winding_t *winding; + + qboolean sidefound; // false if ->side hasn't been checked + side_t *side; // NULL = non-visible + face_t *face[2]; // output face in bsp file +#ifdef ME + struct tmp_face_s *tmpface; //pointer to the tmpface created for this portal + int planenum; //number of the map plane used by the portal +#endif +} portal_t; +//bsp tree +typedef struct +{ + node_t *headnode; + node_t outside_node; + vec3_t mins, maxs; +} tree_t; + +//============================================================================= +// bspc.c +//============================================================================= + +extern qboolean noprune; +extern qboolean nodetail; +extern qboolean fulldetail; +extern qboolean nomerge; +extern qboolean nosubdiv; +extern qboolean nowater; +extern qboolean noweld; +extern qboolean noshare; +extern qboolean notjunc; +extern qboolean onlyents; +#ifdef ME +extern qboolean nocsg; +extern qboolean create_aas; +extern qboolean freetree; +extern qboolean lessbrushes; +extern qboolean nobrushmerge; +extern qboolean cancelconversion; +extern qboolean noliquids; +extern qboolean capsule_collision; +extern qboolean writeaasmap; +extern qboolean groundonly; +extern qboolean writebrushmap; +extern qboolean noPatches; +#endif //ME + +extern float subdivide_size; +extern float bsp_grid_size; +extern vec_t microvolume; + +extern char outbase[32]; +extern char source[1024]; + +//============================================================================= +// map.c +//============================================================================= + +#define MAX_MAPFILE_PLANES 512000 //128000 +#define MAX_MAPFILE_BRUSHES 65535 //16384 +#define MAX_MAPFILE_BRUSHSIDES ( MAX_MAPFILE_BRUSHES * 8 ) +#define MAX_MAPFILE_TEXINFO 8192 + +extern int entity_num; + +extern plane_t mapplanes[MAX_MAPFILE_PLANES]; +extern int nummapplanes; +extern int mapplaneusers[MAX_MAPFILE_PLANES]; + +extern int nummapbrushes; +extern mapbrush_t mapbrushes[MAX_MAPFILE_BRUSHES]; + +extern vec3_t map_mins, map_maxs; + +extern int nummapbrushsides; +extern side_t brushsides[MAX_MAPFILE_BRUSHSIDES]; +extern brush_texture_t side_brushtextures[MAX_MAPFILE_BRUSHSIDES]; + +#ifdef ME + +typedef struct +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + int value; + char texture[64]; // texture name (textures/*.wal) + int nexttexinfo; // for animations, -1 = end of chain +} map_texinfo_t; + +extern map_texinfo_t map_texinfo[MAX_MAPFILE_TEXINFO]; +extern int map_numtexinfo; +#define NODESTACKSIZE 1024 + +#define MAPTYPE_QUAKE1 1 +#define MAPTYPE_QUAKE2 2 +#define MAPTYPE_QUAKE3 3 +#define MAPTYPE_HALFLIFE 4 +#define MAPTYPE_SIN 5 + +extern int nodestack[NODESTACKSIZE]; +extern int *nodestackptr; +extern int nodestacksize; +extern int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; +extern int dbrushleafnums[MAX_MAPFILE_BRUSHES]; +extern int dplanes2mapplanes[MAX_MAPFILE_PLANES]; + +extern int loadedmaptype; +#endif //ME + +extern int c_boxbevels; +extern int c_edgebevels; +extern int c_areaportals; +extern int c_clipbrushes; +extern int c_squattbrushes; + +//finds a float plane for the given normal and distance +int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t* points ); +//returns the plane type for the given normal +int PlaneTypeForNormal( vec3_t normal ); +//returns the plane defined by the three given points +int PlaneFromPoints( int *p0, int *p1, int *p2 ); +//add bevels to the map brush +void AddBrushBevels( mapbrush_t *b ); +//makes brush side windings for the brush +qboolean MakeBrushWindings( mapbrush_t *ob ); +//marks brush bevels of the brush as bevel +void MarkBrushBevels( mapbrush_t *brush ); +//returns true if the map brush already exists +int BrushExists( mapbrush_t *brush ); +//loads a map from a bsp file +int LoadMapFromBSP( struct quakefile_s *qf ); +//resets map loading +void ResetMapLoading( void ); +//print some map info +void PrintMapInfo( void ); +//writes a map file (type depending on loaded map type) +void WriteMapFile( char *filename ); + +//============================================================================= +// map_q3.c +//============================================================================= +void Q3_ResetMapLoading( void ); +//loads a map from a Quake3 bsp file +void Q3_LoadMapFromBSP( struct quakefile_s *qf ); + +//============================================================================= +// textures.c +//============================================================================= + +typedef struct +{ + char name[64]; + int flags; + int value; + int contents; + char animname[64]; +} textureref_t; + +#define MAX_MAP_TEXTURES 1024 + +extern textureref_t textureref[MAX_MAP_TEXTURES]; + +int FindMiptex( char *name ); +int TexinfoForBrushTexture( plane_t *plane, brush_texture_t *bt, vec3_t origin ); +void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ); + +//============================================================================= +// csg +//============================================================================= + +bspbrush_t *MakeBspBrushList( int startbrush, int endbrush, vec3_t clipmins, vec3_t clipmaxs ); +bspbrush_t *ChopBrushes( bspbrush_t *head ); +bspbrush_t *InitialBrushList( bspbrush_t *list ); +bspbrush_t *OptimizedBrushList( bspbrush_t *list ); +void WriteBrushMap( char *name, bspbrush_t *list ); +void CheckBSPBrush( bspbrush_t *brush ); +void BSPBrushWindings( bspbrush_t *brush ); +bspbrush_t *TryMergeBrushes( bspbrush_t *brush1, bspbrush_t *brush2 ); +tree_t *ProcessWorldBrushes( int brush_start, int brush_end ); + +//============================================================================= +// brushbsp +//============================================================================= + +#define PSIDE_FRONT 1 +#define PSIDE_BACK 2 +#define PSIDE_BOTH ( PSIDE_FRONT | PSIDE_BACK ) +#define PSIDE_FACING 4 + +void OpenBSPBrushMap( char *filename, int contents ); +void WriteBrushListToFile( bspbrush_t *brushlist, char *filename ); +void WriteBrushList( char *name, bspbrush_t *brush, qboolean onlyvis ); +bspbrush_t *CopyBrush( bspbrush_t *brush ); +int SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back ); +node_t *AllocNode( void ); +bspbrush_t *AllocBrush( int numsides ); +int CountBrushList( bspbrush_t *brushes ); +void FreeBrush( bspbrush_t *brushes ); +vec_t BrushVolume( bspbrush_t *brush ); +void BoundBrush( bspbrush_t *brush ); +void FreeBrushList( bspbrush_t *brushes ); +tree_t *BrushBSP( bspbrush_t *brushlist, vec3_t mins, vec3_t maxs ); + +bspbrush_t *BrushFromBounds( vec3_t mins, vec3_t maxs ); +int BrushMostlyOnSide( bspbrush_t *brush, plane_t *plane ); +qboolean WindingIsHuge( winding_t *w ); +qboolean WindingIsTiny( winding_t *w ); +void ResetBrushBSP( void ); + +//============================================================================= +// portals.c +//============================================================================= + +int VisibleContents( int contents ); +void MakeHeadnodePortals( tree_t *tree ); +void MakeNodePortal( node_t *node ); +void SplitNodePortals( node_t *node ); +qboolean Portal_VisFlood( portal_t *p ); +qboolean FloodEntities( tree_t *tree ); +void FillOutside( node_t *headnode ); +void FloodAreas( tree_t *tree ); +void MarkVisibleSides( tree_t *tree, int start, int end ); +void FreePortal( portal_t *p ); +void EmitAreaPortals( node_t *headnode ); +void MakeTreePortals( tree_t *tree ); + +//============================================================================= +// glfile.c +//============================================================================= + +void OutputWinding( winding_t *w, FILE *glview ); +void WriteGLView( tree_t *tree, char *source ); + +//============================================================================= +// gldraw.c +//============================================================================= + +extern vec3_t draw_mins, draw_maxs; +extern qboolean drawflag; + +void Draw_ClearWindow( void ); +void DrawWinding( winding_t *w ); +void GLS_BeginScene( void ); +void GLS_Winding( winding_t *w, int code ); +void GLS_EndScene( void ); + +//============================================================================= +// leakfile.c +//============================================================================= + +void LeakFile( tree_t *tree ); + +//============================================================================= +// tree.c +//============================================================================= + +tree_t *Tree_Alloc( void ); +void Tree_Free( tree_t *tree ); +void Tree_Free_r( node_t *node ); +void Tree_Print_r( node_t *node, int depth ); +void Tree_FreePortals_r( node_t *node ); +void Tree_PruneNodes_r( node_t *node ); +void Tree_PruneNodes( node_t *node ); + +//============================================================================= +// faces.c +//============================================================================= + +face_t *AllocFace( void ); +void FreeFace( face_t *f ); +void MakeFaces( node_t *headnode ); +void FixTjuncs( node_t *headnode ); +int GetEdge2( int v1, int v2, face_t *f ); +void MergeNodeFaces( node_t *node ); diff --git a/src/bspc/qfiles.h b/src/bspc/qfiles.h new file mode 100644 index 0000000..787ee64 --- /dev/null +++ b/src/bspc/qfiles.h @@ -0,0 +1,495 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +// +// qfiles.h: quake file formats +// This file must be identical in the quake and utils directories +// + +/* +======================================================================== + +The .pak files are just a linear collapse of a directory tree + +======================================================================== +*/ + +#define IDPAKHEADER ( ( 'K' << 24 ) + ( 'C' << 16 ) + ( 'A' << 8 ) + 'P' ) + +typedef struct +{ + char name[56]; + int filepos, filelen; +} dpackfile_t; + +typedef struct +{ + int ident; // == IDPAKHEADER + int dirofs; + int dirlen; +} dpackheader_t; + +#define MAX_FILES_IN_PACK 4096 + + +/* +======================================================================== + +PCX files are used for as many images as possible + +======================================================================== +*/ + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + + +/* +======================================================================== + +.MD2 triangle model file format + +======================================================================== +*/ + +#define IDALIASHEADER ( ( '2' << 24 ) + ( 'P' << 16 ) + ( 'D' << 8 ) + 'I' ) +#define ALIAS_VERSION 8 + +#define MAX_TRIANGLES 4096 +#define MAX_VERTS 2048 +#define MAX_FRAMES 512 +#define MAX_MD2SKINS 32 +#define MAX_SKINNAME 64 + +typedef struct +{ + short s; + short t; +} dstvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} dtriangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} dtrivertx_t; + +#define DTRIVERTX_V0 0 +#define DTRIVERTX_V1 1 +#define DTRIVERTX_V2 2 +#define DTRIVERTX_LNI 3 +#define DTRIVERTX_SIZE 4 + +typedef struct +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + dtrivertx_t verts[1]; // variable sized +} daliasframe_t; + + +// the glcmd format: +// a positive integer starts a tristrip command, followed by that many +// vertex structures. +// a negative integer starts a trifan command, followed by -x vertexes +// a zero indicates the end of the command list. +// a vertex consists of a floating point s, a floating point t, +// and an integer vertex index. + + +typedef struct +{ + int ident; + int version; + + int skinwidth; + int skinheight; + int framesize; // byte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + + int ofs_skins; // each skin is a MAX_SKINNAME string + int ofs_st; // byte offset from start for stverts + int ofs_tris; // offset for dtriangles + int ofs_frames; // offset for first frame + int ofs_glcmds; + int ofs_end; // end of file + +} dmdl_t; + +/* +======================================================================== + +.SP2 sprite file format + +======================================================================== +*/ + +#define IDSPRITEHEADER ( ( '2' << 24 ) + ( 'S' << 16 ) + ( 'D' << 8 ) + 'I' ) +// little-endian "IDS2" +#define SPRITE_VERSION 2 + +typedef struct +{ + int width, height; + int origin_x, origin_y; // raster coordinates inside pic + char name[MAX_SKINNAME]; // name of pcx file +} dsprframe_t; + +typedef struct { + int ident; + int version; + int numframes; + dsprframe_t frames[1]; // variable sized +} dsprite_t; + +/* +============================================================================== + + .WAL texture file format + +============================================================================== +*/ + + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[32]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored + char animname[32]; // next frame in animation chain + int flags; + int contents; + int value; +} miptex_t; + + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + +#define IDBSPHEADER ( ( 'P' << 24 ) + ( 'S' << 16 ) + ( 'B' << 8 ) + 'I' ) +// little-endian "IBSP" + +#define BSPVERSION 38 + + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define MAX_MAP_MODELS 1024 +#define MAX_MAP_BRUSHES 16384 +#define MAX_MAP_ENTITIES 4096 +#define MAX_MAP_ENTSTRING ( 128 * MAX_MAP_ENTITIES ) +#define MAX_MAP_TEXINFO 8192 + +#define MAX_MAP_AREAS 256 +#define MAX_MAP_AREAPORTALS 1024 +#define MAX_MAP_PLANES 65536 +#define MAX_MAP_NODES 65536 +#define MAX_MAP_BRUSHSIDES 65536 +#define MAX_MAP_LEAFS 65536 +#define MAX_MAP_VERTS 65536 +#define MAX_MAP_FACES 65536 +#define MAX_MAP_LEAFFACES 65536 +#define MAX_MAP_LEAFBRUSHES 65536 +#define MAX_MAP_PORTALS 65536 +#define MAX_MAP_EDGES 128000 +#define MAX_MAP_SURFEDGES 256000 +#define MAX_MAP_LIGHTING 0x320000 +#define MAX_MAP_VISIBILITY 0x280000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_VERTEXES 2 +#define LUMP_VISIBILITY 3 +#define LUMP_NODES 4 +#define LUMP_TEXINFO 5 +#define LUMP_FACES 6 +#define LUMP_LIGHTING 7 +#define LUMP_LEAFS 8 +#define LUMP_LEAFFACES 9 +#define LUMP_LEAFBRUSHES 10 +#define LUMP_EDGES 11 +#define LUMP_SURFEDGES 12 +#define LUMP_MODELS 13 +#define LUMP_BRUSHES 14 +#define LUMP_BRUSHSIDES 15 +#define LUMP_POP 16 +#define LUMP_AREAS 17 +#define LUMP_AREAPORTALS 18 +#define HEADER_LUMPS 19 + +typedef struct +{ + int ident; + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree +} dmodel_t; + + +typedef struct +{ + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +// planes (x&~1) and (x&~1)+1 are allways opposites + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// these definitions also need to be in q_shared.h! + +// lower bits are stronger, and will eat weaker brushes completely +#define CONTENTS_SOLID 1 // an eye is never valid in a solid +#define CONTENTS_WINDOW 2 // translucent, but not watery +#define CONTENTS_AUX 4 +#define CONTENTS_LAVA 8 +#define CONTENTS_SLIME 16 +#define CONTENTS_WATER 32 +#define CONTENTS_MIST 64 +#define LAST_VISIBLE_CONTENTS 64 + +// remaining contents are non-visible, and don't eat brushes + +#define CONTENTS_AREAPORTAL 0x8000 + +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 + +// currents can be added to any other contents, and may be mixed +#define CONTENTS_CURRENT_0 0x40000 +#define CONTENTS_CURRENT_90 0x80000 +#define CONTENTS_CURRENT_180 0x100000 +#define CONTENTS_CURRENT_270 0x200000 +#define CONTENTS_CURRENT_UP 0x400000 +#define CONTENTS_CURRENT_DOWN 0x800000 + +#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game +#define CONTENTS_DEADMONSTER 0x4000000 +#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs +//renamed because it's in conflict with the Q3A translucent contents +#define CONTENTS_Q2TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define CONTENTS_LADDER 0x20000000 + + + +#define SURF_LIGHT 0x1 // value will hold the light strength + +#define SURF_SLICK 0x2 // effects game physics + +#define SURF_SKY 0x4 // don't draw, but add to skybox +#define SURF_WARP 0x8 // turbulent water warp +#define SURF_TRANS33 0x10 +#define SURF_TRANS66 0x20 +#define SURF_FLOWING 0x40 // scroll towards angle +#define SURF_NODRAW 0x80 // don't bother referencing the texture + +#define SURF_HINT 0x100 // make a primary bsp splitter +#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes + +#define SURF_MONSTERSLICK 0x4000000 // slick surf that only affects ai's + + +typedef struct +{ + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + int value; // light emission, etc + char texture[32]; // texture name (textures/*.wal) + int nexttexinfo; // for animations, -1 = end of chain +} texinfo_t; + + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + unsigned short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + +typedef struct +{ + int contents; // OR of all brushes (not needed?) + + short cluster; + short area; + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; +} dleaf_t; + +typedef struct +{ + unsigned short planenum; // facing out of the leaf + short texinfo; +} dbrushside_t; + +typedef struct +{ + int firstside; + int numsides; + int contents; +} dbrush_t; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the visibility lump consists of a header with a count, then +// byte offsets for the PVS and PHS of each cluster, then the raw +// compressed bit vectors +#define DVIS_PVS 0 +#define DVIS_PHS 1 +typedef struct +{ + int numclusters; + int bitofs[8][2]; // bitofs[numclusters][2] +} dvis_t; + +// each area has a list of portals that lead into other areas +// when portals are closed, other areas may not be visible or +// hearable even if the vis info says that it should be +typedef struct +{ + int portalnum; + int otherarea; +} dareaportal_t; + +typedef struct +{ + int numareaportals; + int firstareaportal; +} darea_t; diff --git a/src/bspc/sinfiles.h b/src/bspc/sinfiles.h new file mode 100644 index 0000000..4b8c977 --- /dev/null +++ b/src/bspc/sinfiles.h @@ -0,0 +1,372 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +/* +============================================================================== + + .BSP file format + +============================================================================== +*/ + +#define SIN + +#define SINBSPVERSION 41 + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define SIN_MAX_MAP_MODELS 1024 +#define SIN_MAX_MAP_BRUSHES 8192 +#define SIN_MAX_MAP_ENTITIES 4096 +#define SIN_MAX_MAP_ENTSTRING ( 128 * SIN_MAX_MAP_ENTITIES ) +#define SIN_MAX_MAP_TEXINFO 8192 + +#define SIN_MAX_MAP_AREAS 256 +#define SIN_MAX_MAP_AREAPORTALS 1024 +#define SIN_MAX_MAP_PLANES 65536 +#define SIN_MAX_MAP_NODES 65536 +#define SIN_MAX_MAP_BRUSHSIDES 65536 +#define SIN_MAX_MAP_LEAFS 65536 +#define SIN_MAX_MAP_VERTS 65536 +#define SIN_MAX_MAP_FACES 65536 +#define SIN_MAX_MAP_LEAFFACES 65536 +#define SIN_MAX_MAP_LEAFBRUSHES 65536 +#define SIN_MAX_MAP_PORTALS 65536 +#define SIN_MAX_MAP_EDGES 128000 +#define SIN_MAX_MAP_SURFEDGES 256000 +#define SIN_MAX_MAP_LIGHTING 0x320000 +#define SIN_MAX_MAP_VISIBILITY 0x280000 + +#ifdef SIN +#define SIN_MAX_MAP_LIGHTINFO 8192 +#endif + +#ifdef SIN +#undef SIN_MAX_MAP_LIGHTING //undef the Quake2 bsp version +#define SIN_MAX_MAP_LIGHTING 0x300000 +#endif + +#ifdef SIN +#undef SIN_MAX_MAP_VISIBILITY //undef the Quake2 bsp version +#define SIN_MAX_MAP_VISIBILITY 0x280000 +#endif + +//============================================================================= + +typedef struct +{ + int fileofs, filelen; +} sin_lump_t; + +#define SIN_LUMP_ENTITIES 0 +#define SIN_LUMP_PLANES 1 +#define SIN_LUMP_VERTEXES 2 +#define SIN_LUMP_VISIBILITY 3 +#define SIN_LUMP_NODES 4 +#define SIN_LUMP_TEXINFO 5 +#define SIN_LUMP_FACES 6 +#define SIN_LUMP_LIGHTING 7 +#define SIN_LUMP_LEAFS 8 +#define SIN_LUMP_LEAFFACES 9 +#define SIN_LUMP_LEAFBRUSHES 10 +#define SIN_LUMP_EDGES 11 +#define SIN_LUMP_SURFEDGES 12 +#define SIN_LUMP_MODELS 13 +#define SIN_LUMP_BRUSHES 14 +#define SIN_LUMP_BRUSHSIDES 15 +#define SIN_LUMP_POP 16 +#define SIN_LUMP_AREAS 17 +#define SIN_LUMP_AREAPORTALS 18 + +#ifdef SIN +#define SIN_LUMP_LIGHTINFO 19 +#define SINHEADER_LUMPS 20 +#endif + +typedef struct +{ + int ident; + int version; + sin_lump_t lumps[SINHEADER_LUMPS]; +} sin_dheader_t; + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces + // without walking the bsp tree +} sin_dmodel_t; + +typedef struct +{ + float point[3]; +} sin_dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +// planes (x&~1) and (x&~1)+1 are allways opposites + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} sin_dplane_t; + + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// these definitions also need to be in q_shared.h! + +// lower bits are stronger, and will eat weaker brushes completely +#ifdef SIN +#define CONTENTS_FENCE 4 +#endif +// remaining contents are non-visible, and don't eat brushes + +#ifdef SIN +#define CONTENTS_DUMMYFENCE 0x1000 +#endif + +#ifdef SIN +#define SURF_MASKED 0x2 // surface texture is masked +#endif + +#define SURF_SKY 0x4 // don't draw, but add to skybox +#define SURF_WARP 0x8 // turbulent water warp + +#ifdef SIN +#define SURF_NONLIT 0x10 // surface is not lit +#define SURF_NOFILTER 0x20 // surface is not bi-linear filtered +#endif + +#define SURF_FLOWING 0x40 // scroll towards angle +#define SURF_NODRAW 0x80 // don't bother referencing the texture + +#define SURF_HINT 0x100 // make a primary bsp splitter +#define SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes + +#ifdef SIN +#define SURF_CONVEYOR 0x40 // surface is not lit +#endif + +#ifdef SIN +#define SURF_WAVY 0x400 // surface has waves +#define SURF_RICOCHET 0x800 // projectiles bounce literally bounce off this surface +#define SURF_PRELIT 0x1000 // surface has intensity information for pre-lighting +#define SURF_MIRROR 0x2000 // surface is a mirror +#define SURF_CONSOLE 0x4000 // surface is a console +#define SURF_USECOLOR 0x8000 // surface is lit with non-lit * color +#define SURF_HARDWAREONLY 0x10000 // surface has been damaged +#define SURF_DAMAGE 0x20000 // surface can be damaged +#define SURF_WEAK 0x40000 // surface has weak hit points +#define SURF_NORMAL 0x80000 // surface has normal hit points +#define SURF_ADD 0x100000 // surface will be additive +#define SURF_ENVMAPPED 0x200000 // surface is envmapped +#define SURF_RANDOMANIMATE 0x400000 // surface start animating on a random frame +#define SURF_ANIMATE 0x800000 // surface animates +#define SURF_RNDTIME 0x1000000 // time between animations is random +#define SURF_TRANSLATE 0x2000000 // surface translates +#define SURF_NOMERGE 0x4000000 // surface is not merged in csg phase +#define SURF_TYPE_BIT0 0x8000000 // 0 bit of surface type +#define SURF_TYPE_BIT1 0x10000000 // 1 bit of surface type +#define SURF_TYPE_BIT2 0x20000000 // 2 bit of surface type +#define SURF_TYPE_BIT3 0x40000000 // 3 bit of surface type + +#define SURF_START_BIT 27 +#define SURFACETYPE_FROM_FLAGS( x ) ( ( x >> ( SURF_START_BIT ) ) & 0xf ) + + +#define SURF_TYPE_SHIFT( x ) ( ( x ) << ( SURF_START_BIT ) ) // macro for getting proper bit mask + +#define SURF_TYPE_NONE SURF_TYPE_SHIFT( 0 ) +#define SURF_TYPE_WOOD SURF_TYPE_SHIFT( 1 ) +#define SURF_TYPE_METAL SURF_TYPE_SHIFT( 2 ) +#define SURF_TYPE_STONE SURF_TYPE_SHIFT( 3 ) +#define SURF_TYPE_CONCRETE SURF_TYPE_SHIFT( 4 ) +#define SURF_TYPE_DIRT SURF_TYPE_SHIFT( 5 ) +#define SURF_TYPE_FLESH SURF_TYPE_SHIFT( 6 ) +#define SURF_TYPE_GRILL SURF_TYPE_SHIFT( 7 ) +#define SURF_TYPE_GLASS SURF_TYPE_SHIFT( 8 ) +#define SURF_TYPE_FABRIC SURF_TYPE_SHIFT( 9 ) +#define SURF_TYPE_MONITOR SURF_TYPE_SHIFT( 10 ) +#define SURF_TYPE_GRAVEL SURF_TYPE_SHIFT( 11 ) +#define SURF_TYPE_VEGETATION SURF_TYPE_SHIFT( 12 ) +#define SURF_TYPE_PAPER SURF_TYPE_SHIFT( 13 ) +#define SURF_TYPE_DUCT SURF_TYPE_SHIFT( 14 ) +#define SURF_TYPE_WATER SURF_TYPE_SHIFT( 15 ) +#endif + + +typedef struct +{ + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} sin_dnode_t; + +#ifdef SIN + +typedef struct sin_lightvalue_s +{ + int value; // light emission, etc + vec3_t color; + float direct; + float directangle; + float directstyle; + char directstylename[32]; +} sin_lightvalue_t; + +typedef struct sin_texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int flags; // miptex flags + overrides + char texture[64]; // texture name (textures/*.wal) + int nexttexinfo; // for animations, -1 = end of chain + float trans_mag; + int trans_angle; + int base_angle; + float animtime; + float nonlit; + float translucence; + float friction; + float restitution; + vec3_t color; + char groupname[32]; +} sin_texinfo_t; + +#endif //SIN + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} sin_dedge_t; + +#ifdef MAXLIGHTMAPS +#undef MAXLIGHTMAPS +#endif +#define MAXLIGHTMAPS 16 +typedef struct +{ + unsigned short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +#ifdef SIN + int lightinfo; +#endif +} sin_dface_t; + +typedef struct +{ + int contents; // OR of all brushes (not needed?) + + short cluster; + short area; + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; +} sin_dleaf_t; + +typedef struct +{ + unsigned short planenum; // facing out of the leaf + short texinfo; +#ifdef SIN + int lightinfo; +#endif +} sin_dbrushside_t; + +typedef struct +{ + int firstside; + int numsides; + int contents; +} sin_dbrush_t; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the visibility lump consists of a header with a count, then +// byte offsets for the PVS and PHS of each cluster, then the raw +// compressed bit vectors +#define DVIS_PVS 0 +#define DVIS_PHS 1 +typedef struct +{ + int numclusters; + int bitofs[8][2]; // bitofs[numclusters][2] +} sin_dvis_t; + +// each area has a list of portals that lead into other areas +// when portals are closed, other areas may not be visible or +// hearable even if the vis info says that it should be +typedef struct +{ + int portalnum; + int otherarea; +} sin_dareaportal_t; + +typedef struct +{ + int numareaportals; + int firstareaportal; +} sin_darea_t; diff --git a/src/bspc/textures.c b/src/bspc/textures.c new file mode 100644 index 0000000..b33cc9f --- /dev/null +++ b/src/bspc/textures.c @@ -0,0 +1,249 @@ +/* +=========================================================================== + +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: textures.c +// Function: textures +// Programmer: Mr Elusive (MrElusive@worldentity.com) +// Last update: 1999-08-10 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" +#include "l_bsp_q2.h" + + +int nummiptex; +textureref_t textureref[MAX_MAP_TEXTURES]; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int FindMiptex( char *name ) { + int i; + char path[1024]; + miptex_t *mt; + + for ( i = 0; i < nummiptex; i++ ) + { + if ( !strcmp( name, textureref[i].name ) ) { + return i; + } //end if + } //end for + if ( nummiptex == MAX_MAP_TEXTURES ) { + Error( "MAX_MAP_TEXTURES" ); + } + strcpy( textureref[i].name, name ); + + // load the miptex to get the flags and values + sprintf( path, "%stextures/%s.wal", gamedir, name ); + if ( TryLoadFile( path, (void **)&mt ) != -1 ) { + textureref[i].value = LittleLong( mt->value ); + textureref[i].flags = LittleLong( mt->flags ); + textureref[i].contents = LittleLong( mt->contents ); + strcpy( textureref[i].animname, mt->animname ); + FreeMemory( mt ); + } //end if + nummiptex++; + + if ( textureref[i].animname[0] ) { + FindMiptex( textureref[i].animname ); + } + + return i; +} //end of the function FindMipTex +//=========================================================================== +//textureAxisFromPlane +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +vec3_t baseaxis[18] = +{ + {0,0,1}, {1,0,0}, {0,-1,0}, // floor + {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling + {1,0,0}, {0,1,0}, {0,0,-1}, // west wall + {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall + {0,1,0}, {1,0,0}, {0,0,-1}, // south wall + {0,-1,0}, {1,0,0}, {0,0,-1} // north wall +}; + +void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ) { + int bestaxis; + vec_t dot,best; + int i; + + best = 0; + bestaxis = 0; + + for ( i = 0 ; i < 6 ; i++ ) + { + dot = DotProduct( pln->normal, baseaxis[i * 3] ); + if ( dot > best ) { + best = dot; + bestaxis = i; + } + } + + VectorCopy( baseaxis[bestaxis * 3 + 1], xv ); + VectorCopy( baseaxis[bestaxis * 3 + 2], yv ); +} //end of the function TextureAxisFromPlane +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int TexinfoForBrushTexture( plane_t *plane, brush_texture_t *bt, vec3_t origin ) { + vec3_t vecs[2]; + int sv, tv; + vec_t ang, sinv, cosv; + vec_t ns, nt; + texinfo_t tx, *tc; + int i, j, k; + float shift[2]; + brush_texture_t anim; + int mt; + + if ( !bt->name[0] ) { + return 0; + } + + memset( &tx, 0, sizeof( tx ) ); + strcpy( tx.texture, bt->name ); + + TextureAxisFromPlane( plane, vecs[0], vecs[1] ); + + shift[0] = DotProduct( origin, vecs[0] ); + shift[1] = DotProduct( origin, vecs[1] ); + + if ( !bt->scale[0] ) { + bt->scale[0] = 1; + } + if ( !bt->scale[1] ) { + bt->scale[1] = 1; + } + + +// rotate axis + if ( bt->rotate == 0 ) { + sinv = 0 ; cosv = 1; + } else if ( bt->rotate == 90 ) { + sinv = 1 ; cosv = 0; + } else if ( bt->rotate == 180 ) { + sinv = 0 ; cosv = -1; + } else if ( bt->rotate == 270 ) { + sinv = -1 ; cosv = 0; + } else + { + ang = bt->rotate / 180 * Q_PI; + sinv = sin( ang ); + cosv = cos( ang ); + } + + if ( vecs[0][0] ) { + sv = 0; + } else if ( vecs[0][1] ) { + sv = 1; + } else { + sv = 2; + } + + if ( vecs[1][0] ) { + tv = 0; + } else if ( vecs[1][1] ) { + tv = 1; + } else { + tv = 2; + } + + for ( i = 0 ; i < 2 ; i++ ) + { + ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; + nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; + vecs[i][sv] = ns; + vecs[i][tv] = nt; + } + + for ( i = 0 ; i < 2 ; i++ ) + for ( j = 0 ; j < 3 ; j++ ) + tx.vecs[i][j] = vecs[i][j] / bt->scale[i]; + + tx.vecs[0][3] = bt->shift[0] + shift[0]; + tx.vecs[1][3] = bt->shift[1] + shift[1]; + tx.flags = bt->flags; + tx.value = bt->value; + + // + // find the texinfo + // + tc = texinfo; + for ( i = 0 ; i < numtexinfo ; i++, tc++ ) + { + if ( tc->flags != tx.flags ) { + continue; + } + if ( tc->value != tx.value ) { + continue; + } + for ( j = 0 ; j < 2 ; j++ ) + { + if ( strcmp( tc->texture, tx.texture ) ) { + goto skip; + } + for ( k = 0 ; k < 4 ; k++ ) + { + if ( tc->vecs[j][k] != tx.vecs[j][k] ) { + goto skip; + } + } + } + return i; +skip:; + } + *tc = tx; + numtexinfo++; + + // load the next animation + mt = FindMiptex( bt->name ); + if ( textureref[mt].animname[0] ) { + anim = *bt; + strcpy( anim.name, textureref[mt].animname ); + tc->nexttexinfo = TexinfoForBrushTexture( plane, &anim, origin ); + } else { + tc->nexttexinfo = -1; + } + + + return i; +} //end of the function TexinfoForBrushTexture diff --git a/src/bspc/tree.c b/src/bspc/tree.c new file mode 100644 index 0000000..e481113 --- /dev/null +++ b/src/bspc/tree.c @@ -0,0 +1,298 @@ +/* +=========================================================================== + +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: textures.c +// Function: textures +// Programmer: Mr Elusive (MrElusive@worldentity.com) +// Last update: 1999-08-10 +// Tab Size: 3 +//=========================================================================== + +#include "qbsp.h" + +extern int c_nodes; +int c_pruned; +int freedtreemem = 0; + +void RemovePortalFromNode( portal_t *portal, node_t *l ); + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +node_t *NodeForPoint( node_t *node, vec3_t origin ) { + plane_t *plane; + vec_t d; + + while ( node->planenum != PLANENUM_LEAF ) + { + plane = &mapplanes[node->planenum]; + d = DotProduct( origin, plane->normal ) - plane->dist; + if ( d >= 0 ) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + return node; +} //end of the function NodeForPoint +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_FreePortals_r( node_t *node ) { + portal_t *p, *nextp; + int s; + + // free children + if ( node->planenum != PLANENUM_LEAF ) { + Tree_FreePortals_r( node->children[0] ); + Tree_FreePortals_r( node->children[1] ); + } + + // free portals + for ( p = node->portals; p; p = nextp ) + { + s = ( p->nodes[1] == node ); + nextp = p->next[s]; + + RemovePortalFromNode( p, p->nodes[!s] ); +#ifdef ME + if ( p->winding ) { + freedtreemem += MemorySize( p->winding ); + } + freedtreemem += MemorySize( p ); +#endif //ME + FreePortal( p ); + } + node->portals = NULL; +} //end of the function Tree_FreePortals_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_Free_r( node_t *node ) { +// face_t *f, *nextf; + bspbrush_t *brush, *nextbrush; + + //free children + if ( node->planenum != PLANENUM_LEAF ) { + Tree_Free_r( node->children[0] ); + Tree_Free_r( node->children[1] ); + } //end if + //free bspbrushes +// FreeBrushList (node->brushlist); + for ( brush = node->brushlist; brush; brush = nextbrush ) + { + nextbrush = brush->next; +#ifdef ME + freedtreemem += MemorySize( brush ); +#endif //ME + FreeBrush( brush ); + } //end for + node->brushlist = NULL; + + /* + NOTE: only used when creating Q2 bsp + // free faces + for (f = node->faces; f; f = nextf) + { + nextf = f->next; +#ifdef ME + if (f->w) freedtreemem += MemorySize(f->w); + freedtreemem += sizeof(face_t); +#endif //ME + FreeFace(f); + } //end for + */ + + // free the node + if ( node->volume ) { +#ifdef ME + freedtreemem += MemorySize( node->volume ); +#endif //ME + FreeBrush( node->volume ); + } //end if + + if ( numthreads == 1 ) { + c_nodes--; + } +#ifdef ME + freedtreemem += MemorySize( node ); +#endif //ME + FreeMemory( node ); +} //end of the function Tree_Free_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_Free( tree_t *tree ) { + //if no tree just return + if ( !tree ) { + return; + } + // + freedtreemem = 0; + // + Tree_FreePortals_r( tree->headnode ); + Tree_Free_r( tree->headnode ); +#ifdef ME + freedtreemem += MemorySize( tree ); +#endif //ME + FreeMemory( tree ); +#ifdef ME + Log_Print( "freed " ); + PrintMemorySize( freedtreemem ); + Log_Print( " of tree memory\n" ); +#endif //ME +} //end of the function Tree_Free +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +tree_t *Tree_Alloc( void ) { + tree_t *tree; + + tree = GetMemory( sizeof( *tree ) ); + memset( tree, 0, sizeof( *tree ) ); + ClearBounds( tree->mins, tree->maxs ); + + return tree; +} //end of the function Tree_Alloc +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_Print_r( node_t *node, int depth ) { + int i; + plane_t *plane; + bspbrush_t *bb; + + for ( i = 0 ; i < depth ; i++ ) + printf( " " ); + if ( node->planenum == PLANENUM_LEAF ) { + if ( !node->brushlist ) { + printf( "NULL\n" ); + } else + { + for ( bb = node->brushlist ; bb ; bb = bb->next ) + printf( "%i ", bb->original->brushnum ); + printf( "\n" ); + } + return; + } + + plane = &mapplanes[node->planenum]; + printf( "#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum, + plane->normal[0], plane->normal[1], plane->normal[2], + plane->dist ); + Tree_Print_r( node->children[0], depth + 1 ); + Tree_Print_r( node->children[1], depth + 1 ); +} //end of the function Tree_Print_r +//=========================================================================== +// NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_PruneNodes_r( node_t *node ) { + bspbrush_t *b, *next; + + if ( node->planenum == PLANENUM_LEAF ) { + return; + } + + Tree_PruneNodes_r( node->children[0] ); + Tree_PruneNodes_r( node->children[1] ); + + if ( create_aas ) { + if ( ( node->children[0]->contents & CONTENTS_LADDER ) || + ( node->children[1]->contents & CONTENTS_LADDER ) ) { + return; + } + } + + if ( ( node->children[0]->contents & CONTENTS_SOLID ) + && ( node->children[1]->contents & CONTENTS_SOLID ) ) { + if ( node->faces ) { + Error( "node->faces seperating CONTENTS_SOLID" ); + } + if ( node->children[0]->faces || node->children[1]->faces ) { + Error( "!node->faces with children" ); + } + // FIXME: free stuff + node->planenum = PLANENUM_LEAF; + node->contents = CONTENTS_SOLID; + node->detail_seperator = false; + + if ( node->brushlist ) { + Error( "PruneNodes: node->brushlist" ); + } + // combine brush lists + node->brushlist = node->children[1]->brushlist; + + for ( b = node->children[0]->brushlist; b; b = next ) + { + next = b->next; + b->next = node->brushlist; + node->brushlist = b; + } //end for + //free the child nodes + FreeMemory( node->children[0] ); + FreeMemory( node->children[1] ); + //two nodes are cut away + c_pruned += 2; + } //end if +} //end of the function Tree_PruneNodes_r +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Tree_PruneNodes( node_t *node ) { + Log_Print( "------- Prune Nodes --------\n" ); + c_pruned = 0; + Tree_PruneNodes_r( node ); + Log_Print( "%5i pruned nodes\n", c_pruned ); +} //end of the function Tree_PruneNodes diff --git a/src/bspc/writebsp.c b/src/bspc/writebsp.c new file mode 100644 index 0000000..ea69a6d --- /dev/null +++ b/src/bspc/writebsp.c @@ -0,0 +1,632 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// NO LONGER IN PROJECT +#if 0 +#include "qbsp.h" + +int c_nofaces; +int c_facenodes; + + +extern int numplanes; +extern int numfaces; +extern int numleaffaces; +extern int numleafs; +extern int numleafbrushes; +extern int numsurfedges; +extern int numnodes; + +extern int nummodels; +extern int numbrushsides; +extern int numbrushes; +extern int numvertexes; +extern int numedges; + +extern dplane_t dplanes[MAX_MAP_PLANES]; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; +extern dface_t dfaces[MAX_MAP_FACES]; +extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; +extern unsigned short dleaffaces[MAX_MAP_LEAFFACES]; +extern int dsurfedges[MAX_MAP_SURFEDGES]; +extern dnode_t dnodes[MAX_MAP_NODES]; +extern dmodel_t dmodels[MAX_MAP_MODELS]; +extern dbrush_t dbrushes[MAX_MAP_BRUSHES]; +extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; + +/* +========================================================= + +ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES + +========================================================= +*/ + +int planeused[MAX_MAP_PLANES]; + +/* +============ +EmitPlanes + +There is no oportunity to discard planes, because all of the original +brushes will be saved in the map. +============ +*/ +void EmitPlanes( void ) { + int i; + dplane_t *dp; + plane_t *mp; + //ME: this causes a crash?? +// int planetranslate[MAX_MAP_PLANES]; + + mp = mapplanes; + for ( i = 0 ; i < nummapplanes ; i++, mp++ ) + { + dp = &dplanes[numplanes]; +// planetranslate[i] = numplanes; + VectorCopy( mp->normal, dp->normal ); + dp->dist = mp->dist; + dp->type = mp->type; + numplanes++; + if ( numplanes >= MAX_MAP_PLANES ) { + Error( "MAX_MAP_PLANES" ); + } + } +} + + +//======================================================== + +void EmitMarkFace( dleaf_t *leaf_p, face_t *f ) { + int i; + int facenum; + + while ( f->merged ) + f = f->merged; + + if ( f->split[0] ) { + EmitMarkFace( leaf_p, f->split[0] ); + EmitMarkFace( leaf_p, f->split[1] ); + return; + } + + facenum = f->outputnumber; + if ( facenum == -1 ) { + return; // degenerate face + + } + if ( facenum < 0 || facenum >= numfaces ) { + Error( "Bad leafface" ); + } + for ( i = leaf_p->firstleafface ; i < numleaffaces ; i++ ) + if ( dleaffaces[i] == facenum ) { + break; + } // merged out face + if ( i == numleaffaces ) { + if ( numleaffaces >= MAX_MAP_LEAFFACES ) { + Error( "MAX_MAP_LEAFFACES" ); + } + + dleaffaces[numleaffaces] = facenum; + numleaffaces++; + } + +} + + +/* +================== +EmitLeaf +================== +*/ +void EmitLeaf( node_t *node ) { + dleaf_t *leaf_p; + portal_t *p; + int s; + face_t *f; + bspbrush_t *b; + int i; + int brushnum; + + // emit a leaf + if ( numleafs >= MAX_MAP_LEAFS ) { + Error( "MAX_MAP_LEAFS" ); + } + + leaf_p = &dleafs[numleafs]; + numleafs++; + + leaf_p->contents = node->contents; + leaf_p->cluster = node->cluster; + leaf_p->area = node->area; + + // + // write bounding box info + // + VectorCopy( node->mins, leaf_p->mins ); + VectorCopy( node->maxs, leaf_p->maxs ); + + // + // write the leafbrushes + // + leaf_p->firstleafbrush = numleafbrushes; + for ( b = node->brushlist ; b ; b = b->next ) + { + if ( numleafbrushes >= MAX_MAP_LEAFBRUSHES ) { + Error( "MAX_MAP_LEAFBRUSHES" ); + } + + brushnum = b->original - mapbrushes; + for ( i = leaf_p->firstleafbrush ; i < numleafbrushes ; i++ ) + if ( dleafbrushes[i] == brushnum ) { + break; + } + if ( i == numleafbrushes ) { + dleafbrushes[numleafbrushes] = brushnum; + numleafbrushes++; + } + } + leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush; + + // + // write the leaffaces + // + if ( leaf_p->contents & CONTENTS_SOLID ) { + return; // no leaffaces in solids + + } + leaf_p->firstleafface = numleaffaces; + + for ( p = node->portals ; p ; p = p->next[s] ) + { + s = ( p->nodes[1] == node ); + f = p->face[s]; + if ( !f ) { + continue; // not a visible portal + + } + EmitMarkFace( leaf_p, f ); + } + + leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface; +} + + +/* +================== +EmitFace +================== +*/ +void EmitFace( face_t *f ) { + dface_t *df; + int i; + int e; + + f->outputnumber = -1; + + if ( f->numpoints < 3 ) { + return; // degenerated + } + if ( f->merged || f->split[0] || f->split[1] ) { + return; // not a final face + } + + // save output number so leaffaces can use + f->outputnumber = numfaces; + + if ( numfaces >= MAX_MAP_FACES ) { + Error( "numfaces == MAX_MAP_FACES" ); + } + df = &dfaces[numfaces]; + numfaces++; + + // planenum is used by qlight, but not quake + df->planenum = f->planenum & ( ~1 ); + df->side = f->planenum & 1; + + df->firstedge = numsurfedges; + df->numedges = f->numpoints; + df->texinfo = f->texinfo; + for ( i = 0 ; i < f->numpoints ; i++ ) + { +// e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f); + e = GetEdge2( f->vertexnums[i], f->vertexnums[( i + 1 ) % f->numpoints], f ); + if ( numsurfedges >= MAX_MAP_SURFEDGES ) { + Error( "numsurfedges == MAX_MAP_SURFEDGES" ); + } + dsurfedges[numsurfedges] = e; + numsurfedges++; + } +} + +/* +============ +EmitDrawingNode_r +============ +*/ +int EmitDrawNode_r( node_t *node ) { + dnode_t *n; + face_t *f; + int i; + + if ( node->planenum == PLANENUM_LEAF ) { + EmitLeaf( node ); + return -numleafs; + } + + // emit a node + if ( numnodes == MAX_MAP_NODES ) { + Error( "MAX_MAP_NODES" ); + } + n = &dnodes[numnodes]; + numnodes++; + + VectorCopy( node->mins, n->mins ); + VectorCopy( node->maxs, n->maxs ); + + planeused[node->planenum]++; + planeused[node->planenum ^ 1]++; + + if ( node->planenum & 1 ) { + Error( "WriteDrawNodes_r: odd planenum" ); + } + n->planenum = node->planenum; + n->firstface = numfaces; + + if ( !node->faces ) { + c_nofaces++; + } else { + c_facenodes++; + } + + for ( f = node->faces ; f ; f = f->next ) + EmitFace( f ); + + n->numfaces = numfaces - n->firstface; + + + // + // recursively output the other nodes + // + for ( i = 0 ; i < 2 ; i++ ) + { + if ( node->children[i]->planenum == PLANENUM_LEAF ) { + n->children[i] = -( numleafs + 1 ); + EmitLeaf( node->children[i] ); + } else + { + n->children[i] = numnodes; + EmitDrawNode_r( node->children[i] ); + } + } + + return n - dnodes; +} + +//========================================================= + + +/* +============ +WriteBSP +============ +*/ +void WriteBSP( node_t *headnode ) { + int oldfaces; + + c_nofaces = 0; + c_facenodes = 0; + + qprintf( "--- WriteBSP ---\n" ); + + oldfaces = numfaces; + dmodels[nummodels].headnode = EmitDrawNode_r( headnode ); +// EmitAreaPortals (headnode); + + qprintf( "%5i nodes with faces\n", c_facenodes ); + qprintf( "%5i nodes without faces\n", c_nofaces ); + qprintf( "%5i faces\n", numfaces - oldfaces ); +} + +//=========================================================== + +/* +============ +SetModelNumbers +============ +*/ +void SetModelNumbers( void ) { + int i; + int models; + char value[10]; + + models = 1; + for ( i = 1 ; i < num_entities ; i++ ) + { + if ( entities[i].numbrushes ) { + sprintf( value, "*%i", models ); + models++; + SetKeyValue( &entities[i], "model", value ); + } + } + +} + +/* +============ +SetLightStyles +============ +*/ +#define MAX_SWITCHED_LIGHTS 32 +void SetLightStyles( void ) { + int stylenum; + char *t; + entity_t *e; + int i, j; + char value[10]; + char lighttargets[MAX_SWITCHED_LIGHTS][64]; + + + // any light that is controlled (has a targetname) + // must have a unique style number generated for it + + stylenum = 0; + for ( i = 1 ; i < num_entities ; i++ ) + { + e = &entities[i]; + + t = ValueForKey( e, "classname" ); + if ( Q_strncasecmp( t, "light", 5 ) ) { + continue; + } + t = ValueForKey( e, "targetname" ); + if ( !t[0] ) { + continue; + } + + // find this targetname + for ( j = 0 ; j < stylenum ; j++ ) + if ( !strcmp( lighttargets[j], t ) ) { + break; + } + if ( j == stylenum ) { + if ( stylenum == MAX_SWITCHED_LIGHTS ) { + Error( "stylenum == MAX_SWITCHED_LIGHTS" ); + } + strcpy( lighttargets[j], t ); + stylenum++; + } + sprintf( value, "%i", 32 + j ); + SetKeyValue( e, "style", value ); + } + +} + +//=========================================================== + +/* +============ +EmitBrushes +============ +*/ +void EmitBrushes( void ) { + int i, j, bnum, s, x; + dbrush_t *db; + mapbrush_t *b; + dbrushside_t *cp; + vec3_t normal; + vec_t dist; + int planenum; + + numbrushsides = 0; + numbrushes = nummapbrushes; + + for ( bnum = 0 ; bnum < nummapbrushes ; bnum++ ) + { + b = &mapbrushes[bnum]; + db = &dbrushes[bnum]; + + db->contents = b->contents; + db->firstside = numbrushsides; + db->numsides = b->numsides; + for ( j = 0 ; j < b->numsides ; j++ ) + { + if ( numbrushsides == MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + cp = &dbrushsides[numbrushsides]; + numbrushsides++; + cp->planenum = b->original_sides[j].planenum; + cp->texinfo = b->original_sides[j].texinfo; + } + +#ifdef ME + //for collision detection, bounding boxes are axial :) + //brushes are convex so just add dot or line touching planes on the sides of + //the brush parallell to the axis planes +#endif + // add any axis planes not contained in the brush to bevel off corners + for ( x = 0 ; x < 3 ; x++ ) + for ( s = -1 ; s <= 1 ; s += 2 ) + { + // add the plane + VectorCopy( vec3_origin, normal ); + normal[x] = s; + if ( s == -1 ) { + dist = -b->mins[x]; + } else { + dist = b->maxs[x]; + } + planenum = FindFloatPlane( normal, dist, 0, NULL ); + for ( i = 0 ; i < b->numsides ; i++ ) + if ( b->original_sides[i].planenum == planenum ) { + break; + } + if ( i == b->numsides ) { + if ( numbrushsides >= MAX_MAP_BRUSHSIDES ) { + Error( "MAX_MAP_BRUSHSIDES" ); + } + + dbrushsides[numbrushsides].planenum = planenum; + dbrushsides[numbrushsides].texinfo = + dbrushsides[numbrushsides - 1].texinfo; + numbrushsides++; + db->numsides++; + } + } + + } + +} + +//=========================================================== + +/* +================== +BeginBSPFile +================== +*/ +void BeginBSPFile( void ) { + // these values may actually be initialized + // if the file existed when loaded, so clear them explicitly + nummodels = 0; + numfaces = 0; + numnodes = 0; + numbrushsides = 0; + numvertexes = 0; + numleaffaces = 0; + numleafbrushes = 0; + numsurfedges = 0; + + // edge 0 is not used, because 0 can't be negated + numedges = 1; + + // leave vertex 0 as an error + numvertexes = 1; + + // leave leaf 0 as an error + numleafs = 1; + dleafs[0].contents = CONTENTS_SOLID; +} + + +/* +============ +EndBSPFile +============ +*/ +void EndBSPFile( void ) { +#if 0 + char path[1024]; + int len; + byte *buf; +#endif + + + EmitBrushes(); + EmitPlanes(); + Q2_UnparseEntities(); + + // load the pop +#if 0 + sprintf( path, "%s/pics/pop.lmp", gamedir ); + len = LoadFile( path, &buf ); + memcpy( dpop, buf, sizeof( dpop ) ); + FreeMemory( buf ); +#endif +} + + +/* +================== +BeginModel +================== +*/ +int firstmodleaf; +extern int firstmodeledge; +extern int firstmodelface; +void BeginModel( void ) { + dmodel_t *mod; + int start, end; + mapbrush_t *b; + int j; + entity_t *e; + vec3_t mins, maxs; + + if ( nummodels == MAX_MAP_MODELS ) { + Error( "MAX_MAP_MODELS" ); + } + mod = &dmodels[nummodels]; + + mod->firstface = numfaces; + + firstmodleaf = numleafs; + firstmodeledge = numedges; + firstmodelface = numfaces; + + // + // bound the brushes + // + e = &entities[entity_num]; + + start = e->firstbrush; + end = start + e->numbrushes; + ClearBounds( mins, maxs ); + + for ( j = start ; j < end ; j++ ) + { + b = &mapbrushes[j]; + if ( !b->numsides ) { + continue; // not a real brush (origin brush) + } + AddPointToBounds( b->mins, mins, maxs ); + AddPointToBounds( b->maxs, mins, maxs ); + } + + VectorCopy( mins, mod->mins ); + VectorCopy( maxs, mod->maxs ); +} + + +/* +================== +EndModel +================== +*/ +void EndModel( void ) { + dmodel_t *mod; + + mod = &dmodels[nummodels]; + + mod->numfaces = numfaces - mod->firstface; + + nummodels++; +} + +#endif diff --git a/src/cgame/cg_atmospheric.c b/src/cgame/cg_atmospheric.c new file mode 100644 index 0000000..b314637 --- /dev/null +++ b/src/cgame/cg_atmospheric.c @@ -0,0 +1,952 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +/* +** +** cg_atmospheric.c +** +** Add atmospheric effects (e.g. rain, snow etc.) to view. +** +** Current supported effects are rain and snow. +** +*/ + +#define ATM_NEW + +#include "cg_local.h" + +#define MAX_ATMOSPHERIC_HEIGHT MAX_MAP_SIZE // maximum world height +#define MIN_ATMOSPHERIC_HEIGHT -MAX_MAP_SIZE // minimum world height + +//int getgroundtime, getskytime, rendertime, checkvisibletime, generatetime; +//int n_getgroundtime, n_getskytime, n_rendertime, n_checkvisibletime, n_generatetime; + +//static qboolean CG_LoadTraceMap( void ); + +#define MAX_ATMOSPHERIC_PARTICLES 4000 // maximum # of particles +#define MAX_ATMOSPHERIC_DISTANCE 1000 // maximum distance from refdef origin that particles are visible +#define MAX_ATMOSPHERIC_EFFECTSHADERS 6 // maximum different effectshaders for an atmospheric effect +#define ATMOSPHERIC_DROPDELAY 1000 +#define ATMOSPHERIC_CUTHEIGHT 800 + +#define ATMOSPHERIC_RAIN_SPEED ( 1.1f * DEFAULT_GRAVITY ) +#define ATMOSPHERIC_RAIN_HEIGHT 150 + +#define ATMOSPHERIC_SNOW_SPEED ( 0.1f * DEFAULT_GRAVITY ) +#define ATMOSPHERIC_SNOW_HEIGHT 3 + +typedef enum { + ATM_NONE, + ATM_RAIN, + ATM_SNOW +} atmFXType_t; + +#ifndef ATM_NEW +/* +** Atmospheric Particles PolyPool +*/ +static polyVert_t atmPolyPool[MAX_ATMOSPHERIC_PARTICLES * 3]; +static int numParticlesInFrame; +static qhandle_t atmPolyShader; + +static void CG_ClearPolyPool( void ) { + numParticlesInFrame = 0; + atmPolyShader = 0; +} + +static void CG_RenderPolyPool( void ) { + if ( numParticlesInFrame ) { + trap_R_AddPolysToScene( atmPolyShader, 3, atmPolyPool, numParticlesInFrame ); + CG_ClearPolyPool(); + } +} +#endif // ATM_NEW + +static void CG_AddPolyToPool( qhandle_t shader, const polyVert_t *verts ) { +#ifndef ATM_NEW + if ( atmPolyShader && atmPolyShader != shader ) { + CG_RenderPolyPool(); + } + + if ( numParticlesInFrame == MAX_ATMOSPHERIC_PARTICLES ) { + CG_RenderPolyPool(); + } + + atmPolyShader = shader; + memcpy( &atmPolyPool[numParticlesInFrame * 3], verts, 3 * sizeof( polyVert_t ) ); + numParticlesInFrame++; +#else + int firstIndex; + int firstVertex; + int i; + + polyBuffer_t* pPolyBuffer = CG_PB_FindFreePolyBuffer( shader, 3, 3 ); + if ( !pPolyBuffer ) { + return; + } + + firstIndex = pPolyBuffer->numIndicies; + firstVertex = pPolyBuffer->numVerts; + + for ( i = 0; i < 3; i++ ) { + VectorCopy( verts[i].xyz, pPolyBuffer->xyz[firstVertex + i] ); + + pPolyBuffer->st[firstVertex + i][0] = verts[i].st[0]; + pPolyBuffer->st[firstVertex + i][1] = verts[i].st[1]; + pPolyBuffer->color[firstVertex + i][0] = verts[i].modulate[0]; + pPolyBuffer->color[firstVertex + i][1] = verts[i].modulate[1]; + pPolyBuffer->color[firstVertex + i][2] = verts[i].modulate[2]; + pPolyBuffer->color[firstVertex + i][3] = verts[i].modulate[3]; + + pPolyBuffer->indicies[firstIndex + i] = firstVertex + i; + + } + + pPolyBuffer->numIndicies += 3; + pPolyBuffer->numVerts += 3; +#endif // ATM_NEW +} + +/* +** CG_AtmosphericKludge +*/ + +static qboolean kludgeChecked, kludgeResult; +qboolean CG_AtmosphericKludge() { + // Activate rain for specified kludge maps that don't + // have it specified for them. + + if ( kludgeChecked ) { + return( kludgeResult ); + } + kludgeChecked = qtrue; + kludgeResult = qfalse; + + /*if( !Q_stricmp( cgs.mapname, "maps/trainyard.bsp" ) ) + { + //CG_EffectParse( "T=RAIN,B=5 10,C=0.5 2,G=0.5 2,BV=30 100,GV=20 80,W=1 2,D=1000 1000" ); + CG_EffectParse( "T=RAIN,B=5 10,C=0.5,G=0.5 2,BV=50 50,GV=200 200,W=1 2,D=1000" ); + return( kludgeResult = qtrue ); + }*/ +/* if( !Q_stricmp( cgs.mapname, "maps/mp_railgun.bsp" ) ) + { + //CG_EffectParse( "T=RAIN,B=5 10,C=0.5 2,G=0.5 2,BV=30 100,GV=20 80,W=1 2,D=1000 1000" ); +// CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=50 50,GV=30 80,W=1 2,D=5000" ); + + // snow storm, quite horizontally + //CG_EffectParse( "T=SNOW,B=20 30,C=0.8,G=0.5 8,BV=100 100,GV=70 150,W=3 5,D=5000" ); + + // mild snow storm, quite vertically - likely go for this + //CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,D=5000" ); + CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,D=2000" ); + + // cpu-cheap press event effect + //CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,D=500" ); +// CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,D=750" ); + return( kludgeResult = qtrue ); + }*/ + + /*if( !Q_stricmp( cgs.mapname, "maps/mp_goliath.bsp" ) ) { + //CG_EffectParse( "T=SNOW,B=5 7,C=0.2,G=0.1 5,BV=15 25,GV=25 40,W=3 5,D=400" ); + CG_EffectParse( "T=SNOW,B=5 7,C=0.2,G=0.1 5,BV=15 25,GV=25 40,W=3 5,H=512,D=2000" ); + return( kludgeResult = qtrue ); + }*/ + /*if( !Q_stricmp( cgs.rawmapname, "sp_bruck_test006" ) ) { + //T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,H=608,D=2000 + CG_EffectParse( "T=SNOW,B=5 10,C=0.5,G=0.3 2,BV=20 30,GV=25 40,W=3 5,H=512,D=2000 4000" ); + //CG_EffectParse( "T=SNOW,B=5 7,C=0.2,G=0.1 5,BV=15 25,GV=25 40,W=3 5,H=512,D=2000" ); + return( kludgeResult = qtrue ); + }*/ + + return( kludgeResult = qfalse ); +} + +typedef enum { + ACT_NOT, + ACT_FALLING +} active_t; + +typedef struct cg_atmosphericParticle_s { + vec3_t pos, delta, deltaNormalized, colour; + float height, weight; + active_t active; + int nextDropTime; + qhandle_t *effectshader; +} cg_atmosphericParticle_t; + +typedef struct cg_atmosphericEffect_s { + cg_atmosphericParticle_t particles[MAX_ATMOSPHERIC_PARTICLES]; + qhandle_t effectshaders[MAX_ATMOSPHERIC_EFFECTSHADERS]; + int lastRainTime, numDrops; + int gustStartTime, gustEndTime; + int baseStartTime, baseEndTime; + int gustMinTime, gustMaxTime; + int changeMinTime, changeMaxTime; + int baseMinTime, baseMaxTime; + float baseWeight, gustWeight; + int baseDrops, gustDrops; + int baseHeightOffset; + int numEffectShaders; + vec3_t baseVec, gustVec; + + vec3_t viewDir; + + qboolean ( *ParticleCheckVisible )( cg_atmosphericParticle_t *particle ); + qboolean ( *ParticleGenerate )( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ); + void ( *ParticleRender )( cg_atmosphericParticle_t *particle ); + + int dropsActive, oldDropsActive; + int dropsRendered, dropsCreated, dropsSkipped; +} cg_atmosphericEffect_t; + +static cg_atmosphericEffect_t cg_atmFx; + + +static qboolean CG_SetParticleActive( cg_atmosphericParticle_t *particle, active_t active ) { + particle->active = active; + return active ? qtrue : qfalse; +} + + +/* +** Raindrop management functions +*/ + +static qboolean CG_RainParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ) { + // Attempt to 'spot' a raindrop somewhere below a sky texture. + + float angle, distance; + float groundHeight, skyHeight; +// int msec = trap_Milliseconds(); + +// n_generatetime++; + + angle = random() * 2 * M_PI; + distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random(); + + particle->pos[0] = cg.refdef_current->vieworg[0] + sin( angle ) * distance; + particle->pos[1] = cg.refdef_current->vieworg[1] + cos( angle ) * distance; + + // ydnar: choose a spawn point randomly between sky and ground + skyHeight = BG_GetSkyHeightAtPoint( particle->pos ); + if ( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) { + return qfalse; + } + groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos ); + if ( groundHeight >= skyHeight ) { + return qfalse; + } + particle->pos[2] = groundHeight + random() * ( skyHeight - groundHeight ); + + // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling') + if ( cg_atmFx.baseHeightOffset > 0 ) { + if ( particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset ) { + particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset; + + if ( particle->pos[2] < groundHeight ) { + return qfalse; + } + } + } + + // ydnar: rain goes in bursts + { + float maxActiveDrops; + + // every 10 seconds allow max raindrops + maxActiveDrops = 0.50 * cg_atmFx.numDrops + 0.001 * cg_atmFx.numDrops * ( 10000 - ( cg.time % 10000 ) ); + if ( cg_atmFx.oldDropsActive > maxActiveDrops ) { + return qfalse; + } + } + + CG_SetParticleActive( particle, ACT_FALLING ); + particle->colour[0] = 0.6 + 0.2 * random() * 0xFF; + particle->colour[1] = 0.6 + 0.2 * random() * 0xFF; + particle->colour[2] = 0.6 + 0.2 * random() * 0xFF; + VectorCopy( currvec, particle->delta ); + particle->delta[2] += crandom() * 100; + VectorCopy( particle->delta, particle->deltaNormalized ); + VectorNormalizeFast( particle->deltaNormalized ); + particle->height = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100; + particle->weight = currweight; + particle->effectshader = &cg_atmFx.effectshaders[0]; +// particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ]; + +// generatetime += trap_Milliseconds() - msec; + return( qtrue ); +} + +static qboolean CG_RainParticleCheckVisible( cg_atmosphericParticle_t *particle ) { + // Check the raindrop is visible and still going, wrapping if necessary. + + float moved; + vec2_t distance; +// int msec = trap_Milliseconds(); + + if ( !particle || particle->active == ACT_NOT ) { +// checkvisibletime += trap_Milliseconds() - msec; + return( qfalse ); + } + + moved = ( cg.time - cg_atmFx.lastRainTime ) * 0.001; // Units moved since last frame + VectorMA( particle->pos, moved, particle->delta, particle->pos ); + if ( particle->pos[2] + particle->height < BG_GetSkyGroundHeightAtPoint( particle->pos ) ) { +// checkvisibletime += trap_Milliseconds() - msec; + return CG_SetParticleActive( particle, ACT_NOT ); + } + + distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0]; + distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1]; + if ( ( distance[0] * distance[0] + distance[1] * distance[1] ) > Square( MAX_ATMOSPHERIC_DISTANCE ) ) { + // ydnar: just nuke this particle, let it respawn + return CG_SetParticleActive( particle, ACT_NOT ); + + /* + // Attempt to respot the particle at our other side + particle->pos[0] -= 1.85f * distance[0]; + particle->pos[1] -= 1.85f * distance[1]; + + // Valid spot? + pointHeight = BG_GetSkyHeightAtPoint( particle->pos ); + if( pointHeight == MAX_ATMOSPHERIC_HEIGHT ) { +// checkvisibletime += trap_Milliseconds() - msec; + return CG_SetParticleActive( particle, ACT_NOT ); + } + + pointHeight = BG_GetSkyGroundHeightAtPoint( particle->pos ); + if( pointHeight == MAX_ATMOSPHERIC_HEIGHT || pointHeight >= particle->pos[2] ) { +// checkvisibletime += trap_Milliseconds() - msec; + return CG_SetParticleActive( particle, ACT_NOT ); + } + */ + } + +// checkvisibletime += trap_Milliseconds() - msec; + return( qtrue ); +} + +static void CG_RainParticleRender( cg_atmosphericParticle_t *particle ) { + // Draw a raindrop + + vec3_t forward, right; + polyVert_t verts[3]; + vec2_t line; + float len, frac, dist; + vec3_t start, finish; + float groundHeight; +// int msec = trap_Milliseconds(); + +// n_rendertime++; + + if ( particle->active == ACT_NOT ) { +// rendertime += trap_Milliseconds() - msec; + return; + } + + if ( CG_CullPoint( particle->pos ) ) { + return; + } + + VectorCopy( particle->pos, start ); + + dist = DistanceSquared( particle->pos, cg.refdef_current->vieworg ); + + // Make sure it doesn't clip through surfaces + groundHeight = BG_GetSkyGroundHeightAtPoint( start ); + len = particle->height; + if ( start[2] <= groundHeight ) { + // Stop snow going through surfaces. + len = particle->height - groundHeight + start[2]; + frac = start[2]; + VectorMA( start, len - particle->height, particle->deltaNormalized, start ); + } + + if ( len <= 0 ) { +// rendertime += trap_Milliseconds() - msec; + return; + } + + // fade nearby rain particles + if ( dist < Square( 128.f ) ) { + dist = .25f + .75f * ( dist / Square( 128.f ) ); + } else { + dist = 1.0f; + } + + VectorCopy( particle->deltaNormalized, forward ); + VectorMA( start, -len, forward, finish ); + + line[0] = DotProduct( forward, cg.refdef_current->viewaxis[1] ); + line[1] = DotProduct( forward, cg.refdef_current->viewaxis[2] ); + + VectorScale( cg.refdef_current->viewaxis[1], line[1], right ); + VectorMA( right, -line[0], cg.refdef_current->viewaxis[2], right ); + VectorNormalize( right ); + + // dist = 1.0; + + VectorCopy( finish, verts[0].xyz ); + verts[0].st[0] = 0.5f; + verts[0].st[1] = 0; + verts[0].modulate[0] = particle->colour[0]; + verts[0].modulate[1] = particle->colour[1]; + verts[0].modulate[2] = particle->colour[2]; + verts[0].modulate[3] = 100 * dist; + + VectorMA( start, -particle->weight, right, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = particle->colour[0]; + verts[1].modulate[1] = particle->colour[1]; + verts[2].modulate[2] = particle->colour[2]; + verts[1].modulate[3] = 200 * dist; + + VectorMA( start, particle->weight, right, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = particle->colour[0]; + verts[2].modulate[1] = particle->colour[1]; + verts[2].modulate[2] = particle->colour[2]; + verts[2].modulate[3] = 200 * dist; + + CG_AddPolyToPool( *particle->effectshader, verts ); + +// rendertime += trap_Milliseconds() - msec; +} + +/* +** Snow management functions +*/ + +static qboolean CG_SnowParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ) { + // Attempt to 'spot' a snowflake somewhere below a sky texture. + + float angle, distance; + float groundHeight, skyHeight; +// int msec = trap_Milliseconds(); + +// n_generatetime++; + + angle = random() * 2 * M_PI; + distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random(); + + particle->pos[0] = cg.refdef_current->vieworg[0] + sin( angle ) * distance; + particle->pos[1] = cg.refdef_current->vieworg[1] + cos( angle ) * distance; + + // ydnar: choose a spawn point randomly between sky and ground + skyHeight = BG_GetSkyHeightAtPoint( particle->pos ); + if ( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) { + return qfalse; + } + groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos ); + if ( groundHeight >= skyHeight ) { + return qfalse; + } + particle->pos[2] = groundHeight + random() * ( skyHeight - groundHeight ); + + // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling') + if ( cg_atmFx.baseHeightOffset > 0 ) { + if ( particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset ) { + particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset; + if ( particle->pos[2] < groundHeight ) { + return qfalse; + } + } + } + + CG_SetParticleActive( particle, ACT_FALLING ); + VectorCopy( currvec, particle->delta ); + particle->delta[2] += crandom() * 25; + VectorCopy( particle->delta, particle->deltaNormalized ); + VectorNormalizeFast( particle->deltaNormalized ); + particle->height = ATMOSPHERIC_SNOW_HEIGHT + random() * 2; + particle->weight = particle->height * 0.5f; + particle->effectshader = &cg_atmFx.effectshaders[0]; +// particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ]; + +// generatetime += trap_Milliseconds() - msec; + return( qtrue ); +} + +static qboolean CG_SnowParticleCheckVisible( cg_atmosphericParticle_t *particle ) { + // Check the snowflake is visible and still going, wrapping if necessary. + + float moved; + vec2_t distance; +// int msec = trap_Milliseconds(); + +// n_checkvisibletime++; + + if ( !particle || particle->active == ACT_NOT ) { +// checkvisibletime += trap_Milliseconds() - msec; + return( qfalse ); + } + + moved = ( cg.time - cg_atmFx.lastRainTime ) * 0.001; // Units moved since last frame + VectorMA( particle->pos, moved, particle->delta, particle->pos ); + if ( particle->pos[2] < BG_GetSkyGroundHeightAtPoint( particle->pos ) ) { +// checkvisibletime += trap_Milliseconds() - msec; + return CG_SetParticleActive( particle, ACT_NOT ); + } + + distance[0] = particle->pos[0] - cg.refdef_current->vieworg[0]; + distance[1] = particle->pos[1] - cg.refdef_current->vieworg[1]; + if ( ( distance[0] * distance[0] + distance[1] * distance[1] ) > Square( MAX_ATMOSPHERIC_DISTANCE ) ) { + // ydnar: just nuke this particle, let it respawn + return CG_SetParticleActive( particle, ACT_NOT ); + + /* + // Attempt to respot the particle at our other side + particle->pos[0] -= 1.85f * distance[0]; + particle->pos[1] -= 1.85f * distance[1]; + + // ydnar: place particle in random position between ground and sky + groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos ); + skyHeight = BG_GetSkyHeightAtPoint( particle->pos ); + if( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) + return CG_SetParticleActive( particle, ACT_NOT ); + particle->pos[ 2 ] = groundHeight + random() * (skyHeight - groundHeight); + + // ydnar: valid spot? + if( particle->pos[ 2 ] <= groundHeight || particle->pos[ 2 ] >= skyHeight ) + return CG_SetParticleActive( particle, ACT_NOT ); + */ + } + +// checkvisibletime += trap_Milliseconds() - msec; + return( qtrue ); +} + +static void CG_SnowParticleRender( cg_atmosphericParticle_t *particle ) { + // Draw a snowflake + + vec3_t forward, right; + polyVert_t verts[3]; + vec2_t line; + float len, frac, sinTumbling, cosTumbling, particleWidth, dist; + vec3_t start, finish; + float groundHeight; +// int msec = trap_Milliseconds(); + +// n_rendertime++; + + if ( particle->active == ACT_NOT ) { +// rendertime += trap_Milliseconds() - msec; + return; + } + + if ( CG_CullPoint( particle->pos ) ) { + return; + } + + VectorCopy( particle->pos, start ); + + sinTumbling = sin( particle->pos[2] * 0.03125f * ( 0.5f * particle->weight ) ); + cosTumbling = cos( ( particle->pos[2] + particle->pos[1] ) * 0.03125f * ( 0.5f * particle->weight ) ); + start[0] += 24 * ( 1 - particle->deltaNormalized[2] ) * sinTumbling; + start[1] += 24 * ( 1 - particle->deltaNormalized[2] ) * cosTumbling; + + // Make sure it doesn't clip through surfaces + groundHeight = BG_GetSkyGroundHeightAtPoint( start ); + len = particle->height; + if ( start[2] <= groundHeight ) { + // Stop snow going through surfaces. + len = particle->height - groundHeight + start[2]; + frac = start[2]; + VectorMA( start, len - particle->height, particle->deltaNormalized, start ); + } + + if ( len <= 0 ) { +// rendertime += trap_Milliseconds() - msec; + return; + } + + line[0] = particle->pos[0] - cg.refdef_current->vieworg[0]; + line[1] = particle->pos[1] - cg.refdef_current->vieworg[1]; + + dist = DistanceSquared( particle->pos, cg.refdef_current->vieworg ); + // dist becomes scale + if ( dist > Square( 500.f ) ) { + dist = 1.f + ( ( dist - Square( 500.f ) ) * ( 10.f / Square( 2000.f ) ) ); + } else { + dist = 1.f; + } + + len *= dist; + + VectorCopy( particle->deltaNormalized, forward ); + VectorMA( start, -( len /** sinTumbling*/ ), forward, finish ); + + line[0] = DotProduct( forward, cg.refdef_current->viewaxis[1] ); + line[1] = DotProduct( forward, cg.refdef_current->viewaxis[2] ); + + VectorScale( cg.refdef_current->viewaxis[1], line[1], right ); + VectorMA( right, -line[0], cg.refdef_current->viewaxis[2], right ); + VectorNormalize( right ); + + particleWidth = dist * ( /*cosTumbling **/ particle->weight ); + + VectorMA( finish, -particleWidth, right, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorMA( start, -particleWidth, right, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorMA( start, particleWidth, right, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + CG_AddPolyToPool( *particle->effectshader, verts ); + +// rendertime += trap_Milliseconds() - msec; +} + +/* +** Set up gust parameters. +*/ + +static void CG_EffectGust() { + // Generate random values for the next gust + + int diff; + + cg_atmFx.baseEndTime = cg.time + cg_atmFx.baseMinTime + ( rand() % ( cg_atmFx.baseMaxTime - cg_atmFx.baseMinTime ) ); + diff = cg_atmFx.changeMaxTime - cg_atmFx.changeMinTime; + cg_atmFx.gustStartTime = cg_atmFx.baseEndTime + cg_atmFx.changeMinTime + ( diff ? ( rand() % diff ) : 0 ); + diff = cg_atmFx.gustMaxTime - cg_atmFx.gustMinTime; + cg_atmFx.gustEndTime = cg_atmFx.gustStartTime + cg_atmFx.gustMinTime + ( diff ? ( rand() % diff ) : 0 ); + diff = cg_atmFx.changeMaxTime - cg_atmFx.changeMinTime; + cg_atmFx.baseStartTime = cg_atmFx.gustEndTime + cg_atmFx.changeMinTime + ( diff ? ( rand() % diff ) : 0 ); +} + +static qboolean CG_EffectGustCurrent( vec3_t curr, float *weight, int *num ) { + // Calculate direction for new drops. + + vec3_t temp; + float frac; + + if ( cg.time < cg_atmFx.baseEndTime ) { + VectorCopy( cg_atmFx.baseVec, curr ); + *weight = cg_atmFx.baseWeight; + *num = cg_atmFx.baseDrops; + } else { + VectorSubtract( cg_atmFx.gustVec, cg_atmFx.baseVec, temp ); + if ( cg.time < cg_atmFx.gustStartTime ) { + frac = ( (float)( cg.time - cg_atmFx.baseEndTime ) ) / ( (float)( cg_atmFx.gustStartTime - cg_atmFx.baseEndTime ) ); + VectorMA( cg_atmFx.baseVec, frac, temp, curr ); + *weight = cg_atmFx.baseWeight + ( cg_atmFx.gustWeight - cg_atmFx.baseWeight ) * frac; + *num = cg_atmFx.baseDrops + ( (float)( cg_atmFx.gustDrops - cg_atmFx.baseDrops ) ) * frac; + } else if ( cg.time < cg_atmFx.gustEndTime ) { + VectorCopy( cg_atmFx.gustVec, curr ); + *weight = cg_atmFx.gustWeight; + *num = cg_atmFx.gustDrops; + } else + { + frac = 1.0 - ( (float)( cg.time - cg_atmFx.gustEndTime ) ) / ( (float)( cg_atmFx.baseStartTime - cg_atmFx.gustEndTime ) ); + VectorMA( cg_atmFx.baseVec, frac, temp, curr ); + *weight = cg_atmFx.baseWeight + ( cg_atmFx.gustWeight - cg_atmFx.baseWeight ) * frac; + *num = cg_atmFx.baseDrops + ( (float)( cg_atmFx.gustDrops - cg_atmFx.baseDrops ) ) * frac; + if ( cg.time >= cg_atmFx.baseStartTime ) { + return( qtrue ); + } + } + } + return( qfalse ); +} + +static void CG_EP_ParseFloats( char *floatstr, float *f1, float *f2 ) { + // Parse the float or floats + + char *middleptr; + char buff[64]; + + Q_strncpyz( buff, floatstr, sizeof( buff ) ); + for ( middleptr = buff; *middleptr && *middleptr != ' '; middleptr++ ) ; + if ( *middleptr ) { + *middleptr++ = 0; + *f1 = atof( floatstr ); + *f2 = atof( middleptr ); + } else { + *f1 = *f2 = atof( floatstr ); + } +} + +static void CG_EP_ParseInts( char *intstr, int *i1, int *i2 ) { + // Parse the int or ints + + char *middleptr; + char buff[64]; + + Q_strncpyz( buff, intstr, sizeof( buff ) ); + for ( middleptr = buff; *middleptr && *middleptr != ' '; middleptr++ ) ; + if ( *middleptr ) { + *middleptr++ = 0; + *i1 = atof( intstr ); + *i2 = atof( middleptr ); + } else { + *i1 = *i2 = atof( intstr ); + } +} + +void CG_EffectParse( const char *effectstr ) { + // Split the string into it's component parts. + + float bmin, bmax, cmin, cmax, gmin, gmax, bdrop, gdrop /*, wsplash, lsplash*/; + int count, bheight; + char *startptr, *eqptr, *endptr; + char workbuff[128]; + atmFXType_t atmFXType = ATM_NONE; + + if ( CG_AtmosphericKludge() ) { + return; + } + + // Set up some default values + cg_atmFx.baseVec[0] = cg_atmFx.baseVec[1] = 0; + cg_atmFx.gustVec[0] = cg_atmFx.gustVec[1] = 100; + bmin = 5; + bmax = 10; + cmin = 1; + cmax = 1; + gmin = 0; + gmax = 2; + bdrop = gdrop = 300; + cg_atmFx.baseWeight = 0.7f; + cg_atmFx.gustWeight = 1.5f; + bheight = 0; + + // Parse the parameter string + Q_strncpyz( workbuff, effectstr, sizeof( workbuff ) ); + for ( startptr = workbuff; *startptr; ) + { + for ( eqptr = startptr; *eqptr && *eqptr != '=' && *eqptr != ','; eqptr++ ) ; + if ( !*eqptr ) { + break; // No more string + } + if ( *eqptr == ',' ) { + startptr = eqptr + 1; // Bad argument, continue + continue; + } + *eqptr++ = 0; + for ( endptr = eqptr; *endptr && *endptr != ','; endptr++ ) ; + if ( *endptr ) { + *endptr++ = 0; + } + + if ( atmFXType == ATM_NONE ) { + if ( Q_stricmp( startptr, "T" ) ) { + cg_atmFx.numDrops = 0; + CG_Printf( "Atmospheric effect must start with a type.\n" ); + return; + } + if ( !Q_stricmp( eqptr, "RAIN" ) ) { + atmFXType = ATM_RAIN; + cg_atmFx.ParticleCheckVisible = &CG_RainParticleCheckVisible; + cg_atmFx.ParticleGenerate = &CG_RainParticleGenerate; + cg_atmFx.ParticleRender = &CG_RainParticleRender; + + cg_atmFx.baseVec[2] = cg_atmFx.gustVec[2] = -ATMOSPHERIC_RAIN_SPEED; + } else if ( !Q_stricmp( eqptr, "SNOW" ) ) { + atmFXType = ATM_SNOW; + cg_atmFx.ParticleCheckVisible = &CG_SnowParticleCheckVisible; + cg_atmFx.ParticleGenerate = &CG_SnowParticleGenerate; + cg_atmFx.ParticleRender = &CG_SnowParticleRender; + + cg_atmFx.baseVec[2] = cg_atmFx.gustVec[2] = -ATMOSPHERIC_SNOW_SPEED; + } else { + cg_atmFx.numDrops = 0; + CG_Printf( "Only effect type 'rain' and 'snow' are supported.\n" ); + return; + } + } else { + if ( !Q_stricmp( startptr, "B" ) ) { + CG_EP_ParseFloats( eqptr, &bmin, &bmax ); + } else if ( !Q_stricmp( startptr, "C" ) ) { + CG_EP_ParseFloats( eqptr, &cmin, &cmax ); + } else if ( !Q_stricmp( startptr, "G" ) ) { + CG_EP_ParseFloats( eqptr, &gmin, &gmax ); + } else if ( !Q_stricmp( startptr, "BV" ) ) { + CG_EP_ParseFloats( eqptr, &cg_atmFx.baseVec[0], &cg_atmFx.baseVec[1] ); + } else if ( !Q_stricmp( startptr, "GV" ) ) { + CG_EP_ParseFloats( eqptr, &cg_atmFx.gustVec[0], &cg_atmFx.gustVec[1] ); + } else if ( !Q_stricmp( startptr, "W" ) ) { + CG_EP_ParseFloats( eqptr, &cg_atmFx.baseWeight, &cg_atmFx.gustWeight ); + } else if ( !Q_stricmp( startptr, "D" ) ) { + CG_EP_ParseFloats( eqptr, &bdrop, &gdrop ); + } else if ( !Q_stricmp( startptr, "H" ) ) { + CG_EP_ParseInts( eqptr, &bheight, &bheight ); + } else { CG_Printf( "Unknown effect key '%s'.\n", startptr );} + } + startptr = endptr; + } + + if ( atmFXType == ATM_NONE || !BG_LoadTraceMap( cgs.rawmapname, cg.mapcoordsMins, cg.mapcoordsMaxs ) ) { + // No effects + + cg_atmFx.numDrops = -1; + return; + } + + cg_atmFx.baseHeightOffset = bheight; + if ( cg_atmFx.baseHeightOffset < 0 ) { + cg_atmFx.baseHeightOffset = 0; + } + cg_atmFx.baseMinTime = 1000 * bmin; + cg_atmFx.baseMaxTime = 1000 * bmax; + cg_atmFx.changeMinTime = 1000 * cmin; + cg_atmFx.changeMaxTime = 1000 * cmax; + cg_atmFx.gustMinTime = 1000 * gmin; + cg_atmFx.gustMaxTime = 1000 * gmax; + cg_atmFx.baseDrops = bdrop; + cg_atmFx.gustDrops = gdrop; + + cg_atmFx.numDrops = ( cg_atmFx.baseDrops > cg_atmFx.gustDrops ) ? cg_atmFx.baseDrops : cg_atmFx.gustDrops; + if ( cg_atmFx.numDrops > MAX_ATMOSPHERIC_PARTICLES ) { + cg_atmFx.numDrops = MAX_ATMOSPHERIC_PARTICLES; + } + // Load graphics + + // Rain + if ( atmFXType == ATM_RAIN ) { + cg_atmFx.numEffectShaders = 1; + cg_atmFx.effectshaders[0] = trap_R_RegisterShader( "gfx/misc/raindrop" ); + if ( !( cg_atmFx.effectshaders[0] ) ) { + cg_atmFx.effectshaders[0] = -1; + cg_atmFx.numEffectShaders = 0; + } + + // Snow + } else if ( atmFXType == ATM_SNOW ) { + cg_atmFx.numEffectShaders = 1; + cg_atmFx.effectshaders[0] = trap_R_RegisterShader( "gfx/misc/snow" ); + + // This really should never happen + } else { + cg_atmFx.numEffectShaders = 0; + } + + // Initialise atmospheric effect to prevent all particles falling at the start + for ( count = 0; count < cg_atmFx.numDrops; count++ ) + cg_atmFx.particles[count].nextDropTime = ATMOSPHERIC_DROPDELAY + ( rand() % ATMOSPHERIC_DROPDELAY ); + + CG_EffectGust(); +} + +/* +** Main render loop +*/ + +void CG_AddAtmosphericEffects() { + // Add atmospheric effects (e.g. rain, snow etc.) to view + + int curr, max, currnum; + cg_atmosphericParticle_t *particle; + vec3_t currvec; + float currweight; + + if ( cg_atmFx.numDrops <= 0 || cg_atmFx.numEffectShaders == 0 || cg_atmosphericEffects.value <= 0 ) { + return; + } + +#ifndef ATM_NEW + CG_ClearPolyPool(); +#endif // ATM_NEW + + max = cg_atmosphericEffects.value < 1 ? cg_atmosphericEffects.value * cg_atmFx.numDrops : cg_atmFx.numDrops; + if ( CG_EffectGustCurrent( currvec, &currweight, &currnum ) ) { + CG_EffectGust(); // Recalculate gust parameters + + } + // ydnar: allow parametric management of drop count for swelling/waning precip + cg_atmFx.oldDropsActive = cg_atmFx.dropsActive; + cg_atmFx.dropsActive = 0; + + cg_atmFx.dropsRendered = cg_atmFx.dropsCreated = cg_atmFx.dropsSkipped = 0; + +// getgroundtime = getskytime = rendertime = checkvisibletime = generatetime = 0; +// n_getgroundtime = n_getskytime = n_rendertime = n_checkvisibletime = n_generatetime = 0; + + VectorSet( cg_atmFx.viewDir, cg.refdef_current->viewaxis[0][0], cg.refdef_current->viewaxis[0][1], 0.f ); + + for ( curr = 0; curr < max; curr++ ) + { + particle = &cg_atmFx.particles[curr]; + //% if( !CG_SnowParticleCheckVisible( particle ) ) + if ( !cg_atmFx.ParticleCheckVisible( particle ) ) { + // Effect has terminated / fallen from screen view + /* + if( !particle->nextDropTime ) + { + // Stop rain being synchronized + particle->nextDropTime = cg.time + rand() % ATMOSPHERIC_DROPDELAY; + } + if( currnum < curr || particle->nextDropTime > cg.time ) + { + cg_atmFx.dropsRendered++; + continue; + } */ + //% if( !CG_SnowParticleGenerate( particle, currvec, currweight ) ) + if ( !cg_atmFx.ParticleGenerate( particle, currvec, currweight ) ) { + // Ensure it doesn't attempt to generate every frame, to prevent + // 'clumping' when there's only a small sky area available. + particle->nextDropTime = cg.time + ATMOSPHERIC_DROPDELAY; + continue; + } else + { + cg_atmFx.dropsCreated++; + } + } + + //% CG_RainParticleRender( particle ); + cg_atmFx.ParticleRender( particle ); + cg_atmFx.dropsActive++; + } + +// CG_RenderPolyPool(); + + cg_atmFx.lastRainTime = cg.time; + +// CG_Printf( "Active: %d Generated: %d Rendered: %d Skipped: %d\n", cg_atmFx.dropsActive, cg_atmFx.dropsCreated, cg_atmFx.dropsRendered, cg_atmFx.dropsSkipped ); +// CG_Printf( "gg: %i gs: %i rt: %i cv: %i ge: %i\n", getgroundtime, getskytime, rendertime, checkvisibletime, generatetime ); +// CG_Printf( "\\-> %i \\-> %i \\-> %i \\-> %i \\-> %i\n", n_getgroundtime, n_getskytime, n_rendertime, n_checkvisibletime, n_generatetime ); +} diff --git a/src/cgame/cg_character.c b/src/cgame/cg_character.c new file mode 100644 index 0000000..4e16f9b --- /dev/null +++ b/src/cgame/cg_character.c @@ -0,0 +1,538 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +/* +** Character loading +*/ + +#include "cg_local.h" + +char bigTextBuffer[100000]; + +// TTimo - defined but not used +#if 0 +/* +====================== +CG_ParseGibModels + +Read a configuration file containing gib models for use with this character +====================== +*/ +static qboolean CG_ParseGibModels( bg_playerclass_t* classInfo ) { + char *text_p; + int len; + int i; + char *token; + fileHandle_t f; + + memset( classInfo->gibModels, 0, sizeof( classInfo->gibModels ) ); + + // load the file + len = trap_FS_FOpenFile( va( "models/players/%s/gibs.cfg", classInfo->modelPath ), &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + if ( len >= sizeof( bigTextBuffer ) - 1 ) { + CG_Printf( "File %s too long\n", va( "models/players/%s/gibs.cfg", classInfo->modelPath ) ); + return qfalse; + } + trap_FS_Read( bigTextBuffer, len, f ); + bigTextBuffer[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = bigTextBuffer; + + for ( i = 0; i < MAX_GIB_MODELS; i++ ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + // cache this model + classInfo->gibModels[i] = trap_R_RegisterModel( token ); + } + + return qtrue; +} +#endif + +/* +====================== +CG_ParseHudHeadConfig +====================== +*/ +static qboolean CG_ParseHudHeadConfig( const char *filename, animation_t* hha ) { + char *text_p; + int len; + int i; + float fps; + char *token; + fileHandle_t f; + + // load the file + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + + if ( len >= sizeof( bigTextBuffer ) - 1 ) { + CG_Printf( "File %s too long\n", filename ); + return qfalse; + } + + trap_FS_Read( bigTextBuffer, len, f ); + bigTextBuffer[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = bigTextBuffer; + + for ( i = 0 ; i < MAX_HD_ANIMATIONS ; i++ ) { + token = COM_Parse( &text_p ); // first frame + if ( !token ) { + break; + } + hha[i].firstFrame = atoi( token ); + + token = COM_Parse( &text_p ); // length + if ( !token ) { + break; + } + hha[i].numFrames = atoi( token ); + + token = COM_Parse( &text_p ); // fps + if ( !token ) { + break; + } + fps = atof( token ); + if ( fps == 0 ) { + fps = 1; + } + + hha[i].frameLerp = 1000 / fps; + hha[i].initialLerp = 1000 / fps; + + token = COM_Parse( &text_p ); // looping frames + if ( !token ) { + break; + } + hha[i].loopFrames = atoi( token ); + + if ( hha[i].loopFrames > hha[i].numFrames ) { + hha[i].loopFrames = hha[i].numFrames; + } else if ( hha[i].loopFrames < 0 ) { + hha[i].loopFrames = 0; + } + } + + if ( i != MAX_HD_ANIMATIONS ) { + CG_Printf( "Error parsing hud head animation file: %s", filename ); + return qfalse; + } + + return qtrue; +} + +/* +================== +CG_CalcMoveSpeeds +================== +*/ +static void CG_CalcMoveSpeeds( bg_character_t *character ) { + char *tags[2] = {"tag_footleft", "tag_footright"}; + vec3_t oldPos[2]; + refEntity_t refent; + animation_t *anim; + int i, j, k; + float totalSpeed; + int numSpeed; + int lastLow, low; + orientation_t o[2]; + + memset( &refent, 0, sizeof( refent ) ); + + refent.hModel = character->mesh; + + for ( i = 0; i < character->animModelInfo->numAnimations; i++ ) { + anim = character->animModelInfo->animations[i]; + + if ( anim->moveSpeed >= 0 ) { + continue; + } + + totalSpeed = 0; + lastLow = -1; + numSpeed = 0; + + // for each frame + for ( j = 0; j < anim->numFrames; j++ ) { + + refent.frame = anim->firstFrame + j; + refent.oldframe = refent.frame; + refent.torsoFrameModel = refent.oldTorsoFrameModel = refent.frameModel = refent.oldframeModel = anim->mdxFile; + + // for each foot + for ( k = 0; k < 2; k++ ) { + if ( trap_R_LerpTag( &o[k], &refent, tags[k], 0 ) < 0 ) { + CG_Error( "CG_CalcMoveSpeeds: unable to find tag %s, cannot calculate movespeed", tags[k] ); + } + } + + // find the contact foot + if ( anim->flags & ANIMFL_LADDERANIM ) { + if ( o[0].origin[0] > o[1].origin[0] ) { + low = 0; + } else { + low = 1; + } + totalSpeed += fabs( oldPos[low][2] - o[low].origin[2] ); + } else { + if ( o[0].origin[2] < o[1].origin[2] ) { + low = 0; + } else { + low = 1; + } + totalSpeed += fabs( oldPos[low][0] - o[low].origin[0] ); + } + + numSpeed++; + + // save the positions + for ( k = 0; k < 2; k++ ) { + VectorCopy( o[k].origin, oldPos[k] ); + } + lastLow = low; + } + + // record the speed + anim->moveSpeed = (int)( ( totalSpeed / numSpeed ) * 1000.0 / anim->frameLerp ); + } +} + +/* +====================== +CG_ParseAnimationFiles + + Read in all the configuration and script files for this model. +====================== +*/ +static qboolean CG_ParseAnimationFiles( bg_character_t *character, const char *animationGroup, const char *animationScript ) { + char filename[MAX_QPATH]; + fileHandle_t f; + int len; + + // set the name of the animationGroup and animationScript in the animModelInfo structure + Q_strncpyz( character->animModelInfo->animationGroup, animationGroup, sizeof( character->animModelInfo->animationGroup ) ); + Q_strncpyz( character->animModelInfo->animationScript, animationScript, sizeof( character->animModelInfo->animationScript ) ); + + BG_R_RegisterAnimationGroup( animationGroup, character->animModelInfo ); + + // calc movespeed values if required + CG_CalcMoveSpeeds( character ); + + // load the script file + len = trap_FS_FOpenFile( animationScript, &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + if ( len >= sizeof( bigTextBuffer ) - 1 ) { + CG_Printf( "File %s is too long\n", filename ); + return qfalse; + } + trap_FS_Read( bigTextBuffer, len, f ); + bigTextBuffer[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + BG_AnimParseAnimScript( character->animModelInfo, &cgs.animScriptData, animationScript, bigTextBuffer ); + + return qtrue; +} + +/* +================== +CG_CheckForExistingAnimModelInfo + + If this player model has already been parsed, then use the existing information. + Otherwise, set the modelInfo pointer to the first free slot. + + returns qtrue if existing model found, qfalse otherwise +================== +*/ +static qboolean CG_CheckForExistingAnimModelInfo( const char *animationGroup, const char *animationScript, animModelInfo_t **animModelInfo ) { + int i; + animModelInfo_t *trav, *firstFree = NULL; + + for ( i = 0, trav = cgs.animScriptData.modelInfo; i < MAX_ANIMSCRIPT_MODELS; i++, trav++ ) { + if ( *trav->animationGroup && *trav->animationScript ) { + if ( !Q_stricmp( trav->animationGroup, animationGroup ) && !Q_stricmp( trav->animationScript, animationScript ) ) { + // found a match, use this animModelInfo + *animModelInfo = trav; + return qtrue; + } + } else if ( !firstFree ) { + firstFree = trav; + } + } + + if ( !firstFree ) { + CG_Error( "unable to find a free modelinfo slot, cannot continue\n" ); + } else { + *animModelInfo = firstFree; + // clear the structure out ready for use + memset( *animModelInfo, 0, sizeof( *animModelInfo ) ); + } + + // qfalse signifies that we need to parse the information from the script files + return qfalse; +} + +/* +============== +CG_RegisterAcc +============== +*/ +static qboolean CG_RegisterAcc( const char *modelName, int *model, const char* skinname, qhandle_t* skin ) { + char filename[MAX_QPATH]; + + *model = trap_R_RegisterModel( modelName ); + + if ( !*model ) { + return qfalse; + } + + COM_StripExtension( modelName, filename ); + Q_strcat( filename, sizeof( filename ), va( "_%s.skin", skinname ) ); + *skin = trap_R_RegisterSkin( filename ); + + return qtrue; +} + +typedef struct { + char *type; + accType_t index; +} acc_t; + +static acc_t cg_accessories[] = { + { "md3_beltr", ACC_BELT_LEFT }, + { "md3_beltl", ACC_BELT_RIGHT }, + { "md3_belt", ACC_BELT }, + { "md3_back", ACC_BACK }, + { "md3_weapon", ACC_WEAPON }, + { "md3_weapon2", ACC_WEAPON2 }, +}; + +static int cg_numAccessories = sizeof( cg_accessories ) / sizeof( cg_accessories[0] ); + +static acc_t cg_headAccessories[] = { + { "md3_hat", ACC_HAT }, + { "md3_rank", ACC_RANK }, + { "md3_hat2", ACC_MOUTH2 }, + { "md3_hat3", ACC_MOUTH3 }, +}; + +static int cg_numHeadAccessories = sizeof( cg_headAccessories ) / sizeof( cg_headAccessories[0] ); + +/* +==================== +CG_RegisterCharacter +==================== +*/ +qboolean CG_RegisterCharacter( const char *characterFile, bg_character_t *character ) { + bg_characterDef_t characterDef; + char *filename; + char buf[MAX_QPATH]; + char accessoryname[MAX_QPATH]; + int i; + + memset( &characterDef, 0, sizeof( characterDef ) ); + + if ( !BG_ParseCharacterFile( characterFile, &characterDef ) ) { + return qfalse; // the parser will provide the error message + } + + // Register Mesh + if ( !( character->mesh = trap_R_RegisterModel( characterDef.mesh ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register mesh '%s' referenced from '%s'\n", characterDef.mesh, characterFile ); + } + + // Register Skin + COM_StripExtension( characterDef.mesh, buf ); + filename = va( "%s_%s.skin", buf, characterDef.skin ); + if ( !( character->skin = trap_R_RegisterSkin( filename ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register skin '%s' referenced from '%s'\n", filename, characterFile ); + } else { + for ( i = 0; i < cg_numAccessories; i++ ) { + if ( trap_R_GetSkinModel( character->skin, cg_accessories[i].type, accessoryname ) ) { + if ( !CG_RegisterAcc( accessoryname, &character->accModels[cg_accessories[i].index], characterDef.skin, &character->accSkins[cg_accessories[i].index] ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register accessory '%s' referenced from '%s'->'%s'\n", accessoryname, characterFile, filename ); + } + } + } + + for ( i = 0; i < cg_numHeadAccessories; i++ ) { + if ( trap_R_GetSkinModel( character->skin, cg_headAccessories[i].type, accessoryname ) ) { + if ( !CG_RegisterAcc( accessoryname, &character->accModels[cg_headAccessories[i].index], characterDef.skin, &character->accSkins[cg_headAccessories[i].index] ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register accessory '%s' referenced from '%s'->'%s'\n", accessoryname, characterFile, filename ); + } + } + } + } + + // Register Undressed Corpse Media + if ( *characterDef.undressedCorpseModel ) { + // Register Undressed Corpse Model + if ( !( character->undressedCorpseModel = trap_R_RegisterModel( characterDef.undressedCorpseModel ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register undressed corpse model '%s' referenced from '%s'\n", characterDef.undressedCorpseModel, characterFile ); + } + + // Register Undressed Corpse Skin + COM_StripExtension( characterDef.undressedCorpseModel, buf ); + filename = va( "%s_%s.skin", buf, characterDef.undressedCorpseSkin ); + if ( !( character->undressedCorpseSkin = trap_R_RegisterSkin( filename ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register undressed corpse skin '%s' referenced from '%s'\n", filename, characterFile ); + } + } + + // Register the head for the hud + if ( *characterDef.hudhead ) { + // Register Hud Head Model + if ( !( character->hudhead = trap_R_RegisterModel( characterDef.hudhead ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register hud head model '%s' referenced from '%s'\n", characterDef.hudhead, characterFile ); + } + + if ( *characterDef.hudheadskin && !( character->hudheadskin = trap_R_RegisterSkin( characterDef.hudheadskin ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register hud head skin '%s' referenced from '%s'\n", characterDef.hudheadskin, characterFile ); + } + + if ( *characterDef.hudheadanims ) { + if ( !CG_ParseHudHeadConfig( characterDef.hudheadanims, character->hudheadanimations ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to register hud head animations '%s' referenced from '%s'\n", characterDef.hudheadanims, characterFile ); + } + } else { + CG_Printf( S_COLOR_YELLOW "WARNING: no hud head animations supplied in '%s'\n", characterFile ); + } + } + + // Parse Animation Files + if ( !CG_CheckForExistingAnimModelInfo( characterDef.animationGroup, characterDef.animationScript, &character->animModelInfo ) ) { + if ( !CG_ParseAnimationFiles( character, characterDef.animationGroup, characterDef.animationScript ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to load animation files referenced from '%s'\n", characterFile ); + return qfalse; + } + } + + // STILL MISSING: GIB MODELS (OPTIONAL?) + + return qtrue; +} + +bg_character_t *CG_CharacterForClientinfo( clientInfo_t *ci, centity_t *cent ) { + int team, cls; + + if ( cent && cent->currentState.eType == ET_CORPSE ) { + if ( cent->currentState.onFireStart >= 0 ) { + return cgs.gameCharacters[ cent->currentState.onFireStart ]; + } else { + if ( cent->currentState.modelindex < 4 ) { + return BG_GetCharacter( cent->currentState.modelindex, cent->currentState.modelindex2 ); + } else { + return BG_GetCharacter( cent->currentState.modelindex - 4, cent->currentState.modelindex2 ); + } + } + } + + if ( cent && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) { + team = ci->team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + + cls = ( cent->currentState.powerups >> PW_OPS_CLASS_1 ) & 7; + + return BG_GetCharacter( team, cls ); + } + + if ( ci->character ) { + return ci->character; + } + + return BG_GetCharacter( ci->team, ci->cls ); +} + +bg_character_t *CG_CharacterForPlayerstate( playerState_t* ps ) { + int team, cls; + + if ( ps->powerups[PW_OPS_DISGUISED] ) { + team = cgs.clientinfo[ps->clientNum].team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + + cls = 0; + if ( ps->powerups[PW_OPS_CLASS_1] ) { + cls |= 1; + } + if ( ps->powerups[PW_OPS_CLASS_2] ) { + cls |= 2; + } + if ( ps->powerups[PW_OPS_CLASS_3] ) { + cls |= 4; + } + + return BG_GetCharacter( team, cls ); + } + + return BG_GetCharacter( cgs.clientinfo[ps->clientNum].team, cgs.clientinfo[ps->clientNum].cls ); +} + +/* +======================== +CG_RegisterPlayerClasses +======================== +*/ +void CG_RegisterPlayerClasses( void ) { + bg_playerclass_t *classInfo; + bg_character_t *character; + int team, cls; + + for ( team = TEAM_AXIS; team <= TEAM_ALLIES; team++ ) { + for ( cls = PC_SOLDIER; cls < NUM_PLAYER_CLASSES; cls++ ) { + classInfo = BG_GetPlayerClassInfo( team, cls ); + character = BG_GetCharacter( team, cls ); + + Q_strncpyz( character->characterFile, classInfo->characterFile, sizeof( character->characterFile ) ); + + if ( !CG_RegisterCharacter( character->characterFile, character ) ) { + CG_Error( "ERROR: CG_RegisterPlayerClasses: failed to load character file '%s' for the %s %s\n", character->characterFile, ( team == TEAM_AXIS ? "Axis" : "Allied" ), BG_ClassnameForNumber( classInfo->classNum ) ); + } + + if ( !( classInfo->icon = trap_R_RegisterShaderNoMip( classInfo->iconName ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to load class icon '%s' for the %s %s\n", classInfo->iconName, ( team == TEAM_AXIS ? "Axis" : "Allied" ), BG_ClassnameForNumber( classInfo->classNum ) ); + } + + if ( !( classInfo->arrow = trap_R_RegisterShaderNoMip( classInfo->iconArrow ) ) ) { + CG_Printf( S_COLOR_YELLOW "WARNING: failed to load icon arrow '%s' for the %s %s\n", classInfo->iconArrow, ( team == TEAM_AXIS ? "Axis" : "Allied" ), BG_ClassnameForNumber( classInfo->classNum ) ); + } + } + } +} diff --git a/src/cgame/cg_commandmap.c b/src/cgame/cg_commandmap.c new file mode 100644 index 0000000..ba197f6 --- /dev/null +++ b/src/cgame/cg_commandmap.c @@ -0,0 +1,1593 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +static mapEntityData_t mapEntities[MAX_GENTITIES]; +static int mapEntityCount = 0; +static int mapEntityTime = 0; +static qboolean expanded = qfalse; + +extern playerInfo_t pi; + +qboolean ccInitial = qtrue; + +void CG_TransformToCommandMapCoord( float *coord_x, float *coord_y ) { + *coord_x = CC_2D_X + ( ( *coord_x - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * CC_2D_W; + *coord_y = CC_2D_Y + ( ( *coord_y - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * CC_2D_H; +} + +// START xkan, 9/19/2002 +//static float automapZoom = 3.583; // apporoximately 1.2^7 +static float automapZoom = 5.159; + +int CG_CurLayerForZ( int z ) { + int curlayer = 0; + + while ( z > cgs.ccLayerCeils[curlayer] && curlayer < cgs.ccLayers ) + curlayer++; + + if ( curlayer == cgs.ccLayers ) { + CG_Printf( "^3Warning: no valid command map layer for z\n" ); + curlayer = 0; + } + + return curlayer; +} + +static qboolean CG_ScissorEntIsCulled( mapEntityData_t* mEnt, mapScissor_t *scissor ) { + if ( !scissor->circular ) { + if ( mEnt->automapTransformed[0] < scissor->tl[0] + || mEnt->automapTransformed[0] > scissor->br[0] + || mEnt->automapTransformed[1] < scissor->tl[1] + || mEnt->automapTransformed[1] > scissor->br[1] ) { + return qtrue; + } + } else { + float distSquared; + vec2_t distVec; + + distVec[0] = mEnt->automapTransformed[0] - ( scissor->tl[0] + ( 0.5f * ( scissor->br[0] - scissor->tl[0] ) ) ); + distVec[1] = mEnt->automapTransformed[1] - ( scissor->tl[1] + ( 0.5f * ( scissor->br[1] - scissor->tl[1] ) ) ); + distSquared = distVec[0] * distVec[0] + distVec[1] * distVec[1]; + + if ( distSquared > Square( 0.5f * ( scissor->br[0] - scissor->tl[0] ) ) ) { + return qtrue; + } + } + + return qfalse; +} + +static qboolean CG_ScissorPointIsCulled( vec2_t vec, mapScissor_t *scissor ) { + if ( !scissor->circular ) { + if ( vec[0] < scissor->tl[0] + || vec[0] > scissor->br[0] + || vec[1] < scissor->tl[1] + || vec[1] > scissor->br[1] ) { + return qtrue; + } + } else { + float distSquared; + vec2_t distVec; + + distVec[0] = vec[0] - ( scissor->tl[0] + ( 0.5f * ( scissor->br[0] - scissor->tl[0] ) ) ); + distVec[1] = vec[1] - ( scissor->tl[1] + ( 0.5f * ( scissor->br[1] - scissor->tl[1] ) ) ); + distSquared = distVec[0] * distVec[0] + distVec[1] * distVec[1]; + + if ( distSquared > Square( 0.5f * ( scissor->br[0] - scissor->tl[0] ) ) ) { + return qtrue; + } + } + + return qfalse; +} + +/* +===================================================================================== +CG_TransformAutomapEntity: calculate the scaled (zoomed) yet unshifted coordinate for +each map entity within the automap +===================================================================================== +*/ +void CG_TransformAutomapEntity( void ) { + int i; + + for ( i = 0; i < mapEntityCount; i++ ) { + mapEntityData_t* mEnt = &mapEntities[i]; + + // calculate the screen coordinate of this entity for the automap, consider the zoom value + mEnt->automapTransformed[0] = ( mEnt->x - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] * 100 * automapZoom; + mEnt->automapTransformed[1] = ( mEnt->y - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] * 100 * automapZoom; + } +} + +void CG_AdjustAutomapZoom( int zoomIn ) { + if ( zoomIn ) { + automapZoom *= 1.2; + if ( automapZoom > 7.43 ) { // approximately 1.2^11 + automapZoom = 7.43; + } + } else { + automapZoom /= 1.2; + // zoom value of 1 corresponds to the most zoomed out view. The whole map is displayed + // in the automap + if ( automapZoom < 1 ) { + automapZoom = 1; + } + } + // recalculate the screen coordinates since the zoom changed + CG_TransformAutomapEntity(); +} +// END xkan, 9/19/2002 + +void CG_ParseMapEntity( int* mapEntityCount, int* offset, team_t team ) { + mapEntityData_t* mEnt = &mapEntities[( *mapEntityCount )]; + char buffer[16]; + + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->type = atoi( buffer ); + + switch ( mEnt->type ) { + case ME_CONSTRUCT: // Gordon: these ones don't need much info + case ME_DESTRUCT: + case ME_DESTRUCT_2: + case ME_COMMANDMAP_MARKER: + break; + + case ME_TANK: + case ME_TANK_DEAD: + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->x = atoi( buffer ) * 128; + + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->y = atoi( buffer ) * 128; + + if ( cgs.ccLayers ) { + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->z = atoi( buffer ) * 128; + } + break; + + default: + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->x = atoi( buffer ) * 128; + + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->y = atoi( buffer ) * 128; + + if ( cgs.ccLayers ) { + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->z = atoi( buffer ) * 128; + } + + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->yaw = atoi( buffer ); + break; + } + + trap_Argv( ( *offset )++, buffer, 16 ); + mEnt->data = atoi( buffer ); + + mEnt->transformed[0] = ( mEnt->x - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] * CC_2D_W; + mEnt->transformed[1] = ( mEnt->y - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] * CC_2D_H; + + mEnt->team = team; + + ( *mapEntityCount )++; +} + +/* +======================= +CG_ParseMapEntityInfo +======================= +*/ +void CG_ParseMapEntityInfo( int axis_number, int allied_number ) { + int i, offset; + + mapEntityCount = 0; + mapEntityTime = cg.time; + + offset = 3; + + for ( i = 0; i < axis_number; i++ ) { + CG_ParseMapEntity( &mapEntityCount, &offset, TEAM_AXIS ); + } + + for ( i = 0; i < allied_number; i++ ) { + CG_ParseMapEntity( &mapEntityCount, &offset, TEAM_ALLIES ); + } + + CG_TransformAutomapEntity(); +} + +static qboolean gridInitDone = qfalse; +static vec2_t gridStartCoord, gridStep; + +static void CG_DrawGrid( float x, float y, float w, float h, mapScissor_t *scissor ) { + vec2_t step; + vec2_t dim_x, dim_y; + vec4_t line; + float xscale, yscale; + float grid_x, grid_y; + vec2_t dist; + vec4_t gridColour; + + dist[0] = cg.mapcoordsMaxs[0] - cg.mapcoordsMins[0]; + dist[1] = cg.mapcoordsMaxs[1] - cg.mapcoordsMins[1]; + + if ( !gridInitDone ) { + gridStep[0] = 1200.f; + gridStep[1] = 1200.f; + + // ensure minimal grid density + while ( ( cg.mapcoordsMaxs[0] - cg.mapcoordsMins[0] ) / gridStep[0] < 7 ) + gridStep[0] -= 50.f; + while ( ( cg.mapcoordsMins[1] - cg.mapcoordsMaxs[1] ) / gridStep[1] < 7 ) + gridStep[1] -= 50.f; + + gridStartCoord[0] = .5f * ( ( ( ( cg.mapcoordsMaxs[0] - cg.mapcoordsMins[0] ) / gridStep[0] ) - ( (int)( ( cg.mapcoordsMaxs[0] - cg.mapcoordsMins[0] ) / gridStep[0] ) ) ) * gridStep[0] ); + gridStartCoord[1] = .5f * ( ( ( ( cg.mapcoordsMins[1] - cg.mapcoordsMaxs[1] ) / gridStep[1] ) - ( (int)( ( cg.mapcoordsMins[1] - cg.mapcoordsMaxs[1] ) / gridStep[1] ) ) ) * gridStep[1] ); + + gridInitDone = qtrue; + } + + if ( scissor ) { + dim_x[0] = cg.mapcoordsMins[0]; + dim_x[1] = cg.mapcoordsMaxs[0]; + + dim_y[0] = cg.mapcoordsMaxs[1]; + dim_y[1] = cg.mapcoordsMins[1]; + + // transform + xscale = ( w * scissor->zoomFactor ) / dist[0]; + yscale = ( h * scissor->zoomFactor ) / -dist[1]; + + dim_x[0] = ( dim_x[0] - cg.mapcoordsMins[0] ) * xscale; + dim_x[1] = ( dim_x[1] - cg.mapcoordsMins[0] ) * xscale; + + dim_y[0] = ( dim_y[0] - cg.mapcoordsMaxs[1] ) * yscale; + dim_y[1] = ( dim_y[1] - cg.mapcoordsMaxs[1] ) * yscale; + + grid_x = ( ( gridStartCoord[0] / dist[0] ) * w * scissor->zoomFactor ) - scissor->tl[0]; + grid_y = ( ( -gridStartCoord[1] / dist[1] ) * h * scissor->zoomFactor ) - scissor->tl[1]; + + step[0] = gridStep[0] * xscale; + step[1] = gridStep[1] * yscale; + + // draw + Vector4Set( gridColour, clrBrownLine[0], clrBrownLine[1], clrBrownLine[2], .4f ); + trap_R_SetColor( gridColour ); + for ( ; grid_x < dim_x[1]; grid_x += step[0] ) + { + if ( grid_x < dim_x[0] ) { + continue; + } + + if ( grid_x > w ) { + break; + } + + if ( scissor->circular ) { + // clip line against circle + float xc, yc; + + line[0] = x + grid_x; + xc = line[0] >= x + .5f * w ? line[0] - ( x + .5f * w ) : ( x + .5f * w ) - line[0]; + yc = SQRTFAST( Square( .5f * w ) - Square( xc ) ); + line[1] = y + ( .5f * h ) - yc; + line[2] = 1.f; + line[3] = 2 * yc; + } else { + Vector4Set( line, x + grid_x, y + dim_y[0], 1.f, h ); + } + line[0] *= cgs.screenXScale; + line[1] *= cgs.screenYScale; + line[3] *= cgs.screenYScale; + trap_R_DrawStretchPic( line[0], line[1], line[2], line[3], 0, 0, 0, 1, cgs.media.whiteShader ); + } + + for ( ; grid_y < dim_y[1]; grid_y += step[1] ) + { + if ( grid_y < dim_y[0] ) { + continue; + } + + if ( grid_y > h ) { + break; + } + + if ( scissor->circular ) { + // clip line against circle + float xc, yc; + + line[1] = y + grid_y; + yc = line[1] >= y + .5f * h ? line[1] - ( y + .5f * h ) : ( y + .5f * h ) - line[1]; + xc = SQRTFAST( Square( .5f * h ) - Square( yc ) ); + line[0] = x + ( .5f * w ) - xc; + line[2] = 2 * xc; + line[3] = 1.f; + } else { + Vector4Set( line, x + dim_x[0], y + grid_y, w, 1 ); + } + line[0] *= cgs.screenXScale; + line[1] *= cgs.screenYScale; + line[2] *= cgs.screenXScale; + trap_R_DrawStretchPic( line[0], line[1], line[2], line[3], 0, 0, 0, 1, cgs.media.whiteShader ); + } + trap_R_SetColor( NULL ); + } else { + char coord_char[3], coord_int; + float text_width, text_height; + vec2_t textOrigin; + + dim_x[0] = cg.mapcoordsMins[0]; + dim_x[1] = cg.mapcoordsMaxs[0]; + + dim_y[0] = cg.mapcoordsMaxs[1]; + dim_y[1] = cg.mapcoordsMins[1]; + + // transform + xscale = w / dist[0]; + yscale = h / -dist[1]; + + dim_x[0] = ( dim_x[0] - cg.mapcoordsMins[0] ) * xscale; + dim_x[1] = ( dim_x[1] - cg.mapcoordsMins[0] ) * xscale; + + dim_y[0] = ( dim_y[0] - cg.mapcoordsMaxs[1] ) * yscale; + dim_y[1] = ( dim_y[1] - cg.mapcoordsMaxs[1] ) * yscale; + + grid_x = gridStartCoord[0] * xscale; + grid_y = gridStartCoord[1] * yscale; + + step[0] = gridStep[0] * xscale; + step[1] = gridStep[1] * yscale; + + // draw + textOrigin[0] = grid_x; + textOrigin[1] = grid_y; + + Vector4Set( gridColour, clrBrownLine[0], clrBrownLine[1], clrBrownLine[2], 1.f ); + + coord_char[1] = '\0'; + for ( coord_char[0] = ( 'A' - 1 ); grid_x < dim_x[1]; grid_x += step[0], coord_char[0]++ ) + { + if ( coord_char[0] >= 'A' ) { + text_width = CG_Text_Width_Ext( coord_char, 0.2f, 0, &cgs.media.limboFont2 ); + text_height = CG_Text_Height_Ext( coord_char, 0.2f, 0, &cgs.media.limboFont2 ); + CG_Text_Paint_Ext( ( x + grid_x ) - ( .5f * step[0] ) - ( .5f * text_width ), y + dim_y[0] + textOrigin[1] + 1.5f * text_height, 0.2f, 0.2f, colorBlack, coord_char, 0, 0, 0, &cgs.media.limboFont2 ); + } + trap_R_SetColor( gridColour ); + + Vector4Set( line, x + grid_x, y + dim_y[0], 1, dim_x[1] - dim_x[0] ); + line[0] *= cgs.screenXScale; + line[1] *= cgs.screenYScale; + line[3] *= cgs.screenYScale; + trap_R_DrawStretchPic( line[0], line[1], line[2], line[3], 0, 0, 0, 1, cgs.media.whiteShader ); + } + + for ( coord_int = -1; grid_y < dim_y[1]; grid_y += step[1], coord_int++ ) + { + if ( coord_int >= 0 ) { + Com_sprintf( coord_char, sizeof( coord_char ), "%i", coord_int ); + text_width = CG_Text_Width_Ext( "0", 0.2f, 0, &cgs.media.limboFont2 ); + text_height = CG_Text_Height_Ext( coord_char, 0.2f, 0, &cgs.media.limboFont2 ); + CG_Text_Paint_Ext( x + dim_x[0] + textOrigin[0] + .5f * text_width, ( y + grid_y ) - ( .5f * step[1] ) + ( .5f * text_height ), 0.2f, 0.2f, colorBlack, coord_char, 0, 0, 0, &cgs.media.limboFont2 ); + } + trap_R_SetColor( gridColour ); + + Vector4Set( line, x + dim_x[0], y + grid_y, dim_y[1] - dim_y[0], 1 ); + line[0] *= cgs.screenXScale; + line[1] *= cgs.screenYScale; + line[2] *= cgs.screenXScale; + trap_R_DrawStretchPic( line[0], line[1], line[2], line[3], 0, 0, 0, 1, cgs.media.whiteShader ); + } + trap_R_SetColor( NULL ); + } +} + + +#define COMMANDMAP_PLAYER_ICON_SIZE 6 +#define AUTOMAP_PLAYER_ICON_SIZE 5 + +// xkan: extracted from CG_DrawCommandMap. +// drawingCommandMap - qfalse: command map; qtrue: auto map (upper left in main game view) + +void CG_DrawMapEntity( mapEntityData_t *mEnt, float x, float y, float w, float h, int mEntFilter, mapScissor_t *scissor, qboolean interactive, snapshot_t* snap, int icon_size ) { + int j = 1; + qhandle_t pic; + clientInfo_t* ci; + bg_playerclass_t* classInfo; + centity_t *cent; + const char* name; + vec4_t c_clr = {1.f, 1.f, 1.f, 1.f}; + vec2_t icon_extends, icon_pos, string_pos; + int customimage = 0; + oidInfo_t* oidInfo = NULL; + + switch ( mEnt->type ) { + case ME_PLAYER_DISGUISED: + case ME_PLAYER_REVIVE: + case ME_PLAYER: + ci = &cgs.clientinfo[mEnt->data]; + if ( !ci->infoValid ) { + return; + } + + if ( ci->team == TEAM_AXIS ) { + if ( mEntFilter & CC_FILTER_AXIS ) { + return; + } + } else if ( ci->team == TEAM_ALLIES ) { + if ( mEntFilter & CC_FILTER_ALLIES ) { + return; + } + } else { + return; + } + + cent = &cg_entities[mEnt->data]; + + if ( mEnt->type == ME_PLAYER_DISGUISED && !( cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) ) { + return; + } + + classInfo = CG_PlayerClassForClientinfo( ci, cent ); + + // For these, if availaible, ignore the coordinate data and grab the most up to date pvs data + if ( cent - cg_entities == cg.clientNum ) { + if ( !scissor ) { + mEnt->transformed[0] = ( ( cg.predictedPlayerEntity.lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w; + mEnt->transformed[1] = ( ( cg.predictedPlayerEntity.lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h; + } else { + mEnt->automapTransformed[0] = ( ( cg.predictedPlayerEntity.lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * scissor->zoomFactor; + mEnt->automapTransformed[1] = ( ( cg.predictedPlayerEntity.lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * scissor->zoomFactor; + } + + mEnt->yaw = cg.predictedPlayerState.viewangles[YAW]; + } else if ( ci->team == snap->ps.persistant[PERS_TEAM] && cent->currentValid ) { + if ( !scissor ) { + mEnt->transformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w; + mEnt->transformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h; + } else { + mEnt->automapTransformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * scissor->zoomFactor; + mEnt->automapTransformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * scissor->zoomFactor; + } + + mEnt->yaw = cent->lerpAngles[YAW]; + } else { + // Gordon: only see revivables for own team, duh :) + if ( mEnt->type == ME_PLAYER_REVIVE ) { + return; + } + } + + + // now check to see if the entity is within our clip region + if ( scissor && CG_ScissorEntIsCulled( mEnt, scissor ) ) { + return; + } + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( mEnt->z ) != cgs.ccSelectedLayer ) { + return; + } + } + + if ( scissor ) { + icon_pos[0] = mEnt->automapTransformed[0] - scissor->tl[0] + x - icon_size; + icon_pos[1] = mEnt->automapTransformed[1] - scissor->tl[1] + y - icon_size; + } else { + icon_pos[0] = x + mEnt->transformed[0] - icon_size; + icon_pos[1] = y + mEnt->transformed[1] - icon_size; + string_pos[0] = x + mEnt->transformed[0]; + string_pos[1] = y + mEnt->transformed[1] + icon_size; + } + + icon_extends[0] = 2 * icon_size; + icon_extends[1] = 2 * icon_size; + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } + + if ( mEnt->type == ME_PLAYER_REVIVE ) { + float msec; + vec4_t reviveClr = { 1.f, 1.f, 1.f, 1.f }; + + if ( cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_AXIS ) { + msec = ( cg_redlimbotime.integer - ( cg.time % cg_redlimbotime.integer ) ) / (float)cg_redlimbotime.integer; + } else if ( cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ) { + msec = ( cg_bluelimbotime.integer - ( cg.time % cg_bluelimbotime.integer ) ) / (float)cg_bluelimbotime.integer; + } else { + msec = 0; + } + + reviveClr[3] = .5f + .5f * ( ( sin( sqrt( msec ) * 25 * 2 * M_PI ) + 1 ) * .5f ); + + trap_R_SetColor( reviveClr ); + CG_DrawPic( icon_pos[0] + 3, icon_pos[1] + 3, icon_extends[0] - 3, icon_extends[1] - 3, cgs.media.medicIcon ); + trap_R_SetColor( NULL ); + } else { + if ( cg.clientNum == mEnt->data ) { + if ( ci->ccSelected ) { + trap_R_SetColor( colorRed ); + } else { + trap_R_SetColor( colorYellow ); + } + + CG_DrawPic( icon_pos[0], icon_pos[1], icon_extends[0], icon_extends[1], cgs.media.ccPlayerHighlight ); + trap_R_SetColor( NULL ); + + if ( cg.predictedPlayerEntity.voiceChatSpriteTime > cg.time ) { + CG_DrawPic( icon_pos[0] + 12, icon_pos[1], icon_extends[0] * 0.5f, icon_extends[1] * 0.5f, cg.predictedPlayerEntity.voiceChatSprite ); + } + } else if ( mEnt->type == ME_PLAYER_DISGUISED ) { + trap_R_SetColor( colorOrange ); + CG_DrawPic( icon_pos[0], icon_pos[1], icon_extends[0], icon_extends[1], cgs.media.ccPlayerHighlight ); + trap_R_SetColor( NULL ); + } else if ( /*!(cgs.ccFilter & CC_FILTER_BUDDIES) &&*/ CG_IsOnSameFireteam( cg.clientNum, mEnt->data ) ) { + if ( ci->ccSelected ) { + trap_R_SetColor( colorRed ); + } + + CG_DrawPic( icon_pos[0], icon_pos[1], icon_extends[0], icon_extends[1], cgs.media.ccPlayerHighlight ); + trap_R_SetColor( NULL ); + + if ( !scissor ) { + CG_Text_Paint_Ext( string_pos[0], string_pos[1], 0.2f, 0.2f, colorWhite, ci->name, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); +// CG_DrawStringExt_Shadow( string_pos[0], string_pos[1], ci->name, colorWhite, qfalse, 1, 8, 12, 0 ); + } + + if ( cent->voiceChatSpriteTime > cg.time ) { + CG_DrawPic( icon_pos[0] + 12, icon_pos[1], icon_extends[0] * 0.5f, icon_extends[1] * 0.5f, cent->voiceChatSprite ); + } + } else if ( ci->team == snap->ps.persistant[PERS_TEAM] ) { + if ( ci->ccSelected ) { + trap_R_SetColor( colorRed ); + CG_DrawPic( icon_pos[0], icon_pos[1], icon_extends[0], icon_extends[1], cgs.media.ccPlayerHighlight ); + trap_R_SetColor( NULL ); + } + + if ( cent->voiceChatSpriteTime > cg.time ) { + CG_DrawPic( icon_pos[0] + 12, icon_pos[1], icon_extends[0] * 0.5f, icon_extends[1] * 0.5f, cent->voiceChatSprite ); + } + } + + c_clr[3] = 1.0f; + + trap_R_SetColor( c_clr ); + CG_DrawPic( icon_pos[0], icon_pos[1], icon_extends[0], icon_extends[1], classInfo->icon ); + + CG_DrawRotatedPic( icon_pos[0] - 1, icon_pos[1] - 1, icon_extends[0] + 2, icon_extends[1] + 2, classInfo->arrow, ( 0.5 - ( mEnt->yaw - 180.f ) / 360.f ) ); + trap_R_SetColor( NULL ); + } + return; + case ME_CONSTRUCT: + case ME_DESTRUCT: + case ME_DESTRUCT_2: + case ME_TANK: + case ME_TANK_DEAD: + case ME_COMMANDMAP_MARKER: + cent = NULL; + if ( mEnt->type == ME_TANK || mEnt->type == ME_TANK_DEAD ) { + oidInfo = &cgs.oidInfo[ mEnt->data ]; + + for ( j = 0; j < cg.snap->numEntities; j++ ) { + if ( cg.snap->entities[j].eType == ET_OID_TRIGGER && cg.snap->entities[j].teamNum == mEnt->data ) { + cent = &cg_entities[cg.snap->entities[j].number]; + if ( !scissor ) { + mEnt->transformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w; + mEnt->transformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h; + } else { + mEnt->automapTransformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * scissor->zoomFactor; + mEnt->automapTransformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * scissor->zoomFactor; + } + break; + } + } + } else if ( mEnt->type == ME_CONSTRUCT || mEnt->type == ME_DESTRUCT || mEnt->type == ME_DESTRUCT_2 ) { + cent = &cg_entities[mEnt->data]; + + oidInfo = &cgs.oidInfo[ cent->currentState.modelindex2 ]; + + if ( !scissor ) { + mEnt->transformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w; + mEnt->transformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h; + } else { + mEnt->automapTransformed[0] = ( ( cent->lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * scissor->zoomFactor; + mEnt->automapTransformed[1] = ( ( cent->lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * scissor->zoomFactor; + } + } else if ( mEnt->type == ME_COMMANDMAP_MARKER ) { + oidInfo = &cgs.oidInfo[ mEnt->data ]; + + if ( !scissor ) { + mEnt->transformed[0] = ( ( oidInfo->origin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w; + mEnt->transformed[1] = ( ( oidInfo->origin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h; + } else { + mEnt->automapTransformed[0] = ( ( oidInfo->origin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * scissor->zoomFactor; + mEnt->automapTransformed[1] = ( ( oidInfo->origin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * scissor->zoomFactor; + } + } + + // now check to see if the entity is within our clip region + if ( scissor && CG_ScissorEntIsCulled( mEnt, scissor ) ) { + return; + } + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( mEnt->z ) != cgs.ccSelectedLayer ) { + return; + } + } + + if ( oidInfo ) { + customimage = mEnt->team == TEAM_AXIS ? oidInfo->customimageaxis : oidInfo->customimageallies; + } + +/* if((mEnt->yaw & 0xFF) & (1 << (atoi(CG_ConfigString(mEnt->team == TEAM_AXIS ? CS_MAIN_AXIS_OBJECTIVE : CS_MAIN_ALLIES_OBJECTIVE)) - 1))) { + trap_R_SetColor( colorYellow ); + }*/ + + if ( mEnt->type == ME_CONSTRUCT ) { + if ( mEntFilter & CC_FILTER_CONSTRUCTIONS ) { + return; + } + pic = mEnt->team == TEAM_AXIS ? cgs.media.ccConstructIcon[0] : cgs.media.ccConstructIcon[1]; + } else if ( mEnt->type == ME_TANK ) { + if ( mEntFilter & CC_FILTER_OBJECTIVES ) { + return; + } + pic = cgs.media.ccTankIcon; + } else if ( mEnt->type == ME_TANK_DEAD ) { + if ( mEntFilter & CC_FILTER_OBJECTIVES ) { + return; + } + pic = cgs.media.ccTankIcon; + trap_R_SetColor( colorRed ); + } else if ( mEnt->type == ME_COMMANDMAP_MARKER ) { + pic = 0; + } else if ( mEnt->type == ME_DESTRUCT_2 ) { + pic = 0; + } else { + if ( mEntFilter & CC_FILTER_DESTRUCTIONS ) { + return; + } + pic = mEnt->team == TEAM_AXIS ? cgs.media.ccDestructIcon[cent->currentState.effect1Time][0] : cgs.media.ccDestructIcon[cent->currentState.effect1Time][1]; + } + + { + int info = 0; + + if ( oidInfo ) { + info = oidInfo->spawnflags; + } + + if ( info & ( 1 << 4 ) ) { + if ( mEntFilter & CC_FILTER_OBJECTIVES ) { + return; + } + } + if ( info & ( 1 << 5 ) ) { + if ( mEntFilter & CC_FILTER_HACABINETS ) { + return; + } + } + if ( info & ( 1 << 6 ) ) { + if ( mEnt->type == ME_DESTRUCT_2 ) { + pic = mEnt->team == TEAM_AXIS ? cgs.media.ccCmdPost[0] : cgs.media.ccCmdPost[1]; + } + if ( mEntFilter & CC_FILTER_CMDPOST ) { + return; + } + } + } + + if ( customimage ) { + pic = customimage; + } + + if ( scissor ) { + icon_pos[0] = mEnt->automapTransformed[0] - scissor->tl[0] + x; + icon_pos[1] = mEnt->automapTransformed[1] - scissor->tl[1] + y; + } else { + icon_pos[0] = x + mEnt->transformed[0]; + icon_pos[1] = y + mEnt->transformed[1]; + } + +#define CONST_ICON_NORMAL_SIZE 32.f +#define CONST_ICON_EXPANDED_SIZE 48.f + + if ( interactive && !expanded && BG_RectContainsPoint( x + mEnt->transformed[0] - ( CONST_ICON_NORMAL_SIZE * 0.5f ), y + mEnt->transformed[1] - ( CONST_ICON_NORMAL_SIZE * 0.5f ), CONST_ICON_NORMAL_SIZE, CONST_ICON_NORMAL_SIZE, cgDC.cursorx, cgDC.cursory ) ) { + float w; + + icon_extends[0] = CONST_ICON_EXPANDED_SIZE; + icon_extends[1] = CONST_ICON_EXPANDED_SIZE; + if ( mEnt->type == ME_TANK_DEAD || mEnt->type == ME_TANK ) { + icon_extends[1] *= 0.5f; + } + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } else { + icon_extends[0] *= cgs.ccZoomFactor; + icon_extends[1] *= cgs.ccZoomFactor; + } + + CG_DrawPic( icon_pos[0] - ( icon_extends[0] * 0.5f ), icon_pos[1] - ( icon_extends[1] * 0.5f ), icon_extends[0], icon_extends[1], pic ); + + if ( oidInfo ) { + name = oidInfo->name; + } else { + name = va( "%i", j ); + } + + w = CG_Text_Width_Ext( name, 0.2f, 0, &cgs.media.limboFont2 ); + CG_CommandMap_SetHighlightText( name, icon_pos[0] - ( w * 0.5f ), icon_pos[1] - 8 ); + } else if ( interactive && ( mEnt->yaw & 0xFF ) & ( 1 << cgs.ccSelectedObjective ) ) { + float scalesize; + int time = cg.time % 1400; + + if ( time <= 700 ) { + scalesize = 12 * ( time ) / 700.f; + } else { + scalesize = 12 * ( 1 - ( ( time - 700 ) / 700.f ) ); + } + + icon_extends[0] = CONST_ICON_NORMAL_SIZE + scalesize; + icon_extends[1] = CONST_ICON_NORMAL_SIZE + scalesize; + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } else { + icon_extends[0] *= cgs.ccZoomFactor; + icon_extends[1] *= cgs.ccZoomFactor; + } + if ( mEnt->type == ME_TANK_DEAD || mEnt->type == ME_TANK ) { + icon_extends[1] *= 0.5f; + } + + CG_DrawPic( icon_pos[0] - ( icon_extends[0] * 0.5f ), icon_pos[1] - ( icon_extends[1] * 0.5f ), icon_extends[0], icon_extends[1], pic ); + } else { + icon_extends[0] = CONST_ICON_NORMAL_SIZE; + icon_extends[1] = CONST_ICON_NORMAL_SIZE; + if ( mEnt->type == ME_TANK_DEAD || mEnt->type == ME_TANK ) { + icon_extends[1] *= 0.5f; + } + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } else { + icon_extends[0] *= cgs.ccZoomFactor; + icon_extends[1] *= cgs.ccZoomFactor; + } + + CG_DrawPic( icon_pos[0] - ( icon_extends[0] * 0.5f ), icon_pos[1] - ( icon_extends[1] * 0.5f ), icon_extends[0], icon_extends[1], pic ); + } + trap_R_SetColor( NULL ); + return; + case ME_LANDMINE: +/* if(mEntFilter & CC_FILTER_LANDMINES) { + continue; + }*/ + + // now check to see if the entity is within our clip region + if ( scissor && CG_ScissorEntIsCulled( mEnt, scissor ) ) { + return; + } + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( mEnt->z ) != cgs.ccSelectedLayer ) { + return; + } + } + + if ( mEnt->data == TEAM_AXIS ) { + pic = cgs.media.commandCentreAxisMineShader; + } else { // TEAM_ALLIES + pic = cgs.media.commandCentreAlliedMineShader; + } + + c_clr[3] = 1.0f; + + if ( scissor ) { + icon_pos[0] = mEnt->automapTransformed[0] - scissor->tl[0] + x; + icon_pos[1] = mEnt->automapTransformed[1] - scissor->tl[1] + y; + } else { + icon_pos[0] = x + mEnt->transformed[0]; + icon_pos[1] = y + mEnt->transformed[1]; + } + + icon_extends[0] = 12; + icon_extends[1] = 12; + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } else { + icon_extends[0] *= cgs.ccZoomFactor; + icon_extends[1] *= cgs.ccZoomFactor; + } + + trap_R_SetColor( c_clr ); + CG_DrawPic( icon_pos[0] - icon_extends[0] * 0.5f, icon_pos[1] - icon_extends[1] * 0.5f, icon_extends[0], icon_extends[1], pic ); + trap_R_SetColor( NULL ); + + j++; + return; + default: + return; + } +} + +void CG_DrawMap( float x, float y, float w, float h, int mEntFilter, mapScissor_t *scissor, qboolean interactive, float alpha, qboolean borderblend ) { + int i /*, j = 1*/; + snapshot_t* snap; +// vec4_t c_clr = {1.f, 1.f, 1.f, 1.f}; + mapEntityData_t* mEnt = &mapEntities[0]; + int icon_size; + int exspawn; + + expanded = qfalse; + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + if ( scissor ) { + float s0, s1, t0, t1; + + icon_size = AUTOMAP_PLAYER_ICON_SIZE; + + if ( scissor->br[0] >= scissor->tl[0] ) { + float sc_x, sc_y, sc_w, sc_h; + + sc_x = x; + sc_y = y; + sc_w = w; + sc_h = h; + + CG_DrawPic( sc_x, sc_y, sc_w, sc_h, cgs.media.commandCentreAutomapMaskShader ); + + s0 = ( scissor->tl[0] ) / ( w * scissor->zoomFactor ); + s1 = ( scissor->br[0] ) / ( w * scissor->zoomFactor ); + t0 = ( scissor->tl[1] ) / ( h * scissor->zoomFactor ); + t1 = ( scissor->br[1] ) / ( h * scissor->zoomFactor ); + + CG_AdjustFrom640( &sc_x, &sc_y, &sc_w, &sc_h ); + if ( cgs.ccLayers ) { + trap_R_DrawStretchPic( sc_x, sc_y, sc_w, sc_h, s0, t0, s1, t1, cgs.media.commandCentreAutomapShader[cgs.ccSelectedLayer] ); + } else { + trap_R_DrawStretchPic( sc_x, sc_y, sc_w, sc_h, s0, t0, s1, t1, cgs.media.commandCentreAutomapShader[0] ); + } + trap_R_DrawStretchPic( 0, 0, 0, 0, 0, 0, 0, 0, cgs.media.whiteShader ); // HACK : the code above seems to do weird things to + // the next trap_R_DrawStretchPic issued. This works + // around this. + } + + // Draw the grid + CG_DrawGrid( x, y, w, h, scissor ); + } else { + icon_size = COMMANDMAP_PLAYER_ICON_SIZE; + + { + vec4_t color; + Vector4Set( color, 1.f, 1.f, 1.f, alpha ); + trap_R_SetColor( color ); + if ( cgs.ccLayers ) { + CG_DrawPic( x, y, w, h, cgs.media.commandCentreMapShaderTrans[cgs.ccSelectedLayer] ); + } else { + CG_DrawPic( x, y, w, h, cgs.media.commandCentreMapShaderTrans[0] ); + } + trap_R_SetColor( NULL ); + } + + // Draw the grid + CG_DrawGrid( x, y, w, h, NULL ); + } + + if ( borderblend ) { + vec4_t clr = { 0.f, 0.f, 0.f, 0.75f }; + trap_R_SetColor( clr ); + CG_DrawPic( x, y, w, h, cgs.media.limboBlendThingy ); + trap_R_SetColor( NULL ); + } + + exspawn = CG_DrawSpawnPointInfo( x, y, w, h, qfalse, scissor, -1 ); + + for ( i = 0, mEnt = &mapEntities[0]; i < mapEntityCount; i++, mEnt++ ) { + if ( mEnt->team != CG_LimboPanel_GetRealTeam() ) { + continue; + } + + if ( mEnt->type == ME_PLAYER || + mEnt->type == ME_PLAYER_DISGUISED || + mEnt->type == ME_PLAYER_REVIVE ) { + continue; + } + + CG_DrawMapEntity( mEnt, x, y, w, h, mEntFilter, scissor, interactive, snap, icon_size ); + } + + CG_DrawSpawnPointInfo( x, y, w, h, qtrue, scissor, exspawn ); + + CG_DrawMortarMarker( x, y, w, h, qtrue, scissor, exspawn ); + + for ( i = 0, mEnt = &mapEntities[0]; i < mapEntityCount; i++, mEnt++ ) { + if ( mEnt->team != CG_LimboPanel_GetRealTeam() ) { + continue; + } + + if ( mEnt->type != ME_PLAYER && + mEnt->type != ME_PLAYER_DISGUISED && + mEnt->type != ME_PLAYER_REVIVE ) { + continue; + } + + CG_DrawMapEntity( mEnt, x, y, w, h, mEntFilter, scissor, interactive, snap, icon_size ); + } +} + +void CG_DrawExpandedAutoMap( void ) { + float x, y, w, h; + float b_x, b_y, b_w, b_h; + float s1, t1, s2, t2; +// vec4_t colour = { 1.f, 1.f, 1.f, .5f }; + + x = SCREEN_WIDTH + 10.f; + y = 20.f; + + w = CC_2D_W; + h = CC_2D_H; + + + if ( cgs.autoMapExpanded ) { + if ( cg.time - cgs.autoMapExpandTime < 250.f ) { + x -= ( ( cg.time - cgs.autoMapExpandTime ) / 250.f ) * ( w + 30.f ); + } else { + x = SCREEN_WIDTH - w - 20.f; + } + } else { + if ( cg.time - cgs.autoMapExpandTime < 250.f ) { + x = ( SCREEN_WIDTH - w - 20.f ) + ( ( cg.time - cgs.autoMapExpandTime ) / 250.f ) * ( w + 30.f ); + } else { + return; + } + } + + CG_DrawMap( x, y, w, h, cgs.ccFilter, NULL, qfalse, .7f, qfalse ); + + // Draw the border + + // top left + s1 = 0; + t1 = 0; + s2 = 1; + t2 = 1; + b_x = x - 8; + b_y = y - 8; + b_w = 8; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapCornerShader ); + + // top + s2 = w / 256.f; + b_x = x; + b_y = y - 8; + b_w = w; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapBorderShader ); + + // top right + s1 = 1; + t1 = 0; + s2 = 0; + t2 = 1; + b_x = x + w; + b_y = y - 8; + b_w = 8; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapCornerShader ); + + // right + s1 = 1; + t1 = h / 256.f; + s2 = 0; + t2 = 0; + b_x = x + w; + b_y = y; + b_w = 8; + b_h = h; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapBorder2Shader ); + + // bottom right + s1 = 1; + t1 = 1; + s2 = 0; + t2 = 0; + b_x = x + w; + b_y = y + h; + b_w = 8; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapCornerShader ); + + // bottom + s1 = w / 256.f; + b_x = x; + b_y = y + h; + b_w = w; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapBorderShader ); + + // bottom left + s1 = 0; + t1 = 1; + s2 = 1; + t2 = 0; + b_x = x - 8; + b_y = y + h; + b_w = 8; + b_h = 8; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapCornerShader ); + + // left + s1 = 0; + t1 = 0; + s2 = 1; + t2 = h / 256.f; + b_x = x - 8; + b_y = y; + b_w = 8; + b_h = h; + CG_AdjustFrom640( &b_x, &b_y, &b_w, &b_h ); + trap_R_DrawStretchPic( b_x, b_y, b_w, b_h, s1, t1, s2, t2, cgs.media.commandCentreAutomapBorder2Shader ); +} + +void CG_DrawAutoMap( void ) { + float x, y, w, h; + mapScissor_t mapScissor; + vec2_t automapTransformed; + + memset( &mapScissor, 0, sizeof( mapScissor ) ); + + if ( cgs.ccLayers ) { + cgs.ccSelectedLayer = CG_CurLayerForZ( (int)cg.predictedPlayerEntity.lerpOrigin[2] ); + } + + x = 520; + y = 20; + w = 100; + h = 100; + + if ( cgs.autoMapExpanded ) { + if ( cg.time - cgs.autoMapExpandTime < 100.f ) { + y -= ( ( cg.time - cgs.autoMapExpandTime ) / 100.f ) * 128.f; + CG_DrawExpandedAutoMap(); + } else { + //y -= 128.f; + CG_DrawExpandedAutoMap(); + return; + } + } else { + if ( cg.time - cgs.autoMapExpandTime <= 150.f ) { + //y -= 128.f; + CG_DrawExpandedAutoMap(); + return; + } else if ( ( cg.time - cgs.autoMapExpandTime > 150.f ) && ( cg.time - cgs.autoMapExpandTime < 250.f ) ) { + y = ( y - 128.f ) + ( ( cg.time - cgs.autoMapExpandTime - 150.f ) / 100.f ) * 128.f; + CG_DrawExpandedAutoMap(); + } + } + + mapScissor.circular = qtrue; + + + mapScissor.zoomFactor = automapZoom; + + mapScissor.tl[0] = mapScissor.tl[1] = 0; + mapScissor.br[0] = mapScissor.br[1] = -1; + + automapTransformed[0] = ( ( cg.predictedPlayerEntity.lerpOrigin[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * w * mapScissor.zoomFactor; + automapTransformed[1] = ( ( cg.predictedPlayerEntity.lerpOrigin[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * h * mapScissor.zoomFactor; + + // update clip region (for next drawing). clip region has a size kAutomap_width x kAutomap_height + // and it is after zooming is accounted for. + // + // first try to center the clip region around the player. then make sure the region + // stays within the world map. + mapScissor.tl[0] = automapTransformed[0] - w / 2; + if ( mapScissor.tl[0] < 0 ) { + mapScissor.tl[0] = 0; + } + mapScissor.br[0] = mapScissor.tl[0] + w; + if ( mapScissor.br[0] > ( w * mapScissor.zoomFactor ) ) { + mapScissor.br[0] = w * mapScissor.zoomFactor; + mapScissor.tl[0] = mapScissor.br[0] - w; + } + + mapScissor.tl[1] = automapTransformed[1] - h / 2; + if ( mapScissor.tl[1] < 0 ) { + mapScissor.tl[1] = 0; + } + mapScissor.br[1] = mapScissor.tl[1] + h; + if ( mapScissor.br[1] > ( h * mapScissor.zoomFactor ) ) { + mapScissor.br[1] = h * mapScissor.zoomFactor; + mapScissor.tl[1] = mapScissor.br[1] - h; + } + + CG_DrawMap( x, y, w, h, cgs.ccFilter, &mapScissor, qfalse, 1.f, qfalse ); +} + +/*void CG_DrawWaypointInfo( int x, int y, int w, int h ) { + snapshot_t *snap; + vec2_t pos; + int i; + + if( cgs.ccFilter & CC_FILTER_WAYPOINTS ) { + return; + } + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + for ( i = 0; i < snap->numEntities; i++ ) { + entityState_t *ent = &snap->entities[i]; + + if ( ent->eType != ET_WAYPOINT ) + continue; + + // see if the waypoint owner is someone that you accept waypoints from + if(!CG_IsOnSameFireteam( cg.clientNum, ent->clientNum )) { + continue; + } + + pos[0] = x + ((ent->pos.trBase[0] - cg.mapcoordsMins[0]) * cg.mapcoordsScale[0]) * w; + pos[1] = y + ((ent->pos.trBase[1] - cg.mapcoordsMins[1]) * cg.mapcoordsScale[1]) * h; + + switch( ent->frame ) { + case WAYP_ATTACK: CG_DrawPic( pos[0] - 6, pos[1] - 6, 12, 12, cgs.media.waypointAttackShader ); break; + case WAYP_DEFEND: CG_DrawPic( pos[0] - 6, pos[1] - 6, 12, 12, cgs.media.waypointDefendShader ); break; + case WAYP_REGROUP: CG_DrawPic( pos[0] - 6, pos[1] - 6, 12, 12, cgs.media.waypointRegroupShader ); break; + } + } +}*/ + +int CG_DrawSpawnPointInfo( int px, int py, int pw, int ph, qboolean draw, mapScissor_t *scissor, int expand ) { + int i; + char buffer[64]; + vec2_t point; + int e = -1; + vec2_t icon_extends; + + team_t team = CG_LimboPanel_GetRealTeam(); + + if ( cgs.ccFilter & CC_FILTER_SPAWNS ) { + return -1; + } + +#define FLAGSIZE_EXPANDED 48.f +#define FLAGSIZE_NORMAL 32.f +#define FLAG_LEFTFRAC ( 25 / 128.f ) +#define FLAG_TOPFRAC ( 95 / 128.f ) +#define SPAWN_SIZEUPTIME 1000.f + for ( i = 1; i < cg.spawnCount; i++ ) { + float changetime = 0; + if ( cg.spawnTeams_changeTime[i] ) { + changetime = ( cg.time - cg.spawnTeams_changeTime[i] ); + if ( changetime > SPAWN_SIZEUPTIME || changetime < 0 ) { + changetime = cg.spawnTeams_changeTime[i] = 0; + } + } + + // rain - added parens around ambiguity + if ( ( ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR ) && + ( cg.spawnTeams[i] != team ) ) || + ( ( cg.spawnTeams[i] & 256 ) && !changetime ) ) { + + continue; + } + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( (int)cg.spawnCoords[i][2] ) != cgs.ccSelectedLayer ) { + break; + } + } + + if ( scissor ) { + point[0] = ( ( cg.spawnCoordsUntransformed[i][0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw * scissor->zoomFactor; + point[1] = ( ( cg.spawnCoordsUntransformed[i][1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph * scissor->zoomFactor; + } else { + point[0] = px + ( ( ( cg.spawnCoordsUntransformed[i][0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw ); + point[1] = py + ( ( ( cg.spawnCoordsUntransformed[i][1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph ); + } + + if ( scissor && CG_ScissorPointIsCulled( point, scissor ) ) { + continue; + } + + if ( scissor ) { + point[0] += px - scissor->tl[0]; + point[1] += py - scissor->tl[1]; + } + + icon_extends[0] = FLAGSIZE_NORMAL; + icon_extends[1] = FLAGSIZE_NORMAL; + if ( scissor ) { + icon_extends[0] *= ( scissor->zoomFactor / 5.159 ); + icon_extends[1] *= ( scissor->zoomFactor / 5.159 ); + } else { + icon_extends[0] *= cgs.ccZoomFactor; + icon_extends[1] *= cgs.ccZoomFactor; + } + + point[0] -= ( icon_extends[0] * ( 39 / 128.f ) ); + point[1] += ( icon_extends[1] * ( 31 / 128.f ) ); + + if ( changetime ) { + if ( draw ) { + float size; + + if ( cg.spawnTeams[i] == team ) { + size = 20 * ( changetime / SPAWN_SIZEUPTIME ); + } else { + size = 20 * ( 1 - ( changetime / SPAWN_SIZEUPTIME ) ); + } + + if ( scissor ) { + size *= ( scissor->zoomFactor / 5.159 ); + } else { + size *= cgs.ccZoomFactor; + } + + CG_DrawPic( point[0] - FLAG_LEFTFRAC * size, point[1] - FLAG_TOPFRAC * size, size, size, cgs.media.commandCentreSpawnShader[ cg.spawnTeams[i] == TEAM_AXIS ? 0 : 1 ] ); + } + } else if ( ( draw && i == expand ) || ( !expanded && BG_RectContainsPoint( point[0] - FLAGSIZE_NORMAL * 0.5f, point[1] - FLAGSIZE_NORMAL * 0.5f, FLAGSIZE_NORMAL, FLAGSIZE_NORMAL, cgDC.cursorx, cgDC.cursory ) ) ) { + if ( draw ) { + float size = FLAGSIZE_EXPANDED; + if ( scissor ) { + size *= ( scissor->zoomFactor / 5.159 ); + } else { + size *= cgs.ccZoomFactor; + } + + CG_DrawPic( point[0] - FLAG_LEFTFRAC * size, point[1] - FLAG_TOPFRAC * size, size, size, cgs.media.commandCentreSpawnShader[ cg.spawnTeams[i] == TEAM_AXIS ? 0 : 1 ] ); + } else { + if ( !scissor ) { + float w; + + Com_sprintf( buffer, sizeof( buffer ), "%s (Troops: %i)", cg.spawnPoints[i], cg.spawnPlayerCounts[i] ); + w = CG_Text_Width_Ext( buffer, 0.2f, 0, &cgs.media.limboFont2 ); + CG_CommandMap_SetHighlightText( buffer, point[0] - ( w * 0.5f ), point[1] - 8 ); + } + + e = i; + } + } else { + if ( draw ) { + float size = FLAGSIZE_NORMAL; + if ( scissor ) { + size *= ( scissor->zoomFactor / 5.159 ); + } else { + size *= cgs.ccZoomFactor; + } + + CG_DrawPic( point[0] - FLAG_LEFTFRAC * size, point[1] - FLAG_TOPFRAC * size, size, size, cgs.media.commandCentreSpawnShader[ cg.spawnTeams[i] == TEAM_AXIS ? 0 : 1 ] ); + + if ( !scissor ) { + Com_sprintf( buffer, sizeof( buffer ), "(Troops: %i)", cg.spawnPlayerCounts[i] ); + CG_Text_Paint_Ext( point[0] + FLAGSIZE_NORMAL * 0.25f, point[1], 0.2f, 0.2f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } + } + } + } + + return e; +} + +void CG_DrawMortarMarker( int px, int py, int pw, int ph, qboolean draw, mapScissor_t *scissor, int expand ) { + if ( cg.lastFiredWeapon == WP_MORTAR_SET && cg.mortarImpactTime >= 0 ) { + if ( cg.snap->ps.weapon != WP_MORTAR_SET ) { + cg.mortarImpactTime = 0; + } else { + + //vec4_t colour = { .77f, .1f, .1f, 1.f }; + vec4_t colour = { 1.f, 1.f, 1.f, 1.f }; + vec3_t point; +// int fadeTime = 0; // cg.time - (cg.predictedPlayerEntity.muzzleFlashTime + 5000); + + if ( scissor ) { + point[0] = ( ( cg.mortarImpactPos[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw * scissor->zoomFactor; + point[1] = ( ( cg.mortarImpactPos[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph * scissor->zoomFactor; + } else { + point[0] = px + ( ( ( cg.mortarImpactPos[0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw ); + point[1] = py + ( ( ( cg.mortarImpactPos[1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph ); + } + + // rain - don't return if the marker is culled, just don't + // draw it. + if ( !( scissor && CG_ScissorPointIsCulled( point, scissor ) ) ) { + if ( scissor ) { + point[0] += px - scissor->tl[0]; + point[1] += py - scissor->tl[1]; + } + + if ( cg.mortarImpactOutOfMap ) { + if ( !scissor ) { + // near the edge of the map, fit into it + if ( point[0] + 8.f > ( px + pw ) ) { + point[0] -= 8.f; + } else if ( point[0] - 8.f < px ) { + point[0] += 8.f; + } + + if ( point[1] + 8.f > ( py + ph ) ) { + point[1] -= 8.f; + } else if ( point[1] - 8.f < py ) { + point[1] += 8.f; + } + } + colour[3] = .5f; + } + + trap_R_SetColor( colour ); + CG_DrawRotatedPic( point[0] - 8.f, point[1] - 8.f, 16, 16, cgs.media.ccMortarHit, .5f - ( cg.mortarFireAngles[YAW] /*- 180.f */ + 45.f ) / 360.f ); + trap_R_SetColor( NULL ); + } + } + } + + if ( COM_BitCheck( cg.snap->ps.weapons, WP_MORTAR_SET ) ) { + int i; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + //vec4_t colour = { .23f, 1.f, .23f, 1.f }; + vec4_t colour = { 1.f, 1.f, 1.f, 1.f }; + vec3_t point; + int fadeTime = cg.time - ( cg.artilleryRequestTime[i] + 25000 ); + + if ( fadeTime < 5000 ) { + if ( fadeTime > 0 ) { + colour[3] = 1.f - ( fadeTime / 5000.f ); + } + + if ( scissor ) { + point[0] = ( ( cg.artilleryRequestPos[i][0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw * scissor->zoomFactor; + point[1] = ( ( cg.artilleryRequestPos[i][1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph * scissor->zoomFactor; + } else { + point[0] = px + ( ( ( cg.artilleryRequestPos[i][0] - cg.mapcoordsMins[0] ) * cg.mapcoordsScale[0] ) * pw ); + point[1] = py + ( ( ( cg.artilleryRequestPos[i][1] - cg.mapcoordsMins[1] ) * cg.mapcoordsScale[1] ) * ph ); + } + + // rain - don't return if the marker is culled, just skip + // it (so we draw the rest, if any) + if ( scissor && CG_ScissorPointIsCulled( point, scissor ) ) { + continue; + } + + if ( scissor ) { + point[0] += px - scissor->tl[0]; + point[1] += py - scissor->tl[1]; + } + + trap_R_SetColor( colour ); + CG_DrawPic( point[0] - 8.f, point[1] - 8.f, 16, 16, cgs.media.ccMortarTarget ); + trap_R_SetColor( NULL ); + + //CG_FillRect( point[0] - 8.f, point[1] - 8.f, 16, 16, colour ); + } + } + } +} + +/*void CG_DrawCommandCentreLayers( void ) { + int x, y; + int i; + char *s; + + if( !cgs.ccLayers ) + return; + + x = CC_2D_X + CC_2D_W - 32; + y = CC_2D_Y + CC_2D_H - 32; + + for( i = 0; i < cgs.ccLayers; i++ ) { + if( i == cgs.ccSelectedLayer ) + CG_FillRect( x, y, 32, 32, clrBrownTextLt ); + else + CG_FillRect( x, y, 32, 32, clrBrownText ); + CG_DrawRect( x, y, 32, 32, 1, colorBlack ); + + if( i == 0 ) + s = "G"; + else + s = va( "%i", i ); + + CG_Text_Paint( x + 1 + ( ( 32 - CG_Text_Width( s, .5f, 0 ) ) * .5f ), + y + 16 +( ( 32 - CG_Text_Height( s, .5f, 0 ) ) *.5f ), + .5f, clrBrownTextDk, s, 0, 0, 0 ); + y -= 34; + } +}*/ + +mapEntityData_t* CG_ScanForCommandCentreEntity( void ) { + vec_t rangeSquared = Square( 1000 ); + int ent = 0; + mapEntityData_t* mEnt = &mapEntities[0]; + int i; + + if ( mapEntityCount <= 0 ) { + return NULL; + } + + for ( i = 0; i < mapEntityCount; i++, mEnt++ ) { + float rngSquared; + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( mEnt->z ) != cgs.ccSelectedLayer ) { + continue; + } + } + + rngSquared = Square( CC_2D_X + mEnt->transformed[0] - cgDC.cursorx ) + Square( CC_2D_Y + mEnt->transformed[1] - cgDC.cursory ); + + if ( i == 0 || rngSquared < rangeSquared ) { + rangeSquared = rngSquared; + ent = i; + } + } + + if ( rangeSquared < Square( 8 ) ) { + return &mapEntities[ent]; + } + return NULL; +} + +typedef enum { + MT_CONSTRUCT, + MT_CONSTRUCT_BARE, + MT_DESTRUCT, + MT_DESTRUCT_BARE, + MT_TANK_BARE, + MT_PLAYER, + MT_INFO +} menuType_t; + +qboolean CG_PlayerSelected( void ) { + snapshot_t* snap; + int i; + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( cgs.clientinfo[i].team == snap->ps.persistant[PERS_TEAM] ) { + if ( cgs.clientinfo[i].ccSelected ) { + return qtrue; + } + } + } + return qfalse; +} + +#define CC_MENU_WIDTH 150 + +qboolean CG_CommandCentreLayersClick( void ) { + int x, y; + int i; + + if ( !cgs.ccLayers ) { + return qfalse; + } + + x = CC_2D_X + CC_2D_W - 32; + y = CC_2D_Y + CC_2D_H - 32; + + for ( i = 0; i < cgs.ccLayers; i++ ) { + if ( BG_RectContainsPoint( x, y, 32, 32, cgDC.cursorx, cgDC.cursory ) ) { + cgs.ccSelectedLayer = i; + return qtrue; + } + y -= 34; + } + + return qfalse; +} + +qboolean CG_CommandCentreSpawnPointClick( void ) { + int i; + vec2_t point; + + if ( cgs.ccFilter & CC_FILTER_SPAWNS ) { + return qfalse; + } + + for ( i = 1; i < cg.spawnCount; i++ ) { + if ( ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR ) && cg.spawnTeams[i] && cg.spawnTeams[i] != CG_LimboPanel_GetRealTeam() ) { + continue; + } + + if ( cg.spawnTeams[i] & 256 ) { + continue; + } + + if ( cgs.ccLayers ) { + if ( CG_CurLayerForZ( (int)cg.spawnCoords[i][2] ) != cgs.ccSelectedLayer ) { + continue; + } + } + + point[0] = cg.spawnCoords[i][0]; + point[1] = cg.spawnCoords[i][1]; + + if ( BG_RectContainsPoint( point[0] - FLAGSIZE_NORMAL * 0.5f, point[1] - FLAGSIZE_NORMAL * 0.5f, FLAGSIZE_NORMAL, FLAGSIZE_NORMAL, cgDC.cursorx, cgDC.cursory ) ) { + trap_SendConsoleCommand( va( "setspawnpt %i\n", i ) ); + cg.selectedSpawnPoint = i; + cgs.ccRequestedObjective = -1; + return qtrue; + } + } + + return qfalse; +} + +void CG_CommandCentreClick( int key ) { + switch ( key ) { + case K_MOUSE1: + if ( CG_CommandCentreSpawnPointClick() ) { + return; + } + break; + } +} + +char cg_highlightText[256]; +rectDef_t cg_highlightTextRect; +void CG_CommandMap_SetHighlightText( const char* text, float x, float y ) { + Q_strncpyz( cg_highlightText, text, sizeof( cg_highlightText ) ); + cg_highlightTextRect.x = x; + cg_highlightTextRect.y = y; + expanded = qtrue; +} + +void CG_CommandMap_DrawHighlightText( void ) { + CG_Text_Paint_Ext( cg_highlightTextRect.x, cg_highlightTextRect.y, 0.25f, 0.25f, colorWhite, cg_highlightText, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + *cg_highlightText = '\0'; +} diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c new file mode 100644 index 0000000..7cb6bf4 --- /dev/null +++ b/src/cgame/cg_consolecmds.c @@ -0,0 +1,1176 @@ +/* +=========================================================================== + +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: cg_consolecmds.c + * + * desc: text commands typed in at the local console, or executed by a key binding + * +*/ + + +#include "cg_local.h" + +void CG_TargetCommand_f( void ) { + int targetNum; + char test[4]; + + targetNum = CG_CrosshairPlayer(); + if ( !targetNum ) { + return; + } + + trap_Argv( 1, test, 4 ); + trap_SendConsoleCommand( va( "gc %i %i", targetNum, atoi( test ) ) ); +} + +/* +============= +CG_Viewpos_f + +Debugging command to print the current position +============= +*/ +static void CG_Viewpos_f( void ) { + CG_Printf( "(%i %i %i) : %i\n", (int)cg.refdef.vieworg[0], + (int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2], + (int)cg.refdefViewAngles[YAW] ); +} + +void CG_LimboMenu_f( void ) { + if ( cg.showGameView ) { + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + CG_EventHandling( CGAME_EVENT_GAMEVIEW, qfalse ); + } +} + +static void CG_StatsDown_f( void ) { + if ( !cg.demoPlayback ) { + int i = ( cg.mvTotalClients > 0 ) ? ( cg.mvCurrentActive->mvInfo & MV_PID ) : cg.snap->ps.clientNum; + + if ( cg.mvTotalClients < 1 && cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + Pri( "You must be a player or following a player to use +stats\n" ); + return; + } + + if ( cgs.gamestats.show == SHOW_SHUTDOWN && cg.time < cgs.gamestats.fadeTime ) { + cgs.gamestats.fadeTime = 2 * cg.time + STATS_FADE_TIME - cgs.gamestats.fadeTime; + } else if ( cgs.gamestats.show != SHOW_ON ) { + cgs.gamestats.fadeTime = cg.time + STATS_FADE_TIME; + } + + cgs.gamestats.show = SHOW_ON; + + if ( cgs.gamestats.requestTime < cg.time ) { + cgs.gamestats.requestTime = cg.time + 2000; + trap_SendClientCommand( va( "sgstats %d", i ) ); + } + } +} + +static void CG_StatsUp_f( void ) { + if ( cgs.gamestats.show == SHOW_ON ) { + cgs.gamestats.show = SHOW_SHUTDOWN; + if ( cg.time < cgs.gamestats.fadeTime ) { + cgs.gamestats.fadeTime = 2 * cg.time + STATS_FADE_TIME - cgs.gamestats.fadeTime; + } else { + cgs.gamestats.fadeTime = cg.time + STATS_FADE_TIME; + } + } +} + +void CG_topshotsDown_f( void ) { + if ( !cg.demoPlayback ) { + if ( cgs.topshots.show == SHOW_SHUTDOWN && cg.time < cgs.topshots.fadeTime ) { + cgs.topshots.fadeTime = 2 * cg.time + STATS_FADE_TIME - cgs.topshots.fadeTime; + } else if ( cgs.topshots.show != SHOW_ON ) { + cgs.topshots.fadeTime = cg.time + STATS_FADE_TIME; + } + + cgs.topshots.show = SHOW_ON; + + if ( cgs.topshots.requestTime < cg.time ) { + cgs.topshots.requestTime = cg.time + 2000; + trap_SendClientCommand( "stshots" ); + } + } +} + +void CG_topshotsUp_f( void ) { + if ( cgs.topshots.show == SHOW_ON ) { + cgs.topshots.show = SHOW_SHUTDOWN; + if ( cg.time < cgs.topshots.fadeTime ) { + cgs.topshots.fadeTime = 2 * cg.time + STATS_FADE_TIME - cgs.topshots.fadeTime; + } else { + cgs.topshots.fadeTime = cg.time + STATS_FADE_TIME; + } + } +} + +void CG_ScoresDown_f( void ) { + if ( cg.scoresRequestTime + 2000 < cg.time ) { + // the scores are more than two seconds out of data, + // so request new ones + cg.scoresRequestTime = cg.time; + + // OSP - we get periodic score updates if we are merging clients + if ( !cg.demoPlayback && cg.mvTotalClients < 1 ) { + trap_SendClientCommand( "score" ); + } + + // leave the current scores up if they were already + // displayed, but if this is the first hit, clear them out + if ( !cg.showScores ) { + cg.showScores = qtrue; + if ( !cg.demoPlayback && cg.mvTotalClients < 1 ) { + cg.numScores = 0; + } + } + } else { + // show the cached contents even if they just pressed if it + // is within two seconds + cg.showScores = qtrue; + } +} + +void CG_ScoresUp_f( void ) { + if ( cg.showScores ) { + cg.showScores = qfalse; + cg.scoreFadeTime = cg.time; + } +} + +static void CG_LoadHud_f( void ) { +// String_Init(); +// Menu_Reset(); +// CG_LoadMenus("ui/hud.txt"); +} + +static void CG_LoadWeapons_f( void ) { + int i; + + for ( i = WP_KNIFE; i < WP_NUM_WEAPONS; i++ ) { + // DHM - Nerve :: Only register weapons we use in WolfMP + if ( BG_WeaponInWolfMP( i ) ) { + CG_RegisterWeapon( i, qtrue ); + } + } +} + +/* +static void CG_InventoryDown_f( void ) { + cg.showItems = qtrue; +} + +static void CG_InventoryUp_f( void ) { + cg.showItems = qfalse; + cg.itemFadeTime = cg.time; +} +*/ + +static void CG_TellTarget_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_CrosshairPlayer(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "tell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +static void CG_TellAttacker_f( void ) { + int clientNum; + char command[128]; + char message[128]; + + clientNum = CG_LastAttacker(); + if ( clientNum == -1 ) { + return; + } + + trap_Args( message, 128 ); + Com_sprintf( command, 128, "tell %i %s", clientNum, message ); + trap_SendClientCommand( command ); +} + +/////////// cameras + +#define MAX_CAMERAS 64 // matches define in splines.cpp +qboolean cameraInuse[MAX_CAMERAS]; + +int CG_LoadCamera( const char *name ) { + int i; + for ( i = 1; i < MAX_CAMERAS; i++ ) { // start at '1' since '0' is always taken by the cutscene camera + if ( !cameraInuse[i] ) { + if ( trap_loadCamera( i, name ) ) { + cameraInuse[i] = qtrue; + return i; + } + } + } + return -1; +} + +void CG_FreeCamera( int camNum ) { + cameraInuse[camNum] = qfalse; +} + +// @TEST. See if we can get an initial camera started at the first frame. +char g_initialCamera[256] = ""; +qboolean g_initialCameraStartBlack = qfalse; + +/* +============== +CG_SetInitialCamera +============== +*/ +void CG_SetInitialCamera( const char *name, qboolean startBlack ) { + // Store this info to get reset after first snapshot inited + strcpy( g_initialCamera, name ); + g_initialCameraStartBlack = startBlack; +} + + +/* +============== +CG_StartCamera +============== +*/ +void CG_StartCamera( const char *name, qboolean startBlack ) { + char lname[MAX_QPATH]; + + //if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) // don't allow camera to start if you're dead + // return; + + COM_StripExtension( name, lname ); //----(SA) added + strcat( lname, ".camera" ); + + if ( trap_loadCamera( CAM_PRIMARY, va( "cameras/%s", lname ) ) ) { + cg.cameraMode = qtrue; // camera on in cgame + if ( startBlack ) { + CG_Fade( 0, 0, 0, 255, cg.time, 0 ); // go black + } + trap_Cvar_Set( "cg_letterbox", "1" ); // go letterbox + //trap_SendClientCommand("startCamera"); // camera on in game + trap_startCamera( CAM_PRIMARY, cg.time ); // camera on in client + } else { +//----(SA) removed check for cams in main dir + cg.cameraMode = qfalse; // camera off in cgame + trap_SendClientCommand( "stopCamera" ); // camera off in game + trap_stopCamera( CAM_PRIMARY ); // camera off in client + CG_Fade( 0, 0, 0, 0, cg.time, 0 ); // ensure fadeup + trap_Cvar_Set( "cg_letterbox", "0" ); + CG_Printf( "Unable to load camera %s\n",lname ); + } +} + +/* +============== +CG_StartInitialCamera +============== +*/ +void CG_StartInitialCamera() { + // See if we've got a camera name + if ( g_initialCamera[0] != 0 ) { + // Start a camera with the initial data we stored. + CG_StartCamera( g_initialCamera, g_initialCameraStartBlack ); + + // Clear it now so we don't get re-entrance problems + g_initialCamera[0] = 0; + g_initialCameraStartBlack = qfalse; + + } // if (g_initialCamera[0] != 0)... +} + +/* +============== +CG_StopCamera +============== +*/ +void CG_StopCamera( void ) { + cg.cameraMode = qfalse; // camera off in cgame + trap_SendClientCommand( "stopCamera" ); // camera off in game + trap_stopCamera( CAM_PRIMARY ); // camera off in client + trap_Cvar_Set( "cg_letterbox", "0" ); + + // fade back into world + CG_Fade( 0, 0, 0, 255, 0, 0 ); + CG_Fade( 0, 0, 0, 0, cg.time + 500, 2000 ); + +} + +static void CG_Fade_f( void ) { + int r, g, b, a; + float duration; + + if ( trap_Argc() < 6 ) { + return; + } + + r = atof( CG_Argv( 1 ) ); + g = atof( CG_Argv( 2 ) ); + b = atof( CG_Argv( 3 ) ); + a = atof( CG_Argv( 4 ) ); + + duration = atof( CG_Argv( 5 ) ) * 1000; + + CG_Fade( r, g, b, a, cg.time, duration ); +} + +void CG_QuickMessage_f( void ) { + if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR ) { + return; + } + + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + + if ( cg_quickMessageAlt.integer ) { + trap_UI_Popup( UIMENU_WM_QUICKMESSAGEALT ); + } else { + trap_UI_Popup( UIMENU_WM_QUICKMESSAGE ); + } +} + +void CG_QuickFireteamMessage_f( void ) { + if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR ) { + return; + } + + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + + if ( cg_quickMessageAlt.integer ) { + trap_UI_Popup( UIMENU_WM_FTQUICKMESSAGEALT ); + } else { + trap_UI_Popup( UIMENU_WM_FTQUICKMESSAGE ); + } +} + +void CG_QuickFireteamAdmin_f( void ) { + trap_UI_Popup( UIMENU_NONE ); + + if ( cg.showFireteamMenu ) { + if ( cgs.ftMenuMode == 1 ) { + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + cgs.ftMenuMode = 1; + } + } else if ( cgs.clientinfo[ cg.clientNum ].team != TEAM_SPECTATOR ) { + CG_EventHandling( CGAME_EVENT_FIRETEAMMSG, qfalse ); + cgs.ftMenuMode = 1; + } +} + +static void CG_QuickFireteams_f( void ) { + if ( cg.showFireteamMenu ) { + if ( cgs.ftMenuMode == 0 ) { + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + cgs.ftMenuMode = 0; + } + } else if ( CG_IsOnFireteam( cg.clientNum ) ) { + CG_EventHandling( CGAME_EVENT_FIRETEAMMSG, qfalse ); + cgs.ftMenuMode = 0; + } +} + +static void CG_FTSayPlayerClass_f( void ) { + int playerType; + const char *s; + + playerType = cgs.clientinfo[ cg.clientNum ].cls; + + if ( playerType == PC_MEDIC ) { + s = "IamMedic"; + } else if ( playerType == PC_ENGINEER ) { + s = "IamEngineer"; + } else if ( playerType == PC_FIELDOPS ) { + s = "IamFieldOps"; + } else if ( playerType == PC_COVERTOPS ) { + s = "IamCovertOps"; + } else { + s = "IamSoldier"; + } + + if ( cg.snap && ( cg.snap->ps.pm_type != PM_INTERMISSION ) ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR || cgs.clientinfo[cg.clientNum].team == TEAM_FREE ) { + CG_Printf( CG_TranslateString( "Can't team voice chat as a spectator.\n" ) ); + return; + } + } + + trap_SendConsoleCommand( va( "cmd vsay_buddy -1 %s %s\n", CG_BuildSelectedFirteamString(), s ) ); +} + +static void CG_SayPlayerClass_f( void ) { + int playerType; + const char *s; + + playerType = cgs.clientinfo[ cg.clientNum ].cls; + + if ( playerType == PC_MEDIC ) { + s = "IamMedic"; + } else if ( playerType == PC_ENGINEER ) { + s = "IamEngineer"; + } else if ( playerType == PC_FIELDOPS ) { + s = "IamFieldOps"; + } else if ( playerType == PC_COVERTOPS ) { + s = "IamCovertOps"; + } else { + s = "IamSoldier"; + } + + if ( cg.snap && ( cg.snap->ps.pm_type != PM_INTERMISSION ) ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR || cgs.clientinfo[cg.clientNum].team == TEAM_FREE ) { + CG_Printf( CG_TranslateString( "Can't team voice chat as a spectator.\n" ) ); + return; + } + } + + trap_SendConsoleCommand( va( "cmd vsay_team %s\n", s ) ); +} + +static void CG_VoiceChat_f( void ) { + char chatCmd[64]; + + if ( trap_Argc() != 2 ) { + return; + } + + // NERVE - SMF - don't let spectators voice chat + // NOTE - This cg.snap will be the person you are following, but its just for intermission test + if ( cg.snap && ( cg.snap->ps.pm_type != PM_INTERMISSION ) ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR || cgs.clientinfo[cg.clientNum].team == TEAM_FREE ) { + CG_Printf( CG_TranslateString( "Can't voice chat as a spectator.\n" ) ); + return; + } + } + + trap_Argv( 1, chatCmd, 64 ); + + trap_SendConsoleCommand( va( "cmd vsay %s\n", chatCmd ) ); +} + +static void CG_TeamVoiceChat_f( void ) { + char chatCmd[64]; + + if ( trap_Argc() != 2 ) { + return; + } + + // NERVE - SMF - don't let spectators voice chat + // NOTE - This cg.snap will be the person you are following, but its just for intermission test + if ( cg.snap && ( cg.snap->ps.pm_type != PM_INTERMISSION ) ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR || cgs.clientinfo[cg.clientNum].team == TEAM_FREE ) { + CG_Printf( CG_TranslateString( "Can't team voice chat as a spectator.\n" ) ); + return; + } + } + + trap_Argv( 1, chatCmd, 64 ); + + trap_SendConsoleCommand( va( "cmd vsay_team %s\n", chatCmd ) ); +} + +static void CG_BuddyVoiceChat_f( void ) { + char chatCmd[64]; + + if ( trap_Argc() != 2 ) { + return; + } + + // NERVE - SMF - don't let spectators voice chat + // NOTE - This cg.snap will be the person you are following, but its just for intermission test + if ( cg.snap && ( cg.snap->ps.pm_type != PM_INTERMISSION ) ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR || cgs.clientinfo[cg.clientNum].team == TEAM_FREE ) { + CG_Printf( CG_TranslateString( "Can't buddy voice chat as a spectator.\n" ) ); + return; + } + } + + trap_Argv( 1, chatCmd, 64 ); + + trap_SendConsoleCommand( va( "cmd vsay_buddy -1 %s %s\n", CG_BuildSelectedFirteamString(), chatCmd ) ); +} + + +// ydnar: say, team say, etc +static void CG_MessageMode_f( void ) { + char cmd[ 64 ]; + + if ( cgs.eventHandling != CGAME_EVENT_NONE ) { + return; + } + + // get the actual command + trap_Argv( 0, cmd, 64 ); + + // team say + if ( !Q_stricmp( cmd, "messagemode2" ) ) { + trap_Cvar_Set( "cg_messageType", "2" ); + } + // fireteam say + else if ( !Q_stricmp( cmd, "messagemode3" ) ) { + trap_Cvar_Set( "cg_messageType", "3" ); + } + // (normal) say + else + { + trap_Cvar_Set( "cg_messageType", "1" ); + } + + // clear the chat text + trap_Cvar_Set( "cg_messageText", "" ); + + // open the menu + trap_UI_Popup( UIMENU_INGAME_MESSAGEMODE ); +} + +static void CG_MessageSend_f( void ) { + char messageText[ 256 ]; + int messageType; + + + // get values + trap_Cvar_VariableStringBuffer( "cg_messageType", messageText, 256 ); + messageType = atoi( messageText ); + trap_Cvar_VariableStringBuffer( "cg_messageText", messageText, 256 ); + + // reset values + trap_Cvar_Set( "cg_messageText", "" ); + trap_Cvar_Set( "cg_messageType", "" ); + trap_Cvar_Set( "cg_messagePlayer", "" ); + + // don't send empty messages + if ( messageText[ 0 ] == '\0' ) { + return; + } + + // team say + if ( messageType == 2 ) { + trap_SendConsoleCommand( va( "say_team \"%s\"\n", messageText ) ); + + // fireteam say + } else if ( messageType == 3 ) { + trap_SendConsoleCommand( va( "say_buddy \"%s\"\n", messageText ) ); + + // normal say + } else { + trap_SendConsoleCommand( va( "say \"%s\"\n", messageText ) ); + } +} + + + +static void CG_SetWeaponCrosshair_f( void ) { + char crosshair[64]; + + trap_Argv( 1, crosshair, 64 ); + cg.newCrosshairIndex = atoi( crosshair ) + 1; +} +// -NERVE - SMF + +static void CG_SelectBuddy_f( void ) { + int pos = atoi( CG_Argv( 1 ) ); + int i; + clientInfo_t* ci; + + // Gordon: + // 0 - 5 = select that person + // -1 = none + // -2 = all + + switch ( pos ) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + break; // Gordon: we aren't a leader, so dont allow selection + } + + ci = CG_SortedFireTeamPlayerForPosition( pos, 6 ); + if ( !ci ) { + break; // there was no-one in this position + } + + ci->selected ^= qtrue; + + break; + + case - 1: + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + break; // Gordon: we aren't a leader, so dont allow selection + } + + for ( i = 0; i < 6; i++ ) { + ci = CG_SortedFireTeamPlayerForPosition( i, 6 ); + if ( !ci ) { + break; // there was no-one in this position + } + + ci->selected = qfalse; + } + break; + + case - 2: + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + break; // Gordon: we aren't a leader, so dont allow selection + } + + for ( i = 0; i < 6; i++ ) { + ci = CG_SortedFireTeamPlayerForPosition( i, 6 ); + if ( !ci ) { + break; // there was no-one in this position + } + + ci->selected = qtrue; + } + break; + + } +} + +extern void CG_AdjustAutomapZoom( int zoomIn ); + +static void CG_AutomapZoomIn_f( void ) { + if ( !cgs.autoMapOff ) { + CG_AdjustAutomapZoom( qtrue ); + } +} + +static void CG_AutomapZoomOut_f( void ) { + if ( !cgs.autoMapOff ) { + CG_AdjustAutomapZoom( qfalse ); + } +} + +static void CG_AutomapExpandDown_f( void ) { + if ( !cgs.autoMapExpanded ) { + cgs.autoMapExpanded = qtrue; + if ( cg.time - cgs.autoMapExpandTime < 250.f ) { + cgs.autoMapExpandTime = cg.time - ( 250.f - ( cg.time - cgs.autoMapExpandTime ) ); + } else { + cgs.autoMapExpandTime = cg.time; + } + } +} + +static void CG_AutomapExpandUp_f( void ) { + if ( cgs.autoMapExpanded ) { + cgs.autoMapExpanded = qfalse; + if ( cg.time - cgs.autoMapExpandTime < 250.f ) { + cgs.autoMapExpandTime = cg.time - ( 250.f - ( cg.time - cgs.autoMapExpandTime ) ); + } else { + cgs.autoMapExpandTime = cg.time; + } + } +} + +static void CG_ToggleAutomap_f( void ) { + cgs.autoMapOff = !cgs.autoMapOff; +} + +// OSP +const char *aMonths[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +void CG_currentTime_f( void ) { + qtime_t ct; + + trap_RealTime( &ct ); + CG_Printf( "[cgnotify]Current time: ^3%02d:%02d:%02d (%02d %s %d)\n", ct.tm_hour, ct.tm_min, ct.tm_sec, ct.tm_mday, aMonths[ct.tm_mon], 1900 + ct.tm_year ); +} + +// Dynamically names a demo and sets up the recording +void CG_autoRecord_f( void ) { + trap_SendConsoleCommand( va( "record %s\n", CG_generateFilename() ) ); +} + +// Dynamically names a screenshot[JPEG] +void CG_autoScreenShot_f( void ) { + trap_SendConsoleCommand( va( "screenshot%s %s\n", ( ( cg_useScreenshotJPEG.integer ) ? "JPEG" : "" ), CG_generateFilename() ) ); +} + +void CG_vstrDown_f( void ) { + // The engine also passes back the key code and time of the key press + if ( trap_Argc() == 5 ) { + trap_SendConsoleCommand( va( "vstr %s;", CG_Argv( 1 ) ) ); + } else { CG_Printf( "[cgnotify]Usage: +vstr [down_vstr] [up_vstr]\n" );} +} + +void CG_vstrUp_f( void ) { + // The engine also passes back the key code and time of the key press + if ( trap_Argc() == 5 ) { + trap_SendConsoleCommand( va( "vstr %s;", CG_Argv( 2 ) ) ); + } else { CG_Printf( "[cgnotify]Usage: +vstr [down_vstr] [up_vstr]\n" );} +} + +void CG_keyOn_f( void ) { + if ( !cg.demoPlayback ) { + CG_Printf( "[cgnotify]^3*** NOT PLAYING A DEMO!!\n" ); + return; + } + + if ( demo_infoWindow.integer > 0 ) { + CG_ShowHelp_On( &cg.demohelpWindow ); + } + + CG_EventHandling( CGAME_EVENT_DEMO, qtrue ); +} + +void CG_keyOff_f( void ) { + if ( !cg.demoPlayback ) { + return; + } + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); +} + +void CG_dumpStats_f( void ) { + if ( cgs.dumpStatsTime < cg.time ) { + cgs.dumpStatsTime = cg.time + 2000; + trap_SendClientCommand( ( cg.mvTotalClients < 1 ) ? "weaponstats" : "statsall" ); + } +} +void CG_wStatsDown_f( void ) { + int i = ( cg.mvTotalClients > 0 ) ? ( cg.mvCurrentActive->mvInfo & MV_PID ) : cg.snap->ps.clientNum; + + if ( cg.mvTotalClients < 1 && cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + Pri( "You must be a player or following a player to use +wstats\n" ); + return; + } + + if ( cg.statsRequestTime < cg.time ) { + cg.statsRequestTime = cg.time + 500; + trap_SendClientCommand( va( "wstats %d", i ) ); + } + + cg.showStats = qtrue; +} + +void CG_wStatsUp_f( void ) { + cg.showStats = qfalse; + CG_windowFree( cg.statsWindow ); + cg.statsWindow = NULL; +} + +void CG_toggleSpecHelp_f( void ) { + if ( cg.mvTotalClients > 0 && !cg.demoPlayback ) { + if ( cg.spechelpWindow != SHOW_ON && cg_specHelp.integer > 0 ) { + CG_ShowHelp_On( &cg.spechelpWindow ); + } else if ( cg.spechelpWindow == SHOW_ON ) { + CG_ShowHelp_Off( &cg.spechelpWindow ); + } + } +} +// -OSP + +void CG_Obj_f( void ) { + // Gordon: short circuit this +} + +static void CG_EditSpeakers_f( void ) { + if ( cg.editingSpeakers ) { + CG_DeActivateEditSoundMode(); + } else { + const char *s = Info_ValueForKey( CG_ConfigString( CS_SYSTEMINFO ), "sv_cheats" ); + if ( s[0] != '1' ) { + CG_Printf( "editSpeakers is cheat protected.\n" ); + return; + } + CG_ActivateEditSoundMode(); + } +} + +static void CG_DumpSpeaker_f( void ) { +/* char sscrfilename[MAX_QPATH]; + char soundfile[MAX_STRING_CHARS]; + int i, wait, random; + char *extptr, *buffptr; + fileHandle_t f; + + // Check for argument + if( trap_Argc() < 2 || trap_Argc() > 4 ) + { + CG_Printf( "Usage: dumpspeaker ( | )\n" ); + return; + } + + wait = random = 0; + + // parse the other parameters + for( i = 2; i < trap_Argc(); i++ ) { + char *valueptr = NULL; + + trap_Argv( i, soundfile, sizeof(soundfile) ); + + for( buffptr = soundfile; *buffptr; buffptr++ ) { + if( *buffptr == '=' ) { + valueptr = buffptr + 1; + break; + } + } + + Q_strncpyz( soundfile, soundfile, buffptr - soundfile + 1 ); + + if( !Q_stricmp( soundfile, "wait" ) ) + wait = atoi( valueptr ); + else if( !Q_stricmp( soundfile, "random" ) ) + random = atoi( valueptr ); + } + + // parse soundfile + trap_Argv( 1, soundfile, sizeof(soundfile) ); + + // Open soundfile + Q_strncpyz( sscrfilename, cgs.mapname, sizeof(sscrfilename) ); + extptr = sscrfilename + strlen( sscrfilename ) - 4; + if( extptr < sscrfilename || Q_stricmp( extptr, ".bsp" ) ) + { + CG_Printf( "Unable to dump, unknown map name?\n" ); + return; + } + Q_strncpyz( extptr, ".sscr", 5 ); + trap_FS_FOpenFile( sscrfilename, &f, FS_APPEND_SYNC ); + if( !f ) + { + CG_Printf( "Failed to open '%s' for writing.\n", sscrfilename ); + return; + } + + // Build the entity definition + Com_sprintf( soundfile, sizeof(soundfile), "{\n\"classname\" \"target_speaker\"\n\"origin\" \"%i %i %i\"\n\"noise\" \"%s\"\n", + (int) cg.snap->ps.origin[0], (int) cg.snap->ps.origin[1], (int) cg.snap->ps.origin[2], soundfile ); + + if( wait ) { + Q_strcat( soundfile, sizeof(soundfile), va( "\"wait\" \"%i\"\n", wait ) ); + } + if( random ) { + Q_strcat( soundfile, sizeof(soundfile), va( "\"random\" \"%i\"\n", random ) ); + } + Q_strcat( soundfile, sizeof(soundfile), "}\n\n" ); + + // And write out/acknowledge + trap_FS_Write( soundfile, strlen( soundfile ), f ); + trap_FS_FCloseFile( f ); + CG_Printf( "Entity dumped to '%s' (%i %i %i).\n", sscrfilename, + (int) cg.snap->ps.origin[0], (int) cg.snap->ps.origin[1], (int) cg.snap->ps.origin[2] );*/ + bg_speaker_t speaker; + trace_t tr; + vec3_t end; + + if ( !cg.editingSpeakers ) { + CG_Printf( "Speaker Edit mode needs to be activated to dump speakers\n" ); + return; + } + + memset( &speaker, 0, sizeof( speaker ) ); + + speaker.volume = 127; + speaker.range = 1250; + + VectorMA( cg.refdef_current->vieworg, 32, cg.refdef_current->viewaxis[0], end ); + CG_Trace( &tr, cg.refdef_current->vieworg, NULL, NULL, end, -1, MASK_SOLID ); + + if ( tr.fraction < 1.f ) { + VectorCopy( tr.endpos, speaker.origin ); + VectorMA( speaker.origin, -4, cg.refdef_current->viewaxis[0], speaker.origin ); + } else { + VectorCopy( tr.endpos, speaker.origin ); + } + + if ( !BG_SS_StoreSpeaker( &speaker ) ) { + CG_Printf( S_COLOR_RED "ERROR: Failed to store speaker\n" ); + } +} + +static void CG_ModifySpeaker_f( void ) { + if ( cg.editingSpeakers ) { + CG_ModifyEditSpeaker(); + } +} + +static void CG_UndoSpeaker_f( void ) { + if ( cg.editingSpeakers ) { + CG_UndoEditSpeaker(); + } +} + +void CG_ForceTapOut_f( void ) { + trap_SendClientCommand( "forcetapout" ); +} + +static void CG_CPM_f( void ) { + CG_AddPMItem( PM_MESSAGE, CG_Argv( 1 ), cgs.media.voiceChatShader ); +} + +typedef struct { + char *cmd; + void ( *function )( void ); +} consoleCommand_t; + +static consoleCommand_t commands[] = +{ +// { "obj", CG_Obj_f }, +// { "setspawnpt", CG_Obj_f }, + { "testgun", CG_TestGun_f }, + { "testmodel", CG_TestModel_f }, + { "nextframe", CG_TestModelNextFrame_f }, + { "prevframe", CG_TestModelPrevFrame_f }, + { "nextskin", CG_TestModelNextSkin_f }, + { "prevskin", CG_TestModelPrevSkin_f }, + { "viewpos", CG_Viewpos_f }, + { "+scores", CG_ScoresDown_f }, + { "-scores", CG_ScoresUp_f }, + { "zoomin", CG_ZoomIn_f }, + { "zoomout", CG_ZoomOut_f }, + { "weaplastused", CG_LastWeaponUsed_f }, + { "weapnextinbank", CG_NextWeaponInBank_f }, + { "weapprevinbank", CG_PrevWeaponInBank_f }, + { "weapnext", CG_NextWeapon_f }, + { "weapprev", CG_PrevWeapon_f }, + { "weapalt", CG_AltWeapon_f }, + { "weapon", CG_Weapon_f }, + { "weaponbank", CG_WeaponBank_f }, + { "tell_target", CG_TellTarget_f }, + { "tell_attacker", CG_TellAttacker_f }, + { "tcmd", CG_TargetCommand_f }, + { "fade", CG_Fade_f }, // duffy + { "loadhud", CG_LoadHud_f }, + { "loadweapons", CG_LoadWeapons_f }, + + { "mp_QuickMessage", CG_QuickMessage_f }, + { "mp_fireteammsg", CG_QuickFireteams_f }, + { "mp_fireteamadmin", CG_QuickFireteamAdmin_f }, + { "wm_sayPlayerClass", CG_SayPlayerClass_f }, + { "wm_ftsayPlayerClass",CG_FTSayPlayerClass_f }, + + + { "VoiceChat", CG_VoiceChat_f }, + { "VoiceTeamChat", CG_TeamVoiceChat_f }, + + // ydnar: say, teamsay, etc + { "messageMode", CG_MessageMode_f }, + { "messageMode2", CG_MessageMode_f }, + { "messageMode3", CG_MessageMode_f }, + { "messageSend", CG_MessageSend_f }, + + { "SetWeaponCrosshair", CG_SetWeaponCrosshair_f }, + // -NERVE - SMF + + { "VoiceFireTeamChat", CG_BuddyVoiceChat_f }, + + { "openlimbomenu", CG_LimboMenu_f }, + + { "+stats", CG_StatsDown_f }, + { "-stats", CG_StatsUp_f }, + { "+topshots", CG_topshotsDown_f }, + { "-topshots", CG_topshotsUp_f }, + + // OSP + { "autoRecord", CG_autoRecord_f }, + { "autoScreenshot", CG_autoScreenShot_f }, + { "currentTime", CG_currentTime_f }, + { "keyoff", CG_keyOff_f }, + { "keyon", CG_keyOn_f }, +#ifdef MV_SUPPORT + { "mvactivate", CG_mvToggleAll_f }, + { "mvdel", CG_mvDelete_f }, + { "mvhide", CG_mvHideView_f }, + { "mvnew", CG_mvNew_f }, + { "mvshow", CG_mvShowView_f }, + { "mvswap", CG_mvSwapViews_f }, + { "mvtoggle", CG_mvToggleView_f }, + { "spechelp", CG_toggleSpecHelp_f }, +#endif + { "statsdump", CG_dumpStats_f }, + { "+vstr", CG_vstrDown_f }, + { "-vstr", CG_vstrUp_f }, + // OSP + + { "selectbuddy", CG_SelectBuddy_f }, + + { "MapZoomIn", CG_AutomapZoomIn_f }, + { "MapZoomOut", CG_AutomapZoomOut_f }, + { "+mapexpand", CG_AutomapExpandDown_f }, + { "-mapexpand", CG_AutomapExpandUp_f }, + + { "generateTracemap", CG_GenerateTracemap }, + // xkan, 11/27/2002, toggle automap on/off + { "ToggleAutoMap", CG_ToggleAutomap_f }, + + { "editSpeakers", CG_EditSpeakers_f }, + { "dumpSpeaker", CG_DumpSpeaker_f }, + { "modifySpeaker", CG_ModifySpeaker_f }, + { "undoSpeaker", CG_UndoSpeaker_f }, + { "cpm", CG_CPM_f }, + { "forcetapout", CG_ForceTapOut_f }, +}; + + +/* +================= +CG_ConsoleCommand + +The string has been tokenized and can be retrieved with +Cmd_Argc() / Cmd_Argv() +================= +*/ +qboolean CG_ConsoleCommand( void ) { + const char *cmd; + int i; + + // Arnout - don't allow console commands until a snapshot is present + if ( !cg.snap ) { + return qfalse; + } + + cmd = CG_Argv( 0 ); + + for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { + if ( !Q_stricmp( cmd, commands[i].cmd ) ) { + commands[i].function(); + return qtrue; + } + } + + return qfalse; +} + + +/* +================= +CG_InitConsoleCommands + +Let the client system know about all of our commands +so it can perform tab completion +================= +*/ +void CG_InitConsoleCommands( void ) { + int i; + + for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) { + trap_AddCommand( commands[i].cmd ); + } + + // + // the game server will interpret these commands, which will be automatically + // forwarded to the server after they are not recognized locally + // + trap_AddCommand( "kill" ); + trap_AddCommand( "say" ); + trap_AddCommand( "say_limbo" ); // NERVE - SMF + trap_AddCommand( "tell" ); + trap_AddCommand( "listbotgoals" ); + trap_AddCommand( "give" ); + trap_AddCommand( "god" ); + trap_AddCommand( "notarget" ); + trap_AddCommand( "noclip" ); + trap_AddCommand( "team" ); + trap_AddCommand( "follow" ); + trap_AddCommand( "addbot" ); + trap_AddCommand( "setviewpos" ); + trap_AddCommand( "callvote" ); + trap_AddCommand( "vote" ); + + // Rafael + trap_AddCommand( "nofatigue" ); + + // NERVE - SMF + trap_AddCommand( "follownext" ); + trap_AddCommand( "followprev" ); + + trap_AddCommand( "start_match" ); + trap_AddCommand( "reset_match" ); + trap_AddCommand( "swap_teams" ); + // -NERVE - SMF + // OSP + trap_AddCommand( "?" ); + trap_AddCommand( "bottomshots" ); + trap_AddCommand( "commands" ); + trap_AddCommand( "follow" ); + trap_AddCommand( "lock" ); +#ifdef MV_SUPPORT + trap_AddCommand( "mvadd" ); + trap_AddCommand( "mvaxis" ); + trap_AddCommand( "mvallies" ); + trap_AddCommand( "mvall" ); + trap_AddCommand( "mvnone" ); +#endif + trap_AddCommand( "notready" ); + trap_AddCommand( "pause" ); + trap_AddCommand( "players" ); + trap_AddCommand( "readyteam" ); + trap_AddCommand( "ready" ); + trap_AddCommand( "ref" ); + trap_AddCommand( "say_teamnl" ); + trap_AddCommand( "say_team" ); + trap_AddCommand( "scores" ); + trap_AddCommand( "specinvite" ); + trap_AddCommand( "speclock" ); + trap_AddCommand( "specunlock" ); + trap_AddCommand( "statsall" ); + trap_AddCommand( "statsdump" ); + trap_AddCommand( "timein" ); + trap_AddCommand( "timeout" ); + trap_AddCommand( "topshots" ); + trap_AddCommand( "unlock" ); + trap_AddCommand( "unpause" ); + trap_AddCommand( "unready" ); + trap_AddCommand( "weaponstats" ); + // OSP + + trap_AddCommand( "fireteam" ); + trap_AddCommand( "buddylist" ); + trap_AddCommand( "showstats" ); + + trap_AddCommand( "ignore" ); + trap_AddCommand( "unignore" ); + + trap_AddCommand( "addtt" ); + trap_AddCommand( "selectbuddy" ); + trap_AddCommand( "selectNextBuddy" ); // xkan 9/26/2002 + + trap_AddCommand( "loadgame" ); + trap_AddCommand( "savegame" ); + + trap_AddCommand( "campaign" ); + trap_AddCommand( "listcampaigns" ); + + trap_AddCommand( "setweapons" ); + trap_AddCommand( "setclass" ); +} diff --git a/src/cgame/cg_debriefing.c b/src/cgame/cg_debriefing.c new file mode 100644 index 0000000..69fe91c --- /dev/null +++ b/src/cgame/cg_debriefing.c @@ -0,0 +1,2900 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +team_t CG_Debriefing_FindWinningTeam( void ); +team_t CG_Debriefing_FindOveralWinningTeam( void ); +team_t CG_Debriefing_FindWinningTeamForPos( int pos ); + +int QDECL CG_SortPlayersByXP( const void *a, const void *b ); + +#define DB_MASTER_FONT &cgs.media.limboFont2 + +panel_button_text_t debriefTitleFont = { + 0.3f, 0.3f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, ITEM_ALIGN_CENTER, + DB_MASTER_FONT, +}; + +panel_button_text_t debriefHeadingFont = { + 0.24f, 0.24f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_text_t debriefListFont = { + 0.20f, 0.22f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_text_t debriefPlayerHeadingSmallerFont = { + 0.2f, 0.2f, + { 0.6f, 0.6f, 0.6f, 1.f }, + 0, 0, + DB_MASTER_FONT, +}; + +#define DB_RANK_X 213 + 4 +#define DB_NAME_X DB_RANK_X + 28 +/*#define DB_MEDALS_X DB_NAME_X + 180 +#define DB_TIME_X DB_MEDALS_X + 64*/ +#define DB_TIME_X DB_NAME_X + 180 +#define DB_KILLS_X DB_TIME_X + 48 +#define DB_DEATHS_X DB_KILLS_X + 48 +#define DB_XP_X DB_DEATHS_X + 56 +#define DH_HEADING_Y 60 + +panel_button_t debriefTitleBack = { + "white", + NULL, + { 10, 4, 620, 20 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefTitleBackBorderLower = { + NULL, + NULL, + { 10, 24, 620, 200 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefTitleBackLower = { + "white", + NULL, + { 10, 24, 620, 200 }, + { 1, 0, 0, 0, 153, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefTitleBackBorder = { + NULL, + NULL, + { 10, 4, 620, 20 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefTitle = { + NULL, + NULL, + { 10, 20, 620, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefTitleFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_DebriefingTitle_Draw, + NULL, +}; + +panel_button_t debriefPlayerHeaderBackBorderLower = { + NULL, + NULL, + { 10, 260 - 14, 196, 110 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerHeaderBackLower = { + "white", + NULL, + { 10, 260 - 14, 196, 110 }, + { 1, 0, 0, 0, 153, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerHeaderBack = { + "white", + NULL, + { 10, 240 - 14, 196, 20 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerHeaderBackBorder = { + NULL, + NULL, + { 10, 240 - 14, 196, 20 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + + + + + +panel_button_t debriefPlayerSkillsBackBorderLower = { + NULL, + NULL, + { 210, 260 - 14, 80, 110 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerSkillsBackLower = { + "white", + NULL, + { 210, 260 - 14, 80, 110 }, + { 1, 0, 0, 0, 153, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerSkillsBack = { + "white", + NULL, + { 210, 240 - 14, 80, 20 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t debriefPlayerSkillsBackBorder = { + NULL, + NULL, + { 210, 240 - 14, 80, 20 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + + + + + +panel_button_t debriefPlayerWeaponStatsHeader = { + NULL, + "Weapon Stats", + { 18, 248, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsNameHeader = { + NULL, + "Name", + { 18, 262, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsShotsHeader = { + NULL, + "Shots", + { 78, 262, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsHitsHeader = { + NULL, + "Hits", + { 118, 262, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsKillsHeader = { + NULL, + "Kills", + { 148, 262, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsList = { + NULL, + NULL, + { 18, 262, 164, 80 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerHeadingSmallerFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_DebriefingPlayerWeaponStats_Draw, + NULL, +}; + +panel_button_t debriefPlayerWeaponStatsListScroll = { + NULL, + NULL, + { 18 + 164, 262, 16, 80 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_Scrollbar_KeyDown, /* keyDown */ + CG_Debriefing_Scrollbar_KeyUp, /* keyUp */ + CG_Debriefing_Scrollbar_Draw, + NULL, +}; + + + + +panel_button_t debriefTitleWindow = { + NULL, + NULL, + { 10, 4, 620, 22 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debreifing2_MissionTitle_Draw, + NULL, +}; + +panel_button_t debriefMissionTitleWindow = { + NULL, + NULL, + { 10, 30, 193, 240 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_t debriefMissionImage = { + NULL, + NULL, + { 16, 46, 181, 161 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debreifing2_Mission_Draw, + NULL, +}; + +panel_button_t debriefMissionMaps = { + NULL, + NULL, + { 12, 210, 189, 60 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing2_Maps_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_Debreifing2_Maps_Draw, + NULL, +}; + +panel_button_t debriefMissionAwardsWindow = { + NULL, + "ROLL OF HONOR", + { 213, 30, 417, 240 }, + { ITEM_ALIGN_CENTER, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_t debriefMissionAwardsList = { + NULL, + NULL, + { 215, 44, 413, 220 }, + { ITEM_ALIGN_CENTER, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debreifing2_Awards_Draw, + NULL, +}; + +panel_button_t debriefMissionStatsWindow = { + NULL, + "MISSION STATS", + { 10, 280 - 6, 620, 70 + 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_t debriefMissionStatsHeaders = { + NULL, + NULL, + { 16, 298, 608, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing2TeamSkillHeaders_Draw, + NULL, +}; + +panel_button_t debriefMissionStatsWinner = { + NULL, + NULL, + { 16, 314, 608, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing2TeamSkillXP_Draw, + NULL, +}; + +panel_button_t debriefMissionStatsLoser = { + NULL, + NULL, + { 16, 330, 608, 16 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing2TeamSkillXP_Draw, + NULL, +}; + + + + +panel_button_t debriefPlayerListWindow = { + NULL, + "PLAYERS", + { 213, 30, 417, 326 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_text_t debriefPlayerListFont = { + 0.2f, 0.2f, + { 0.6f, 0.6f, 0.6f, 1.f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_t debriefHeadingRank = { + NULL, + "Rank", + { DB_RANK_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefHeadingName = { + NULL, + "Name", + { DB_NAME_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +#if 0 +panel_button_t debriefHeadingMedals = { + NULL, + "Medals", + { DB_MEDALS_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; +#endif // 0 + +panel_button_t debriefHeadingTime = { + NULL, + "Time", + { DB_TIME_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefHeadingXP = { + NULL, + "XP", + { DB_XP_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_DebriefingXPHeader_Draw, + NULL, +}; + +panel_button_t debriefHeadingKills = { + NULL, + "Kills", + { DB_KILLS_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefHeadingDeaths = { + NULL, + "Deaths", + { DB_DEATHS_X, DH_HEADING_Y, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t debriefPlayerList = { + NULL, + NULL, + { DB_RANK_X, DH_HEADING_Y, 640 - 10 - 8 - 16 - DB_RANK_X - 16, 292 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerListFont, /* font */ + CG_DebriefingPlayerList_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_DebriefingPlayerList_Draw, + NULL, +}; + +panel_button_t debriefPlayerListScroll = { + NULL, + NULL, + { 640 - 10 - 8 - 16, DH_HEADING_Y, 16, 292 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_Scrollbar_KeyDown, /* keyDown */ + CG_Debriefing_Scrollbar_KeyUp, /* keyUp */ + CG_Debriefing_Scrollbar_Draw, + NULL, +}; + + + + +panel_button_text_t debriefPlayerInfoFont = { + 0.2f, 0.2f, + { 0.6f, 0.6f, 0.6f, 1.f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_t debriefPlayerInfoWindow = { + NULL, + "PLAYER STATS", + { 10, 30, 193, 326 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_t debriefPlayerInfoName = { + NULL, + NULL, + { 14, 56, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerName_Draw, + NULL, +}; + +panel_button_t debriefPlayerInfoRank = { + NULL, + NULL, + { 74, 70, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerRank_Draw, + NULL, +}; + +panel_button_t debriefPlayerInfoMedals = { + NULL, + NULL, + { 74, 84, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerMedals_Draw, + NULL, +}; + +panel_button_t debriefPlayerInfoTime = { + NULL, + NULL, + { 74, 98, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerTime_Draw, + NULL, +}; + +panel_button_t debriefPlayerInfoXP = { + NULL, + NULL, + { 74, 112, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerXP_Draw, + NULL, +}; + +panel_button_t debriefPlayerInfoACC = { + NULL, + NULL, + { 74, 126, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefPlayerInfoFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_PlayerACC_Draw, + NULL, +}; + +#define PLAYERHEADER_SKILLS( number ) \ + panel_button_t debriefPlayerInfoSkills ## number = { \ + NULL, \ + NULL, \ + { 18 + ( 100 * ( number % 2 ) ), 140 + ( number / 2 * 24 ), 12, 12 }, \ + { number, 0, 0, 0, 0, 0, 0, 0 }, \ + &debriefPlayerInfoFont, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_Debriefing_PlayerSkills_Draw, \ + NULL, \ + } + +PLAYERHEADER_SKILLS( 0 ); +PLAYERHEADER_SKILLS( 1 ); +PLAYERHEADER_SKILLS( 2 ); +PLAYERHEADER_SKILLS( 3 ); +PLAYERHEADER_SKILLS( 4 ); +PLAYERHEADER_SKILLS( 5 ); +PLAYERHEADER_SKILLS( 6 ); + +panel_button_t* debriefPanelButtons[] = { + &debriefTitleWindow, + + &debriefPlayerListWindow, &debriefPlayerList, &debriefPlayerListScroll, + + &debriefHeadingRank, &debriefHeadingName, +#if 0 + &debriefHeadingMedals, +#endif // 0 + &debriefHeadingTime, &debriefHeadingXP, &debriefHeadingKills, &debriefHeadingDeaths, + + &debriefPlayerInfoWindow, &debriefPlayerInfoName, &debriefPlayerInfoRank, &debriefPlayerInfoMedals, &debriefPlayerInfoTime, &debriefPlayerInfoXP, &debriefPlayerInfoACC, + + &debriefPlayerInfoSkills0, + &debriefPlayerInfoSkills1, + &debriefPlayerInfoSkills2, + &debriefPlayerInfoSkills3, + &debriefPlayerInfoSkills4, + &debriefPlayerInfoSkills5, + &debriefPlayerInfoSkills6, + + &debriefPlayerWeaponStatsHeader, &debriefPlayerWeaponStatsNameHeader, &debriefPlayerWeaponStatsShotsHeader, &debriefPlayerWeaponStatsHitsHeader, &debriefPlayerWeaponStatsKillsHeader, + &debriefPlayerWeaponStatsList, &debriefPlayerWeaponStatsListScroll, + + NULL +}; + + +panel_button_t teamDebriefOutcome = { + "white", + NULL, + { 14, 26, 432, 224 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_TeamDebriefingOutcome_Draw, + NULL, +}; + + + + +panel_button_t teamDebriefMapListBackBorderLower = { + NULL, + NULL, + { 460, 22, 170, 232 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapListBackLower = { + "white", + NULL, + { 460, 22, 170, 232 }, + { 1, 0, 0, 0, 153, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapListBackBorder = { + NULL, + NULL, + { 460, 4, 170, 20 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapListBack = { + "white", + NULL, + { 460, 4, 170, 20 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapList = { + NULL, + NULL, + { 470, 196, 130, 50 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &debriefListFont, /* font */ + CG_TeamDebriefingMapList_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_TeamDebriefingMapList_Draw, + NULL, +}; + +panel_button_t teamDebriefMapListScroll = { + NULL, + NULL, + { 604, 198, 16, 50 }, + { 2, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_Scrollbar_KeyDown, /* keyDown */ + CG_Debriefing_Scrollbar_KeyUp, /* keyUp */ + CG_Debriefing_Scrollbar_Draw, + NULL, +}; + +panel_button_t teamDebriefMapShot = { + NULL, + NULL, + { 464, 28, 170 - 8, 170 - 8 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_TeamDebriefingMapShot_Draw, + NULL, +}; + +panel_button_text_t teamDebriefBigTitle = { + 0.32f, 0.32f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_text_t teamDebriefTitleSmall = { + 0.24f, 0.24f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, ITEM_ALIGN_CENTER, + DB_MASTER_FONT, +}; + +panel_button_text_t teamDebriefTitle = { + 0.28f, 0.28f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, 0, + DB_MASTER_FONT, +}; + +panel_button_t teamDebriefMapWinnerText = { + NULL, + NULL, + { 10, 273, 620, 20 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &teamDebriefBigTitle, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_TeamDebriefingMapWinner_Draw, + NULL, +}; + + + +panel_button_t teamDebriefMapWinnerBackBorderLower = { + NULL, + NULL, + { 10, 277, 620, 77 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapWinnerBackLower = { + "white", + NULL, + { 10, 277, 620, 77 }, + { 1, 0, 0, 0, 153, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapWinnerTextBackBorder = { + NULL, + NULL, + { 10, 258, 620, 20 }, + { 1, 127, 127, 127, 255, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefMapWinnerTextBack = { + "white", + NULL, + { 10, 258, 620, 20 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t teamDebriefAxisXPText = { + NULL, + "Axis", + { 24, 320, 470, 200 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &teamDebriefBigTitle, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t teamDebriefAlliesXPText = { + NULL, + "Allies", + { 24, 340, 470, 200 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &teamDebriefBigTitle, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +#define TDB_SKILL_TITLES_XP( number, title, x ) \ + panel_button_t teamDebriefSkillXPText_ ## number = { \ + NULL, \ + title, \ + { 100 + ( number * 65 ), 304 - ( x * 12 ), 20, 200 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0 }, \ + &teamDebriefTitleSmall, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + BG_PanelButtonsRender_Text, \ + NULL, \ + } + +TDB_SKILL_TITLES_XP( 0, "Battle Sense", 0 ); +TDB_SKILL_TITLES_XP( 1, "Engineer", 1 ); +TDB_SKILL_TITLES_XP( 2, "Medic", 0 ); +TDB_SKILL_TITLES_XP( 3, "Field Ops", 1 ); +TDB_SKILL_TITLES_XP( 4, "Light Weapons",0 ); +TDB_SKILL_TITLES_XP( 5, "Soldier", 1 ); +TDB_SKILL_TITLES_XP( 6, "Covert Ops", 0 ); +TDB_SKILL_TITLES_XP( 7, "Total", 1 ); + +#define TDB_SKILL_AXIS_XP( number ) \ + panel_button_t teamDebriefSkillXPText0_ ## number = { \ + NULL, \ + NULL, \ + { 110 + ( number * 65 ), 320, 470, 200 }, \ + { 0, number, 0, 0, 0, 0, 0, 0 }, \ + &teamDebriefTitle, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_TeamDebriefingTeamSkillXP_Draw, \ + NULL, \ + } + +#define TDB_SKILL_ALLIES_XP( number ) \ + panel_button_t teamDebriefSkillXPText1_ ## number = { \ + NULL, \ + NULL, \ + { 110 + ( number * 65 ), 340, 470, 200 }, \ + { 1, number, 0, 0, 0, 0, 0, 0 }, \ + &teamDebriefTitle, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_TeamDebriefingTeamSkillXP_Draw, \ + NULL, \ + } + +TDB_SKILL_AXIS_XP( 0 ); +TDB_SKILL_AXIS_XP( 1 ); +TDB_SKILL_AXIS_XP( 2 ); +TDB_SKILL_AXIS_XP( 3 ); +TDB_SKILL_AXIS_XP( 4 ); +TDB_SKILL_AXIS_XP( 5 ); +TDB_SKILL_AXIS_XP( 6 ); +TDB_SKILL_AXIS_XP( 7 ); + +TDB_SKILL_ALLIES_XP( 0 ); +TDB_SKILL_ALLIES_XP( 1 ); +TDB_SKILL_ALLIES_XP( 2 ); +TDB_SKILL_ALLIES_XP( 3 ); +TDB_SKILL_ALLIES_XP( 4 ); +TDB_SKILL_ALLIES_XP( 5 ); +TDB_SKILL_ALLIES_XP( 6 ); +TDB_SKILL_ALLIES_XP( 7 ); + +panel_button_t* teamDebriefPanelButtons[] = { + &debriefTitleWindow, &debriefMissionTitleWindow, &debriefMissionAwardsWindow, &debriefMissionImage, &debriefMissionMaps, &debriefMissionAwardsList, + + &debriefMissionStatsWindow, &debriefMissionStatsWinner, &debriefMissionStatsLoser, &debriefMissionStatsHeaders, + + NULL +}; + + + + + + + + + +panel_button_text_t chatPanelButtonFont = { + 0.20f, 0.20f, + { 1.f, 1.f, 1.f, 0.8f }, + 0, ITEM_ALIGN_CENTER, + DB_MASTER_FONT, +}; + +panel_button_text_t chatPanelButtonFontRed = { + 0.20f, 0.20f, + { 1.f, 0.f, 0.f, 0.8f }, + 0, ITEM_ALIGN_CENTER, + DB_MASTER_FONT, +}; + + +panel_button_t chatPanelWindow = { + NULL, + "CHAT", + { 10, 480 - 120, 620, 110 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Window, + NULL, +}; + +panel_button_t chatPanelText = { + NULL, + NULL, + { 18, 480 - 34, 640 - 36, TEAMCHAT_HEIGHT }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_ChatBox_Draw, + NULL, +}; + +panel_button_t chatPanelNextButton = { + NULL, + "MORE", + { 640 - 10 - 60 - 4, 480 - 30, 60, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_NextButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_NextButton_Draw, + NULL, +}; + +panel_button_t chatPanelHTMLButton = { + NULL, + "^1REPORT", + { 640 - 10 - 60 - 4 - 60 - 4, 480 - 30, 60, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_HTMLButton_Draw, + NULL, +}; + +panel_button_t chatPanelQCButton = { + NULL, + "QUICK CHAT", + { 640 - 10 - 60 - 4 - 60 - 4 - 80 - 4, 480 - 30, 80, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_QCButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Button, + NULL, +}; + +panel_button_t chatPanelReadyButton = { + NULL, + "READY", + { 640 - 10 - 60 - 4 - 60 - 4 - 80 - 4 - 60 - 4, 480 - 30, 60, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_ReadyButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_ReadyButton_Draw, + NULL, +}; + +panel_button_t chatTypeButton = { + NULL, + NULL, + { 10 + 4, 480 - 30, 80, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_Debriefing_ChatButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_ChatButton_Draw, + NULL, +}; + +panel_button_t charPanelEditSurround = { + NULL, + NULL, + { 10 + 4 + 80 + 4, 480 - 30, 252, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_PanelButtonsRender_Button, + NULL, +}; + +panel_button_t charPanelEdit = { + NULL, + "chattext", + { 10 + 4 + 80 + 4 + 8, 480 - 34, 236, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &chatPanelButtonFont, /* font */ + NULL, /*BG_PanelButton_EditClick,*/ /* keyDown */ + NULL, /* keyUp */ + CG_Debriefing_ChatEdit_Draw, + CG_Debriefing_ChatEditFinish, +}; + +void CG_Debriefing_ChatEdit_Draw( panel_button_t* button ) { +// qboolean useCvar = button->data[0] ? qfalse : qtrue; + int offset = -1; + char buffer[256 + 1]; + const char* cs; + + trap_Cvar_VariableStringBuffer( button->text, buffer, sizeof( buffer ) ); + + if ( ( cg.time / 1000 ) % 2 ) { + if ( trap_Key_GetOverstrikeMode() ) { + Q_strcat( buffer, sizeof( buffer ), "^7|" ); + } else { + Q_strcat( buffer, sizeof( buffer ), "^7_" ); + } + } else { + Q_strcat( buffer, sizeof( buffer ), " " ); + } + + do { + offset++; + if ( buffer + offset == '\0' ) { + break; + } + } while ( CG_Text_Width_Ext( buffer + offset, button->font->scalex, 0, button->font->font ) > button->rect.w ); + + switch ( cgs.dbChatMode ) { + case 0: + cs = va( "^7%s", buffer + offset ); + break; + case 1: + cs = va( "^5%s", buffer + offset ); + break; + case 2: + cs = va( "^3%s", buffer + offset ); + break; + default: + cs = ""; + break; + } + + + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->rect.h, button->font->scalex, button->font->scaley, button->font->colour, cs, 0, 0, button->font->style, button->font->font ); +} + +void CG_Debriefing_ChatBox_Draw( panel_button_t* button ) { + int w, h; + int i, len; + vec4_t hcolor; + float lineHeight = 9.f; + + int chatWidth = button->rect.w; + int chatHeight = button->rect.h; + + if ( cgs.teamLastChatPos != cgs.teamChatPos ) { + h = ( cgs.teamChatPos - cgs.teamLastChatPos ) * TINYCHAR_HEIGHT; + + w = 0; + + for ( i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++ ) { + len = CG_Text_Width_Ext( cgs.teamChatMsgs[i % chatHeight], 0.2f, 0, &cgs.media.limboFont2 ); + if ( len > w ) { + w = len; + } + } + w *= TINYCHAR_WIDTH; + w += TINYCHAR_WIDTH * 2; + + for ( i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i-- ) { + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { + hcolor[0] = 1; + hcolor[1] = 0; + hcolor[2] = 0; + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + hcolor[0] = 0; + hcolor[1] = 0; + hcolor[2] = 1; + } else { + hcolor[0] = 0; + hcolor[1] = 1; + hcolor[2] = 0; + } + + hcolor[3] = 0.33f; + + trap_R_SetColor( hcolor ); + CG_DrawPic( button->rect.x, button->rect.y - ( cgs.teamChatPos - i ) * lineHeight, chatWidth, lineHeight, cgs.media.teamStatusBar ); + + trap_R_SetColor( NULL ); + + if ( cgs.teamChatMsgTeams[i % chatHeight] == TEAM_AXIS ) { + CG_DrawPic( button->rect.x, button->rect.y - ( cgs.teamChatPos - i - 1 ) * lineHeight - 8, 12, 10, cgs.media.axisFlag ); + } else if ( cgs.teamChatMsgTeams[i % chatHeight] == TEAM_ALLIES ) { + CG_DrawPic( button->rect.x, button->rect.y - ( cgs.teamChatPos - i - 1 ) * lineHeight - 8, 12, 10, cgs.media.alliedFlag ); + } + + CG_Text_Paint_Ext( button->rect.x + 12, button->rect.y - ( cgs.teamChatPos - i - 1 ) * lineHeight, 0.2f, 0.2f, colorWhite, cgs.teamChatMsgs[i % chatHeight], 0, 0, 0, &cgs.media.limboFont2 ); + } + } +} + + +panel_button_t* chatPanelButtons[] = { + &chatPanelWindow, &chatPanelText, + + &chatPanelNextButton, &chatPanelQCButton, &chatTypeButton, &chatPanelReadyButton, + + &charPanelEditSurround, &charPanelEdit, + + NULL +}; + +void CG_ChatPanel_Setup( void ) { + BG_PanelButtonsSetup( chatPanelButtons ); + BG_PanelButtonsSetup( teamDebriefPanelButtons ); + BG_PanelButtonsSetup( debriefPanelButtons ); +} + +void CG_Debriefing_Startup( void ) { + const char *s, *buf; + + cgs.dbShowing = qtrue; + cgs.dbAccuraciesRecieved = qfalse; + cgs.dbWeaponStatsRecieved = qfalse; + cgs.dbPlayerKillsDeathsRecieved = qfalse; + + cgs.dbLastRequestTime = 0; + cgs.dbSelectedClient = cg.clientNum; + + cgs.dbAwardsParsed = qfalse; + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + buf = Info_ValueForKey( s, "winner" ); + + trap_Cvar_Set( "chattext", "" ); + + if ( atoi( buf ) == -1 ) { + } else if ( atoi( buf ) ) { + trap_S_StartLocalSound( trap_S_RegisterSound( "sound/music/allies_win.wav", qtrue ), CHAN_LOCAL_SOUND ); + } else { + trap_S_StartLocalSound( trap_S_RegisterSound( "sound/music/axis_win.wav", qtrue ), CHAN_LOCAL_SOUND ); + } + + cgs.dbMode = 0; +} + +void CG_Debriefing_Shutdown( void ) { + cgs.dbShowing = qfalse; +} + +void CG_Debriefing_InfoRequests( void ) { + if ( cgs.dbLastRequestTime && ( cg.time - cgs.dbLastRequestTime ) < 1000 ) { + return; + } + cgs.dbLastRequestTime = cg.time; + + if ( !cgs.dbPlayerKillsDeathsRecieved ) { + trap_SendClientCommand( "impkd" ); + return; + } + + if ( !cgs.dbAccuraciesRecieved ) { + trap_SendClientCommand( "imwa" ); + return; + } + + if ( !cgs.dbWeaponStatsRecieved ) { + trap_SendClientCommand( va( "imws %i", cgs.dbSelectedClient ) ); + return; + } + + // if nothing else is pending, ask for scores + if ( !cgs.dbLastScoreRequest || ( cg.time - cgs.dbLastScoreRequest ) > 1000 ) { + cgs.dbLastScoreRequest = cg.time; + trap_SendClientCommand( "score" ); + } +} + +qboolean CG_Debriefing_Draw( void ) { + int i; + + if ( !cgs.dbShowing ) { + CG_Debriefing_Startup(); + } + + CG_Debriefing_InfoRequests(); + +// CG_FillRect( 0, 0, 640, 480, colorBlack ); + + if ( trap_Key_GetCatcher() & KEYCATCH_UI ) { + return qtrue; + } + + if ( !trap_Key_GetCatcher() ) { + trap_Key_SetCatcher( KEYCATCH_CGAME ); + } + + switch ( cgs.dbMode ) { + case 1: + BG_PanelButtonsRender( teamDebriefPanelButtons ); + BG_PanelButtonsRender( chatPanelButtons ); + + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + + break; + case 0: + CG_DrawScoreboard(); + + BG_PanelButtonsRender( chatPanelButtons ); + + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + break; + case 2: + for ( i = 0 ; i < MAX_CLIENTS; i++ ) { + cgs.dbSortedClients[i] = i; + } + + qsort( cgs.dbSortedClients, MAX_CLIENTS, sizeof( int ), CG_SortPlayersByXP ); + + BG_PanelButtonsRender( debriefPanelButtons ); + + BG_PanelButtonsRender( chatPanelButtons ); + + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + break; + } + + return qtrue; +} + +qboolean CG_DebriefingPlayerList_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + int pos = ( ( cgs.cursorY - DH_HEADING_Y ) / 12 ) + cgs.dbPlayerListOffset; + if ( pos < 0 || pos >= MAX_CLIENTS ) { + return qfalse; + } + + pos = cgs.dbSortedClients[pos]; + + if ( !cgs.clientinfo[pos].infoValid ) { + return qfalse; + } + + + CG_Debrieing_SetSelectedClient( pos ); + + return qtrue; + } + + return qfalse; +} + +int CG_Debriefing_GetNextWeaponStat( int pos ) { + int i; + + for ( i = pos + 1; i < WS_MAX; i++ ) { + if ( cgs.dbWeaponStats[i].numShots ) { + return i; + } + } + + return -1; +} + +void CG_DebriefingPlayerWeaponStats_Draw( panel_button_t* button ) { + int i; + float y = button->rect.y + 12; + int pos = 0; +// float x; + + if ( !cgs.dbWeaponStatsRecieved ) { + return; + } + + pos = CG_Debriefing_GetNextWeaponStat( -1 ); + for ( i = cgs.dbWeaponListOffset; i > 0 && pos != -1; i-- ) { + pos = CG_Debriefing_GetNextWeaponStat( pos ); + } + + for ( i = 0; i < 7 && pos != -1; i++, pos = CG_Debriefing_GetNextWeaponStat( pos ) ) { + CG_Text_Paint_Ext( 18, y, button->font->scalex, button->font->scaley, button->font->colour, aWeaponInfo[pos].pszName, 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( 78, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", cgs.dbWeaponStats[pos].numShots ), 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( 118, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", cgs.dbWeaponStats[pos].numHits ), 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( 148, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", cgs.dbWeaponStats[pos].numKills ), 0, 0, 0, button->font->font ); + + y += 12; + } +} + + +const char* CG_Debriefing_TimeToString( float msec ) { + int mins, seconds, tens; + + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + + return va( "%i:%i%i", mins, tens, seconds ); +} + +void CG_DebriefingTitle_Draw( panel_button_t* button ) { + const char *s, *buf; + float x, w; + + if ( cg_gameType.integer == GT_WOLF_STOPWATCH ) { + int defender, winner; + + s = CG_ConfigString( CS_MULTI_INFO ); + defender = atoi( Info_ValueForKey( s, "defender" ) ); + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + winner = atoi( Info_ValueForKey( s, "winner" ) ); + + if ( cgs.currentRound ) { + // first round + s = va( CG_TranslateString( "Clock is now set to %s!" ), CG_Debriefing_TimeToString( cgs.nextTimeLimit * 60.f * 1000.f ) ); + } else { + // second round + if ( !defender ) { + if ( winner != defender ) { + s = "ALLIES SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "ALLIES COULDN'T BEAT THE CLOCK!"; + } + } else { + if ( winner != defender ) { + s = "AXIS SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "AXIS COULDN'T BEAT THE CLOCK!"; + } + } + } + } else { + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + buf = Info_ValueForKey( s, "winner" ); + + if ( atoi( buf ) == -1 ) { + // neutral + s = "It's a TIE!"; + } else if ( atoi( buf ) ) { + // allies + s = "Allies Win!"; + } else { + // axis + s = "Axis Win!"; + } + } + + w = CG_Text_Width_Ext( s, button->font->scalex, 0, button->font->font ); + x = button->rect.x + 4; // + ((button->rect.w - w) * 0.5f); + + CG_Text_Paint_Ext( x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, s, 0, 0, 0, button->font->font ); + + s = va( "%i seconds to next map", max( 60 - ( cg.time - cgs.intermissionStartTime ) / 1000, 0 ) ); + w = CG_Text_Width_Ext( s, button->font->scalex, 0, button->font->font ); + x = button->rect.x + button->rect.w - w - 4; + + CG_Text_Paint_Ext( x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, s, 0, 0, 0, button->font->font ); +} + +void CG_DebriefingXPHeader_Draw( panel_button_t* button ) { + if ( cgs.gametype == GT_WOLF_LMS ) { + BG_PanelButtonsRender_TextExt( button, "Score" ); + } else { + BG_PanelButtonsRender_TextExt( button, "XP" ); + } +} + +void CG_DebriefingPlayerList_Draw( panel_button_t* button ) { + int i, j; + float y = button->rect.y + 12; +// float x; + score_t* score = NULL; + +// CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + + for ( i = 0; i + cgs.dbPlayerListOffset < MAX_CLIENTS && i < 24; i++ ) { + clientInfo_t* ci = &cgs.clientinfo[cgs.dbSortedClients[i + cgs.dbPlayerListOffset]]; + if ( !ci->infoValid ) { + break; + } + + for ( j = 0; j < MAX_CLIENTS; j++ ) { + if ( cg.scores[j].client == cgs.dbSortedClients[i + cgs.dbPlayerListOffset] ) { + score = &cg.scores[j]; + break; + } + } + if ( j == MAX_CLIENTS ) { + continue; + } + + if ( cgs.dbSelectedClient == cgs.dbSortedClients[i + cgs.dbPlayerListOffset] ) { + vec4_t clr = { 1.f, 1.f, 1.f, 0.3f }; + CG_FillRect( button->rect.x, y - 10, 640 - 10 - 8 - 16 - button->rect.x, 12, clr ); + } + + CG_Text_Paint_Ext( DB_RANK_X, y, button->font->scalex, button->font->scaley, button->font->colour, CG_Debriefing_RankNameForClientInfo( ci ), 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( DB_NAME_X, y, button->font->scalex, button->font->scaley, button->font->colour, ci->name, 0, 28, 0, button->font->font ); + +/* x = DB_MEDALS_X; + for( j = 0; j < SK_NUM_SKILLS; j++ ) { + if( ci->medals[j] ) { + CG_DrawPic( x, y - 9, 10, 10, cgs.media.medals[j] ); + } + x += 12; + }*/ + + CG_Text_Paint_Ext( DB_TIME_X, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", score->time ), 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( DB_XP_X, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", ci->score ), 0, 0, 0, button->font->font ); + + if ( cgs.dbPlayerKillsDeathsRecieved ) { + CG_Text_Paint_Ext( DB_KILLS_X, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", ci->kills ), 0, 0, 0, button->font->font ); + CG_Text_Paint_Ext( DB_DEATHS_X, y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", ci->deaths ), 0, 0, 0, button->font->font ); + } else { + CG_Text_Paint_Ext( DB_KILLS_X, y, button->font->scalex, button->font->scaley, button->font->colour, "-", 0, 0, 0, button->font->font ); + CG_Text_Paint_Ext( DB_DEATHS_X, y, button->font->scalex, button->font->scaley, button->font->colour, "-", 0, 0, 0, button->font->font ); + } + + y += 12; + } +} + + + + + + + + + + + + + + + + + + + +int QDECL CG_SortPlayersByXP( const void *a, const void *b ) { + int ca = *(int*)a; + int cb = *(int*)b; + + if ( !cgs.clientinfo[cb].infoValid ) { + return -1; + } + if ( !cgs.clientinfo[ca].infoValid ) { + return 1; + } + + if ( cgs.clientinfo[cb].score > cgs.clientinfo[ca].score ) { + return 1; + } + if ( cgs.clientinfo[ca].score > cgs.clientinfo[cb].score ) { + return -1; + } + + return 0; +} + +const char* CG_Debriefing_FullRankNameForClientInfo( clientInfo_t* ci ) { + if ( ci->team != TEAM_AXIS && ci->team != TEAM_ALLIES ) { + return "Spectator"; + } + + return ci->team == TEAM_AXIS ? rankNames_Axis[ci->rank] : rankNames_Allies[ci->rank]; +} + +const char* CG_Debriefing_RankNameForClientInfo( clientInfo_t* ci ) { + if ( ci->team != TEAM_AXIS && ci->team != TEAM_ALLIES ) { + return "Spc"; + } + + return ci->team == TEAM_AXIS ? miniRankNames_Axis[ci->rank] : miniRankNames_Allies[ci->rank]; +} + +void CG_Debriefing_ParseWeaponAccuracies( void ) { + int i; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + cgs.clientinfo[i].totalWeapAcc = atoi( CG_Argv( i + 1 ) ); + } + cgs.dbAccuraciesRecieved = qtrue; +} + +void CG_Debriefing_ParsePlayerKillsDeaths( void ) { + int i; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + cgs.clientinfo[i].kills = atoi( CG_Argv( i * 2 + 1 ) ); + cgs.clientinfo[i].deaths = atoi( CG_Argv( i * 2 + 2 ) ); + } + cgs.dbPlayerKillsDeathsRecieved = qtrue; +} + +void CG_Debriefing_ParseWeaponStats( void ) { + int i; + for ( i = 0; i < WS_MAX; i++ ) { + cgs.dbWeaponStats[i].numShots = atoi( CG_Argv( ( i * 3 ) + 1 ) ); + cgs.dbWeaponStats[i].numHits = atoi( CG_Argv( ( i * 3 ) + 2 ) ); + cgs.dbWeaponStats[i].numKills = atoi( CG_Argv( ( i * 3 ) + 3 ) ); + } + + cgs.dbWeaponStatsRecieved = qtrue; +} + +qboolean CG_Debriefing_ServerCommand( const char* cmd ) { + if ( !Q_stricmp( cmd, "imwa" ) ) { + CG_Debriefing_ParseWeaponAccuracies(); + return qtrue; + } + + if ( !Q_stricmp( cmd, "imws" ) ) { + CG_Debriefing_ParseWeaponStats(); + return qtrue; + } + + if ( !Q_stricmp( cmd, "impkd" ) ) { + CG_Debriefing_ParsePlayerKillsDeaths(); + return qtrue; + } + + return qfalse; +} + +int CG_Debriefing_ScrollGetMax( panel_button_t* button ) { + switch ( button->data[0] ) { + case 0: // player list + return 24; + case 1: + return 7; + case 2: + return 7; + } + return 0; +} + +int CG_Debriefing_ScrollGetCount( panel_button_t* button ) { + int i, cnt = 0; + + switch ( button->data[0] ) { + case 0: // player list + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( !cgs.clientinfo[cgs.dbSortedClients[i]].infoValid ) { + return i; + } + } + return MAX_CLIENTS; + case 1: + if ( !cgs.dbWeaponStatsRecieved ) { + return 0; + } + for ( i = 0; i < WS_MAX; i++ ) { + if ( cgs.dbWeaponStats[i].numShots ) { + cnt++; + } + } + return cnt; + case 2: + if ( cgs.campaignInfoLoaded ) { + return cgs.campaignData.mapCount; + } + return 0; + + } + return 0; +} + +int CG_Debriefing_ScrollGetOffset( panel_button_t* button ) { + switch ( button->data[0] ) { + case 0: // player list + return cgs.dbPlayerListOffset; + case 1: + return cgs.dbWeaponListOffset; + case 2: + return cgs.tdbMapListOffset; + } + return 0; +} + +void CG_Debriefing_ScrollSetOffset( panel_button_t* button, int ofs ) { + switch ( button->data[0] ) { + case 0: + cgs.dbPlayerListOffset = ofs; + return; + case 1: + cgs.dbWeaponListOffset = ofs; + return; + case 2: + cgs.tdbMapListOffset = ofs; + return; + } +} + +void CG_Debriefing_ScrollGetBarRect( panel_button_t* button, rectDef_t* r ) { + int max = CG_Debriefing_ScrollGetMax( button ); + int cnt = CG_Debriefing_ScrollGetCount( button ); + int offset = CG_Debriefing_ScrollGetOffset( button ); + + if ( cnt > max ) { + float h = button->rect.h; + r->h = h * ( max / (float)cnt ); + r->y = button->rect.y + ( offset / (float)( cnt - max ) ) * ( h - r->h ); + } else { + r->h = button->rect.h; + r->y = button->rect.y; + } + + r->x = button->rect.x; + r->w = button->rect.w; +} + +void CG_Debriefing_ScrollCheckOffset( panel_button_t* button ) { + int max = CG_Debriefing_ScrollGetMax( button ); + int cnt = CG_Debriefing_ScrollGetCount( button ); + int offset = CG_Debriefing_ScrollGetOffset( button ); + + int maxofs = max( 0, cnt - max ); + + if ( offset > maxofs ) { + CG_Debriefing_ScrollSetOffset( button, maxofs ); + } else if ( offset < 0 ) { + CG_Debriefing_ScrollSetOffset( button, 0 ); + } +} + +void CG_Debriefing_MouseEvent( int x, int y ) { + panel_button_t* button; + + switch ( cgs.dbMode ) { + case 2: + button = BG_PanelButtons_GetFocusButton(); + if ( button && button->onDraw == CG_Debriefing_Scrollbar_Draw ) { + rectDef_t r; + int count, cnt; + + cnt = CG_Debriefing_ScrollGetCount( button ); + CG_Debriefing_ScrollGetBarRect( button, &r ); + + button->data[1] += y; + + count = ( cnt * button->data[1] * 0.5f ) / (float)( r.h ); + if ( count ) { + int ofs = CG_Debriefing_ScrollGetOffset( button ); + CG_Debriefing_ScrollSetOffset( button, ofs + count ); + CG_Debriefing_ScrollCheckOffset( button ); + ofs = CG_Debriefing_ScrollGetOffset( button ) - ofs; + + if ( ofs == count ) { + button->data[1] -= ofs * ( r.h / (float)cnt ); + } + } + + CG_Debriefing_ScrollGetBarRect( button, &r ); + cgs.cursorY = r.y + button->data[2]; + + return; + } + break; + default: + break; + } + + + cgs.cursorX += x; + if ( cgs.cursorX < 0 ) { + cgs.cursorX = 0; + } else if ( cgs.cursorX > 640 ) { + cgs.cursorX = 640; + } + + cgs.cursorY += y; + if ( cgs.cursorY < 0 ) { + cgs.cursorY = 0; + } else if ( cgs.cursorY > 480 ) { + cgs.cursorY = 480; + } +} + +void CG_Debriefing_Scrollbar_Draw( panel_button_t* button ) { + rectDef_t r; + vec4_t clr1 = { 41 / 255.f, 51 / 255.f, 43 / 255.f, 204 / 255.f }; + vec4_t clr2 = { 0.f, 0.f, 0.f, 153 / 255.f }; + + + CG_Debriefing_ScrollCheckOffset( button ); + + CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, clr2 ); + CG_DrawRect_FixedBorder( button->rect.x, button->rect.y, button->rect.w, button->rect.h, 1, colorMdGrey ); + + CG_Debriefing_ScrollGetBarRect( button, &r ); + + CG_FillRect( r.x, r.y, r.w, r.h, clr1 ); + CG_DrawRect_FixedBorder( r.x, r.y, r.w, r.h, 1, colorMdGrey ); +} + +qboolean CG_Debriefing_Scrollbar_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + rectDef_t r; + CG_Debriefing_ScrollGetBarRect( button, &r ); + if ( BG_CursorInRect( &r ) ) { + BG_PanelButtons_SetFocusButton( button ); + button->data[1] = 0; + button->data[2] = cgs.cursorY - r.y; + } + } + + return qfalse; +} + +qboolean CG_Debriefing_Scrollbar_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( BG_PanelButtons_GetFocusButton() == button ) { + BG_PanelButtons_SetFocusButton( NULL ); + } + } + return qfalse; +} + +void CG_Debriefing_KeyEvent( int key, qboolean down ) { + switch ( cgs.dbMode ) { + case 1: + if ( BG_PanelButtonsKeyEvent( key, down, teamDebriefPanelButtons ) ) { + return; + } + break; + case 0: + break; + case 2: + if ( BG_PanelButtonsKeyEvent( key, down, debriefPanelButtons ) ) { + return; + } + break; + } + + if ( BG_PanelButtonsKeyEvent( key, down, chatPanelButtons ) ) { + return; + } + + if ( !BG_PanelButtons_GetFocusButton() && down && key != K_MOUSE1 ) { + BG_PanelButtons_SetFocusButton( &charPanelEdit ); + BG_PanelButton_EditClick( &charPanelEdit, key ); + BG_PanelButtons_SetFocusButton( NULL ); + } +} + +void CG_Debriefing_PlayerSkills_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + int i; + float x; + + CG_Text_Paint_Ext( button->rect.x, button->rect.y - 2, button->font->scalex, button->font->scaley, button->font->colour, skillNames[button->data[0]], 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + x = button->rect.x; + CG_DrawPic( x, button->rect.y, button->rect.w, button->rect.h, cgs.media.skillPics[ button->data[0] ] ); + + x += button->rect.w + 2; + for ( i = ci->skill[button->data[0]]; i > 0; i-- ) { + + CG_DrawPicST( x, button->rect.y, button->rect.w, button->rect.h, 0, 0, 1.f, 0.5f, cgs.media.limboStar_roll ); + + x += button->rect.w + 2; + } + + { + vec4_t clr = { 1.f, 1.f, 1.f, 0.2f }; + trap_R_SetColor( clr ); + for ( i = ci->skill[button->data[0]]; i < 4; i++ ) { + + CG_DrawPicST( x, button->rect.y, button->rect.w, button->rect.h, 0, 0, 1.f, 0.5f, cgs.media.limboStar_roll ); + + x += button->rect.w + 2; + } + trap_R_SetColor( NULL ); + } +} + + +void CG_Debriefing_PlayerACC_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + float w; + + w = CG_Text_Width_Ext( "ACC: ", button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, "ACC:", 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i%%", ci->totalWeapAcc ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); +} + +void CG_Debriefing_PlayerXP_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + float w; + + w = CG_Text_Width_Ext( "XP: ", button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, "XP:", 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", ci->score ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); +} + + +void CG_Debriefing_PlayerTime_Draw( panel_button_t* button ) { + score_t* score = NULL; + int i; + float w; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( cg.scores[i].client == cgs.dbSelectedClient ) { + score = &cg.scores[i]; + break; + } + } + if ( !score ) { + return; + } + + w = CG_Text_Width_Ext( "Time: ", button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, "Time:", 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", score->time ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); +} + +void CG_Debriefing_PlayerMedals_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + float w, x = button->rect.x; + int i; + + w = CG_Text_Width_Ext( "Medals: ", button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, "Medals:", 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + x = button->rect.x; + for ( i = 0; i < SK_NUM_SKILLS; i++ ) { + if ( ci->medals[i] ) { + CG_DrawPic( x, button->rect.y - 10, 16, 16, cgs.media.medals[i] ); + + x += 16 + 2; + } + } +} + +void CG_Debriefing_PlayerRank_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + float w; + + w = CG_Text_Width_Ext( "Rank: ", button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, "Rank:", 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, CG_Debriefing_FullRankNameForClientInfo( ci ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); +} + +void CG_Debriefing_PlayerName_Draw( panel_button_t* button ) { + clientInfo_t* ci = CG_Debriefing_GetSelectedClientInfo(); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, ci->name, 0, 0, ITEM_TEXTSTYLE_SHADOWED, button->font->font ); +} + +clientInfo_t* CG_Debriefing_GetSelectedClientInfo( void ) { + clientInfo_t* ci; + + if ( cgs.dbSelectedClient < 0 || cgs.dbSelectedClient > MAX_CLIENTS ) { + CG_Debrieing_SetSelectedClient( cg.clientNum ); + } + + ci = &cgs.clientinfo[cgs.dbSelectedClient]; + if ( !ci->infoValid ) { + CG_Debrieing_SetSelectedClient( cg.clientNum ); + ci = &cgs.clientinfo[cgs.dbSelectedClient]; + } + + return ci; +} + +void CG_Debrieing_SetSelectedClient( int clientNum ) { + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + return; + } + + if ( clientNum != cgs.dbSelectedClient ) { + cgs.dbSelectedClient = clientNum; + cgs.dbWeaponStatsRecieved = qfalse; + } +} + +void CG_Debriefing_HTMLButton_Draw( panel_button_t* button ) { + if ( cgs.dbMode != 0 ) { + return; + } + + CG_PanelButtonsRender_Button( button ); +} + +qboolean CG_Debriefing_ChatButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + cgs.dbChatMode = ( cgs.dbChatMode + 1 ) % 3; + + if ( cgs.dbChatMode > 0 ) { + if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR ) { + cgs.dbChatMode = 0; + } + + if ( cgs.dbChatMode > 1 ) { + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + cgs.dbChatMode = 0; + } + } + } + + return qtrue; + } + + return qfalse; +} + +void CG_Debriefing_ReadyButton_Draw( panel_button_t* button ) { + if ( !cg.snap ) { + return; + } + + if ( cg.snap->ps.eFlags & EF_READY ) { + return; + } + + CG_PanelButtonsRender_Button( button ); +} + +void CG_Debriefing_ChatButton_Draw( panel_button_t* button ) { + const char* str; + + switch ( cgs.dbChatMode ) { + case 1: + str = "^5TO TEAM"; + break; + case 2: + str = "^3TO FIRETEAM"; + break; + default: + str = "^2TO GLOBAL"; + break; + } + + CG_PanelButtonsRender_Button_Ext( &button->rect, str ); +} + + +void CG_QuickMessage_f( void ); + +qboolean CG_Debriefing_ReadyButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( !cg.snap ) { + return qfalse; + } + + if ( cg.snap->ps.eFlags & EF_READY ) { + return qfalse; + } + + trap_SendClientCommand( "imready" ); + + return qtrue; + } + + return qfalse; +} + +qboolean CG_Debriefing_QCButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + CG_QuickMessage_f(); + return qtrue; + } + return qfalse; +} + +qboolean CG_Debriefing_NextButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + cgs.dbMode = ( cgs.dbMode + 1 ) % 3; + return qtrue; + } + + return qfalse; +} + +void CG_Debriefing_NextButton_Draw( panel_button_t* button ) { + CG_PanelButtonsRender_Button( button ); +} + +void CG_Debriefing_ChatEditFinish( panel_button_t* button ) { + char buffer[256]; + trap_Cvar_VariableStringBuffer( button->text, buffer, 256 ); + + switch ( cgs.dbChatMode ) { + case 0: + trap_SendClientCommand( va( "say %s\n", buffer ) ); + break; + case 1: + trap_SendClientCommand( va( "say_team %s\n", buffer ) ); + break; + case 2: + trap_SendClientCommand( va( "say_buddy %s\n", buffer ) ); + break; + } + + trap_Cvar_Set( button->text, "" ); +} + +float CG_Debriefing_CalcCampaignProgress( void ) { + int i; + + if ( !cgs.campaignInfoLoaded ) { + return 0; + } + + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + if ( !Q_stricmp( cgs.campaignData.mapnames[i], cgs.rawmapname ) ) { + return ( i + 1 ) / (float)cgs.campaignData.mapCount; + } + } + + return 0; +} + +qboolean CG_TeamDebriefingMapList_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + int pos = ( ( cgs.cursorY - button->rect.y ) / 12 ) + cgs.tdbMapListOffset; + if ( pos < 0 || pos > cgs.campaignData.mapCount ) { + return qfalse; + } + + cgs.tdbSelectedMap = pos; + + return qtrue; + } + + return qfalse; +} + +void CG_TeamDebriefingOutcome_Draw( panel_button_t* button ) { + const char* cs; + char *s, *p; + char buffer[1024]; + float y; + +// DC->fillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + + if ( cgs.tdbSelectedMap == 0 ) { + return; + } else { + if ( cg.teamWonRounds[1] & ( 1 << ( cgs.tdbSelectedMap - 1 ) ) ) { + cs = cgs.campaignData.arenas[ cgs.tdbMapListOffset - 1].axiswintext; + } else if ( cg.teamWonRounds[0] & ( 1 << ( cgs.tdbSelectedMap - 1 ) ) ) { + cs = cgs.campaignData.arenas[ cgs.tdbMapListOffset - 1].alliedwintext; + } else { + return; + } + } + + Q_strncpyz( buffer, cs, sizeof( buffer ) ); + while ( ( s = strchr( buffer, '*' ) ) ) { + *s = '\n'; + } + + BG_FitTextToWidth_Ext( buffer, button->font->scalex, button->rect.w - 16, sizeof( buffer ), button->font->font ); + + y = button->rect.y + 12; + + s = p = buffer; + while ( *p ) { + if ( *p == '\n' ) { + *p++ = '\0'; + CG_Text_Paint_Ext( button->rect.x + 4, y, button->font->scalex, button->font->scaley, button->font->colour, s, 0, 0, 0, button->font->font ); + y += 8; + s = p; + } else { + p++; + } + } +} + + +void CG_TeamDebriefingMapList_Draw( panel_button_t* button ) { + int i; + float y = button->rect.y + 12; + +// CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + + for ( i = 0; i + cgs.tdbMapListOffset <= MAX_MAPS_PER_CAMPAIGN && i < 4; i++ ) { + if ( cgs.tdbSelectedMap == i + cgs.tdbMapListOffset ) { + vec4_t clr = { 1.f, 1.f, 1.f, 0.3f }; + CG_FillRect( button->rect.x, y - 10, button->rect.w, 12, clr ); + } + + if ( i + cgs.tdbMapListOffset == 0 ) { + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, "Campaign Overview", 0, 0, 0, button->font->font ); + } else { + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, cgs.campaignData.arenas[ i + cgs.tdbMapListOffset - 1].longname, 0, 0, 0, button->font->font ); + } + + y += 12; + } +} + +int CG_TeamDebriefing_CalcXP( team_t team, int mapindex, int skillindex ) { + int i, j, cnt = 0; + + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + if ( mapindex != -1 && i != mapindex ) { + continue; + } + + for ( j = 0; j < SK_NUM_SKILLS; j++ ) { + if ( skillindex != -1 && j != skillindex ) { + continue; + } + + cnt += team == TEAM_AXIS ? cgs.tdbAxisMapsXP[ j ][ i ] : cgs.tdbAlliedMapsXP[ j ][ i ]; + } + } + } else if ( cg_gameType.integer == GT_WOLF || cg_gameType.integer == GT_WOLF_STOPWATCH ) { + for ( j = 0; j < SK_NUM_SKILLS; j++ ) { + if ( skillindex != -1 && j != skillindex ) { + continue; + } + + cnt += team == TEAM_AXIS ? cgs.tdbAxisMapsXP[ j ][ 0 ] : cgs.tdbAlliedMapsXP[ j ][ 0 ]; + } + } + + return cnt; +} + +void CG_TeamDebriefingTeamXP_Draw( panel_button_t* button ) { + team_t team = button->data[0] == 0 ? TEAM_AXIS : TEAM_ALLIES; + + int xp = CG_TeamDebriefing_CalcXP( team, cgs.tdbSelectedMap - 1, -1 ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%s XP: %i", team == TEAM_AXIS ? "Axis" : "Allies", xp ), 0, 0, 0, button->font->font ); +} + +void CG_TeamDebriefingTeamSkillXP_Draw( panel_button_t* button ) { + team_t team = button->data[0] == 0 ? TEAM_AXIS : TEAM_ALLIES; + + int xp; + + if ( button->data[1] == SK_NUM_SKILLS ) { + xp = CG_TeamDebriefing_CalcXP( team, cgs.tdbSelectedMap - 1, -1 ); + } else { + xp = CG_TeamDebriefing_CalcXP( team, cgs.tdbSelectedMap - 1, button->data[1] ); + } + +// const char* text = va( "%s: ", skillNames[ button->data[1] ] ); + +// float w = CG_Text_Width_Ext( text, button->font->scalex, 0, button->font->font ); + +// CG_Text_Paint_Ext( button->rect.x - w, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, text, 0, 0, 0, button->font->font ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%i", xp ), 0, 0, 0, button->font->font ); +} + +void CG_TeamDebriefingMapShot_Draw( panel_button_t* button ) { + if ( cgs.tdbSelectedMap == 0 ) { + CG_DrawPicST( button->rect.x, button->rect.y, button->rect.w, button->rect.h, 0, 0, 0.6875, 1, trap_R_RegisterShaderNoMip( "gfx/loading/map_back" ) ); + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, trap_R_RegisterShaderNoMip( va( "levelshots/%s_cc.tga", cgs.campaignData.mapnames[cgs.tdbSelectedMap - 1] ) ) ); + } +} + +void CG_TeamDebriefingMapWinner_Draw( panel_button_t* button ) { +} + +void CG_PanelButtonsRender_Button_Ext( rectDef_t* r, const char* text ) { + vec4_t clrBdr = { 0.1f, 0.1f, 0.1f, 0.5f }; + vec4_t clrBck = { 0.3f, 0.3f, 0.3f, 0.4f }; + vec4_t clrTxt = { 0.6f, 0.6f, 0.6f, 1.0f }; + + vec4_t clrBck_hi = { 0.5f, 0.5f, 0.5f, 0.4f }; + vec4_t clrTxt_hi = { 0.9f, 0.9f, 0.9f, 1.f }; + + qboolean hilight = BG_CursorInRect( r ); + + CG_FillRect( r->x, r->y, r->w, r->h, hilight ? clrBck_hi : clrBck ); + CG_DrawRect_FixedBorder( r->x, r->y, r->w, r->h, 1, clrBdr ); + + if ( text ) { + float w = CG_Text_Width_Ext( text, 0.2f, 0, &cgs.media.limboFont2 ); + CG_Text_Paint_Ext( r->x + ( ( r->w + 2 ) - w ) * 0.5f, r->y + 11, 0.19f, 0.19f, hilight ? clrTxt_hi : clrTxt, text, 0, 0, 0, &cgs.media.limboFont2 ); + } +} + +void CG_PanelButtonsRender_Button( panel_button_t* button ) { + CG_PanelButtonsRender_Button_Ext( &button->rect, button->text ); +} + +void CG_PanelButtonsRender_Window_Ext( rectDef_t* r, const char* text, int align, int innerheight, float fontscale, int yofs ) { + vec4_t clrBdr = { 0.5f, 0.5f, 0.5f, 0.5f }; + vec4_t clrTitleBck = { 0.16f, 0.2f, 0.17f, 0.8f }; + vec4_t clrBck = { 0.0f, 0.0f, 0.0f, 0.8f }; + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + + CG_FillRect( r->x, r->y, r->w, r->h, clrBck ); + CG_DrawRect_FixedBorder( r->x, r->y, r->w, r->h, 1, clrBdr ); + + CG_FillRect( r->x + 2, r->y + 2, r->w - 4, innerheight, clrTitleBck ); + + if ( text ) { + float x; + + if ( align == ITEM_ALIGN_CENTER ) { + float w = CG_Text_Width_Ext( text, fontscale, 0, &cgs.media.limboFont1 ); + x = r->x + ( r->w - w ) * 0.5f; + } else if ( align == ITEM_ALIGN_RIGHT ) { + float w = CG_Text_Width_Ext( text, fontscale, 0, &cgs.media.limboFont1 ); + x = r->x + r->w - w; + } else { + x = r->x + 5; + } + + CG_Text_Paint_Ext( x, r->y + yofs, fontscale, fontscale, clrTxtBck, text, 0, 0, 0, &cgs.media.limboFont1 ); + } +} + + +void CG_PanelButtonsRender_Window( panel_button_t* button ) { + CG_PanelButtonsRender_Window_Ext( &button->rect, button->text, button->data[0], 12, 0.19f, 11 ); +} + +const char* CG_Debreifing2_WinStringForTeam( team_t team ) { + switch ( team ) { + case TEAM_ALLIES: + return "ALLIES WIN!"; + case TEAM_AXIS: + return "AXIS WIN!"; + default: + return "IT'S A TIE!"; + } +} + +void CG_Debreifing2_MissionTitle_Draw( panel_button_t* button ) { + const char* s; + float x, w; + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( cg_gameType.integer == GT_WOLF_STOPWATCH ) { + int defender, winner; + + s = CG_ConfigString( CS_MULTI_INFO ); + defender = atoi( Info_ValueForKey( s, "defender" ) ); + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + winner = atoi( Info_ValueForKey( s, "winner" ) ); + + if ( cgs.currentRound ) { + // first round + s = va( CG_TranslateString( "Clock is now set to %s!" ), CG_Debriefing_TimeToString( cgs.nextTimeLimit * 60.f * 1000.f ) ); + } else { + // second round + if ( !defender ) { + if ( winner != defender ) { + s = "ALLIES SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "ALLIES COULDN'T BEAT THE CLOCK!"; + } + } else { + if ( winner != defender ) { + s = "AXIS SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "AXIS COULDN'T BEAT THE CLOCK!"; + } + } + } + CG_PanelButtonsRender_Window_Ext( &button->rect, s, 0, 18, 0.25f, 16 ); + } else if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + CG_PanelButtonsRender_Window_Ext( &button->rect, CG_Debreifing2_WinStringForTeam( CG_Debriefing_FindWinningTeamForMap() ), 0, 18, 0.25f, 16 ); + + s = va( "CAMPAIGN STATUS: %s", CG_Debreifing2_WinStringForTeam( CG_Debriefing_FindOveralWinningTeam() ) ); + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( button->rect.x + ( button->rect.w - w ) * 0.5f, button->rect.y + 16, 0.25f, 0.25f, clrTxtBck, s, 0, 0, 0, &cgs.media.limboFont1 ); + } else { + CG_PanelButtonsRender_Window_Ext( &button->rect, CG_Debreifing2_WinStringForTeam( CG_Debriefing_FindOveralWinningTeam() ), 0, 18, 0.25f, 16 ); + } + + s = va( "%i SECS TO NEXT MAP", max( 60 - ( cg.time - cgs.intermissionStartTime ) / 1000, 0 ) ); + + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + x = button->rect.x + button->rect.w - w - 4; + + CG_Text_Paint_Ext( x, button->rect.y + 16, 0.25f, 0.25f, clrTxtBck, s, 0, 0, 0, &cgs.media.limboFont1 ); +} + +const char* awardNames[NUM_ENDGAME_AWARDS] = { + "Highest Fragger", // + "Highest Experience Points", // + "Highest Ranking Officer", // + "Most Highly Decorated", // + "Highest Battle Sense", // min lvl 1 + "Best Engineer", // " + "Best Medic", // " + "Best Field Ops", // " + "Highest Light Weapons", // " + "Best Soldier", // " + "Best Covert Ops", // " + "Highest Accuracy", // + "I Ain't Got No Friends Award", // min 5 tks + "Welcome Newbie! Award", // dont get this if any other award given or > 100 xp (this map) +}; + +void CG_Debreifing2_Awards_Parse( void ) { + int i = 0; + char* cs = (char*)CG_ConfigString( CS_ENDGAME_STATS ); + const char* token; + char* s; + int size, len; + char buffer[sizeof( cgs.dbAwardNamesBuffer )]; + + Q_strncpyz( buffer, cs, sizeof( cgs.dbAwardNamesBuffer ) ); + cs = buffer; + + while ( ( s = strchr( cs, ';' ) ) ) { + *s = '"'; + } + + s = cgs.dbAwardNamesBuffer; + size = sizeof( cgs.dbAwardNamesBuffer ); + + for ( i = 0; i < NUM_ENDGAME_AWARDS; i++ ) { + token = COM_Parse( &cs ); + + Q_strncpyz( s, token, size ); + + cgs.dbAwardNames[ i ] = s; + + len = strlen( token ); + size -= len; + s += len + 1; + + token = COM_Parse( &cs ); + + cgs.dbAwardTeams[ i ] = atoi( token ); + } + + cgs.dbAwardsParsed = qtrue; +} + +void CG_Debreifing2_Awards_Draw( panel_button_t* button ) { + int i; + float y = button->rect.y + 1; + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( !cgs.dbAwardsParsed ) { + CG_Debreifing2_Awards_Parse(); + } + + for ( i = 0; i < NUM_ENDGAME_AWARDS; i++ ) { + if ( cgs.dbAwardTeams[ i ] == -1 ) { + continue; + } + + CG_DrawPic( button->rect.x + 6, y + 2, 18, 12, cgs.dbAwardTeams[ i ] == TEAM_AXIS ? cgs.media.axisFlag : cgs.media.alliedFlag ); + + CG_Text_Paint_Ext( button->rect.x + 28, y + 11, 0.19f, 0.19f, clrTxtBck, awardNames[ i ], 0, 0, 0, &cgs.media.limboFont2 ); + + CG_Text_Paint_Ext( button->rect.x + 28 + 180, y + 11, 0.19f, 0.19f, clrTxtBck, cgs.dbAwardNames[ i ], 0, 0, 0, &cgs.media.limboFont2 ); + y += 16; + } +} + +void CG_Debreifing2_Maps_Draw( panel_button_t* button ) { + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + vec4_t clrBck = { 0.3f, 0.3f, 0.3f, 0.4f }; + + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + float y, w; + int i; + + if ( !cgs.campaignInfoLoaded ) { + return; + } + + if ( cgs.tdbSelectedMap == 0 ) { + CG_FillRect( button->rect.x + 2, button->rect.y + 2, button->rect.w - 4, 12, clrBck ); + } + CG_Text_Paint_Ext( button->rect.x + 4, button->rect.y + 11, 0.19f, 0.19f, clrTxtBck, va( "Campaign: %s", cgs.campaignData.campaignName ), 0, 0, 0, &cgs.media.limboFont2 ); + + y = button->rect.y + 14; + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + const char* str; + + if ( cgs.tdbSelectedMap == i + 1 ) { + CG_FillRect( button->rect.x + 2, y + 2, button->rect.w - 4, 12, clrBck ); + } + + CG_Text_Paint_Ext( button->rect.x + 8, y + 11, 0.19f, 0.19f, clrTxtBck, va( "%i. %s", i + 1, cgs.campaignData.arenas[i].longname ), 0, 0, 0, &cgs.media.limboFont2 ); + + if ( i <= cgs.currentCampaignMap ) { + str = CG_Debreifing2_WinStringForTeam( CG_Debriefing_FindWinningTeamForPos( i + 1 ) ); + + w = CG_Text_Width_Ext( str, 0.2f, 0, &cgs.media.limboFont2 ); + CG_Text_Paint_Ext( button->rect.x + button->rect.w - w - 8, y + 11, 0.19f, 0.19f, clrTxtBck, str, 0, 0, 0, &cgs.media.limboFont2 ); + } + + y += 14; + } + } else if ( cg_gameType.integer == GT_WOLF_STOPWATCH ) { + } +} + +void CG_Debreifing2_Mission_Draw( panel_button_t* button ) { + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + if ( !cgs.campaignInfoLoaded ) { + return; + } + + if ( cgs.campaignData.mapTC[0][0] && cgs.campaignData.mapTC[1][0] ) { + int i; + + CG_DrawPicST( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.campaignData.mapTC[0][0] / 1024.f, cgs.campaignData.mapTC[0][1] / 1024.f, cgs.campaignData.mapTC[1][0] / 1024.f, cgs.campaignData.mapTC[1][1] / 1024.f, trap_R_RegisterShaderNoMip( "gfx/loading/camp_map" ) ); + + for ( i = cgs.campaignData.mapCount - 1; i >= 0; i-- ) { + float x, y, w; + + vec4_t colourFadedBlack = { 0.f, 0.f, 0.f, 0.4f }; + + x = button->rect.x + ( ( cgs.campaignData.arenas[i].mappos[0] - cgs.campaignData.mapTC[0][0] ) / 650.f * button->rect.w ); + y = button->rect.y + ( ( cgs.campaignData.arenas[i].mappos[1] - cgs.campaignData.mapTC[0][1] ) / 650.f * button->rect.h ); + + w = CG_Text_Width_Ext( cgs.campaignData.arenas[i].longname, 0.2f, 0, &cgs.media.limboFont2 ); + + if ( x + 10 + w > button->rect.x + button->rect.w ) { + CG_FillRect( x - w - 12 + 1, y - 6 + 1, 12 + w, 12, colourFadedBlack ); + CG_FillRect( x - w - 12, y - 6, 12 + w, 12, colorBlack ); + } else { + CG_FillRect( x + 1, y - 6 + 1, 12 + w, 12, colourFadedBlack ); + CG_FillRect( x, y - 6, 12 + w, 12, colorBlack ); + } + + switch ( CG_Debriefing_FindWinningTeamForPos( i + 1 ) ) { + case TEAM_AXIS: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_axis" ) ); + break; + case TEAM_ALLIES: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_allied" ) ); + break; + default: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_neutral" ) ); + break; + } + + if ( x + 10 + w > button->rect.x + button->rect.w ) { + CG_Text_Paint_Ext( x - w - 10, y + 3, 0.2f, 0.2f, colorWhite, cgs.campaignData.arenas[i].longname, 0, 0, 0, &cgs.media.limboFont2 ); + } else { + CG_Text_Paint_Ext( x + 10, y + 3, 0.2f, 0.2f, colorWhite, cgs.campaignData.arenas[i].longname, 0, 0, 0, &cgs.media.limboFont2 ); + } + } + + if ( cgs.tdbSelectedMap ) { + float x, y; + + x = button->rect.x + ( ( cgs.campaignData.arenas[cgs.tdbSelectedMap - 1].mappos[0] - cgs.campaignData.mapTC[0][0] ) / 650.f * button->rect.w ); + y = button->rect.y + ( ( cgs.campaignData.arenas[cgs.tdbSelectedMap - 1].mappos[1] - cgs.campaignData.mapTC[0][1] ) / 650.f * button->rect.h ); + + switch ( CG_Debriefing_FindWinningTeamForPos( cgs.tdbSelectedMap ) ) { + case TEAM_AXIS: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_axis" ) ); + break; + case TEAM_ALLIES: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_allied" ) ); + break; + default: + break; + } + } + + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, trap_R_RegisterShaderNoMip( "menu/art/unknownmap" ) ); + } + return; + } + + if ( !cgs.arenaInfoLoaded ) { + return; + } + + if ( cgs.arenaData.mappos[0] && cgs.arenaData.mappos[1] ) { + float x, y, w; + vec2_t tl, br; + vec4_t colourFadedBlack = { 0.f, 0.f, 0.f, 0.4f }; + + tl[0] = cgs.arenaData.mappos[0] - .5 * 650.f; + if ( tl[0] < 0 ) { + tl[0] = 0; + } + br[0] = tl[0] + 650.f; + if ( br[0] > 1024.f ) { + br[0] = 1024.f; + tl[0] = br[0] - 650.f; + } + + tl[1] = cgs.arenaData.mappos[1] - .5 * 650.f; + if ( tl[1] < 0 ) { + tl[1] = 0; + } + br[1] = tl[1] + 650.f; + if ( br[1] > 1024.f ) { + br[1] = 1024.f; + tl[1] = br[1] - 650.f; + } + + CG_DrawPicST( button->rect.x, button->rect.y, button->rect.w, button->rect.h, tl[0] / 1024.f, tl[1] / 1024.f, br[0] / 1024.f, br[1] / 1024.f, trap_R_RegisterShaderNoMip( "gfx/loading/camp_map" ) ); + + x = button->rect.x + ( ( cgs.arenaData.mappos[0] - tl[0] ) / 650.f * button->rect.w ); + y = button->rect.y + ( ( cgs.arenaData.mappos[1] - tl[1] ) / 650.f * button->rect.h ); + + w = CG_Text_Width_Ext( cgs.arenaData.longname, 0.2f, 0, &cgs.media.limboFont2 ); + + if ( x + 10 + w > button->rect.x + button->rect.w ) { + CG_FillRect( x - w - 12 + 1, y - 6 + 1, 12 + w, 12, colourFadedBlack ); + CG_FillRect( x - w - 12, y - 6, 12 + w, 12, colorBlack ); + } else { + CG_FillRect( x + 1, y - 6 + 1, 12 + w, 12, colourFadedBlack ); + CG_FillRect( x, y - 6, 12 + w, 12, colorBlack ); + } + + switch ( CG_Debriefing_FindWinningTeam() ) { + case TEAM_AXIS: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_axis" ) ); + break; + case TEAM_ALLIES: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_allied" ) ); + break; + default: + CG_DrawPic( x - 12, y - 12, 24, 24, trap_R_RegisterShaderNoMip( "gfx/loading/pin_neutral" ) ); + break; + } + + if ( x + 10 + w > button->rect.x + button->rect.w ) { + CG_Text_Paint_Ext( x - w - 10, y + 3, 0.2f, 0.2f, colorWhite, cgs.arenaData.longname, 0, 0, 0, &cgs.media.limboFont2 ); + } else { + CG_Text_Paint_Ext( x + 10, y + 3, 0.2f, 0.2f, colorWhite, cgs.arenaData.longname, 0, 0, 0, &cgs.media.limboFont2 ); + } + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, trap_R_RegisterShaderNoMip( "menu/art/unknownmap" ) ); + } +} + +team_t CG_Debriefing_FindWinningTeamForMap( void ) { + const char* s = CG_ConfigString( CS_MULTI_MAPWINNER ); + const char* buf = Info_ValueForKey( s, "winner" ); + + if ( atoi( buf ) == -1 ) { + } else if ( atoi( buf ) ) { + return TEAM_ALLIES; + } else { + return TEAM_AXIS; + } + + return TEAM_FREE; +} + +team_t CG_Debriefing_FindWinningTeamForPos( int pos ) { + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + if ( pos == 0 ) { +// if( cgs.campaignData.mapCount == cgs.currentCampaignMap ) { + int i; + int axiswins = 0, alliedwins = 0; + + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + if ( cg.teamWonRounds[1] & ( 1 << i ) ) { + axiswins++; + } else if ( cg.teamWonRounds[0] & ( 1 << i ) ) { + alliedwins++; + } + } + + if ( axiswins > alliedwins ) { + return TEAM_AXIS; + } else if ( alliedwins > axiswins ) { + return TEAM_ALLIES; + } +/* } else { + const char* s = CG_ConfigString( CS_MULTI_MAPWINNER ); + const char* buf = Info_ValueForKey( s, "winner" ); + + if( atoi( buf ) == -1 ) { + } else if( atoi( buf ) ) { + return TEAM_ALLIES; + } else { + return TEAM_AXIS; + } + }*/ + } else { + if ( cg.teamWonRounds[1] & ( 1 << ( pos - 1 ) ) ) { + return TEAM_AXIS; + } else if ( cg.teamWonRounds[0] & ( 1 << ( pos - 1 ) ) ) { + return TEAM_ALLIES; + } + } + } else if ( cg_gameType.integer == GT_WOLF || cg_gameType.integer == GT_WOLF_LMS ) { + const char* s = CG_ConfigString( CS_MULTI_MAPWINNER ); + const char* buf = Info_ValueForKey( s, "winner" ); + + if ( atoi( buf ) == -1 ) { + } else if ( atoi( buf ) ) { + return TEAM_ALLIES; + } else { + return TEAM_AXIS; + } + } else if ( cg_gameType.integer == GT_WOLF_STOPWATCH ) { + int defender, winner; + const char* s; + + s = CG_ConfigString( CS_MULTI_INFO ); + defender = atoi( Info_ValueForKey( s, "defender" ) ); + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + winner = atoi( Info_ValueForKey( s, "winner" ) ); + + if ( !cgs.currentRound ) { + // second round + if ( !defender ) { + if ( winner != defender ) { + return TEAM_ALLIES; + } else { + return TEAM_AXIS; + } + } else { + if ( winner != defender ) { + return TEAM_AXIS; + } else { + return TEAM_ALLIES; + } + } + } + } + + return TEAM_FREE; +} + +team_t CG_Debriefing_FindOveralWinningTeam( void ) { + return CG_Debriefing_FindWinningTeamForPos( 0 ); +} + +team_t CG_Debriefing_FindWinningTeam( void ) { + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + return CG_Debriefing_FindWinningTeamForPos( cgs.tdbSelectedMap ); + } + + return CG_Debriefing_FindOveralWinningTeam(); +} + +qboolean CG_Debriefing2_Maps_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( cg_gameType.integer == GT_WOLF_CAMPAIGN ) { + int pos = ( ( cgs.cursorY - button->rect.y ) / 14 ) + cgs.tdbMapListOffset; + if ( pos < 0 || pos > cgs.currentCampaignMap + 1 ) { + return qfalse; + } + + cgs.tdbSelectedMap = pos; + } + + return qtrue; + } + + return qfalse; +} + +int skillPositions[ SK_NUM_SKILLS + 1 ] = { + 0, + 70, + 140, + 210, + 280, + 350, + 420, + 490, +}; + +void CG_Debriefing2TeamSkillHeaders_Draw( panel_button_t* button ) { + int i, j; + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return; + } + + for ( j = 0; j < 2; j++ ) { + for ( i = 0; i <= SK_NUM_SKILLS; i++ ) { + float w; + const char* str; + + if ( j == 0 ) { + if ( i == SK_NUM_SKILLS ) { + str = "Total"; + } else { + str = skillNamesLine1[ i ]; + } + } else { + if ( i == SK_NUM_SKILLS ) { + str = ""; + } else { + str = skillNamesLine2[ i ]; + } + } + + if ( *str ) { + w = CG_Text_Width_Ext( str, 0.2f, 0, &cgs.media.limboFont2 ); + + CG_Text_Paint_Ext( button->rect.x + 100 + skillPositions[ i ] - ( w * 0.5f ), button->rect.y + ( j * 11 ), 0.2f, 0.2f, clrTxtBck, str, 0, 0, 0, &cgs.media.limboFont2 ); + } + } + } +} + +void CG_Debriefing2TeamSkillXP_Draw( panel_button_t* button ) { + team_t winner = CG_Debriefing_FindOveralWinningTeam(); + team_t team; + float scale; + int xp, i; + vec4_t clrTxtBck = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return; + } + + if ( button->data[0] ) { + team = winner == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + } else { + team = winner == TEAM_AXIS ? TEAM_AXIS : TEAM_ALLIES; + } + + if ( team == winner ) { + scale = 0.3f; + } else { + scale = 0.2f; + } + + switch ( team ) { + case TEAM_AXIS: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + 11, scale, scale, clrTxtBck, "Axis", 0, 0, 0, &cgs.media.limboFont2 ); + break; + default: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + 11, scale, scale, clrTxtBck, "Allies", 0, 0, 0, &cgs.media.limboFont2 ); + break; + } + + for ( i = 0; i <= SK_NUM_SKILLS; i++ ) { + float w; + const char* str; + + if ( i == SK_NUM_SKILLS ) { + xp = CG_TeamDebriefing_CalcXP( team, cgs.tdbSelectedMap - 1, -1 ); + } else { + xp = CG_TeamDebriefing_CalcXP( team, cgs.tdbSelectedMap - 1, i ); + } + + str = va( "%i", xp ); + + w = CG_Text_Width_Ext( str, scale, 0, &cgs.media.limboFont2 ); + + CG_Text_Paint_Ext( button->rect.x + 100 + skillPositions[ i ] - ( w * 0.5f ), button->rect.y + 11, scale, scale, clrTxtBck, str, 0, 0, 0, &cgs.media.limboFont2 ); + } +} + diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c new file mode 100644 index 0000000..60e7d90 --- /dev/null +++ b/src/cgame/cg_draw.c @@ -0,0 +1,4641 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_draw.c -- draw all of the graphical elements during +// active (after loading) gameplay + +#include "cg_local.h" + +#define STATUSBARHEIGHT 452 +char* BindingFromName( const char *cvar ); +void Controls_GetConfig( void ); +void SetHeadOrigin( clientInfo_t *ci, playerInfo_t *pi ); +void CG_DrawOverlays(); +int activeFont; + +#define TEAM_OVERLAY_TIME 1000 + +//////////////////////// +//////////////////////// +////// new hud stuff +/////////////////////// +/////////////////////// + +void CG_Text_SetActiveFont( int font ) { + activeFont = font; +} + +int CG_Text_Width_Ext( const char *text, float scale, int limit, fontInfo_t* font ) { + int count, len; + glyphInfo_t *glyph; + const char *s = text; + float out, useScale = scale * font->glyphScale; + + out = 0; + if ( text ) { + len = strlen( text ); + if ( limit > 0 && len > limit ) { + len = limit; + } + count = 0; + while ( s && *s && count < len ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } else { + glyph = &font->glyphs[(unsigned char)*s]; + out += glyph->xSkip; + s++; + count++; + } + } + } + + return out * useScale; +} + +int CG_Text_Width( const char *text, float scale, int limit ) { + fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; + + return CG_Text_Width_Ext( text, scale, limit, font ); +} + +int CG_Text_Height_Ext( const char *text, float scale, int limit, fontInfo_t* font ) { + int len, count; + float max; + glyphInfo_t *glyph; + float useScale; + const char *s = text; + + useScale = scale * font->glyphScale; + max = 0; + if ( text ) { + len = strlen( text ); + if ( limit > 0 && len > limit ) { + len = limit; + } + count = 0; + while ( s && *s && count < len ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } else { + glyph = &font->glyphs[(unsigned char)*s]; + if ( max < glyph->height ) { + max = glyph->height; + } + s++; + count++; + } + } + } + return max * useScale; +} + +int CG_Text_Height( const char *text, float scale, int limit ) { + fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; + + return CG_Text_Height_Ext( text, scale, limit, font ); +} + +void CG_Text_PaintChar_Ext( float x, float y, float w, float h, float scalex, float scaley, float s, float t, float s2, float t2, qhandle_t hShader ) { + w *= scalex; + h *= scaley; + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); +} + +void CG_Text_PaintChar( float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader ) { + float w, h; + w = width * scale; + h = height * scale; + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); +} + +void CG_Text_Paint_Centred_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ) { + x -= CG_Text_Width_Ext( text, scalex, limit, font ) * 0.5f; + + CG_Text_Paint_Ext( x, y, scalex, scaley, color, text, adjust, limit, style, font ); +} + +void CG_Text_Paint_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ) { + int len, count; + vec4_t newColor; + glyphInfo_t *glyph; + + scalex *= font->glyphScale; + scaley *= font->glyphScale; + + if ( text ) { + const char *s = text; + trap_R_SetColor( color ); + memcpy( &newColor[0], &color[0], sizeof( vec4_t ) ); + len = strlen( text ); + if ( limit > 0 && len > limit ) { + len = limit; + } + count = 0; + while ( s && *s && count < len ) { + glyph = &font->glyphs[(unsigned char)*s]; + if ( Q_IsColorString( s ) ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( newColor, color, sizeof( newColor ) ); + } else { + memcpy( newColor, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( newColor ) ); + newColor[3] = color[3]; + } + trap_R_SetColor( newColor ); + s += 2; + continue; + } else { + float yadj = scaley * glyph->top; + if ( style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE ) { + int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; + colorBlack[3] = newColor[3]; + trap_R_SetColor( colorBlack ); + CG_Text_PaintChar_Ext( x + ( glyph->pitch * scalex ) + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, scalex, scaley, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); + colorBlack[3] = 1.0; + trap_R_SetColor( newColor ); + } + CG_Text_PaintChar_Ext( x + ( glyph->pitch * scalex ), y - yadj, glyph->imageWidth, glyph->imageHeight, scalex, scaley, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); + x += ( glyph->xSkip * scalex ) + adjust; + s++; + count++; + } + } + trap_R_SetColor( NULL ); + } +} + +void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ) { + fontInfo_t *font = &cgDC.Assets.fonts[activeFont]; + + CG_Text_Paint_Ext( x, y, scale, scale, color, text, adjust, limit, style, font ); +} + +// NERVE - SMF - added back in +int CG_DrawFieldWidth( int x, int y, int width, int value, int charWidth, int charHeight ) { + char num[16], *ptr; + int l; + int frame; + int totalwidth = 0; + + if ( width < 1 ) { + return 0; + } + + // draw number string + if ( width > 5 ) { + width = 5; + } + + switch ( width ) { + case 1: + value = value > 9 ? 9 : value; + value = value < 0 ? 0 : value; + break; + case 2: + value = value > 99 ? 99 : value; + value = value < -9 ? -9 : value; + break; + case 3: + value = value > 999 ? 999 : value; + value = value < -99 ? -99 : value; + break; + case 4: + value = value > 9999 ? 9999 : value; + value = value < -999 ? -999 : value; + break; + } + + Com_sprintf( num, sizeof( num ), "%i", value ); + l = strlen( num ); + if ( l > width ) { + l = width; + } + + ptr = num; + while ( *ptr && l ) + { + if ( *ptr == '-' ) { + frame = STAT_MINUS; + } else { + frame = *ptr - '0'; + } + + totalwidth += charWidth; + ptr++; + l--; + } + + return totalwidth; +} + +int CG_DrawField( int x, int y, int width, int value, int charWidth, int charHeight, qboolean dodrawpic, qboolean leftAlign ) { + char num[16], *ptr; + int l; + int frame; + int startx; + + if ( width < 1 ) { + return 0; + } + + // draw number string + if ( width > 5 ) { + width = 5; + } + + switch ( width ) { + case 1: + value = value > 9 ? 9 : value; + value = value < 0 ? 0 : value; + break; + case 2: + value = value > 99 ? 99 : value; + value = value < -9 ? -9 : value; + break; + case 3: + value = value > 999 ? 999 : value; + value = value < -99 ? -99 : value; + break; + case 4: + value = value > 9999 ? 9999 : value; + value = value < -999 ? -999 : value; + break; + } + + Com_sprintf( num, sizeof( num ), "%i", value ); + l = strlen( num ); + if ( l > width ) { + l = width; + } + + // NERVE - SMF + if ( !leftAlign ) { + x -= 2 + charWidth * ( l ); + } + + startx = x; + + ptr = num; + while ( *ptr && l ) + { + if ( *ptr == '-' ) { + frame = STAT_MINUS; + } else { + frame = *ptr - '0'; + } + + if ( dodrawpic ) { + CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[frame] ); + } + x += charWidth; + ptr++; + l--; + } + + return startx; +} +// -NERVE - SMF + +/* +================ +CG_Draw3DModel + +================ +*/ +void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) { + refdef_t refdef; + refEntity_t ent; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + memset( &refdef, 0, sizeof( refdef ) ); + + memset( &ent, 0, sizeof( ent ) ); + AnglesToAxis( angles, ent.axis ); + VectorCopy( origin, ent.origin ); + ent.hModel = model; + ent.customSkin = skin; + ent.renderfx = RF_NOSHADOW; // no stencil shadows + + refdef.rdflags = RDF_NOWORLDMODEL; + + AxisClear( refdef.viewaxis ); + + refdef.fov_x = 30; + refdef.fov_y = 30; + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + + refdef.time = cg.time; + + trap_R_ClearScene(); + trap_R_AddRefEntityToScene( &ent ); + trap_R_RenderScene( &refdef ); +} + +/* +============== +CG_DrawKeyModel +============== +*/ +void CG_DrawKeyModel( int keynum, float x, float y, float w, float h, int fadetime ) { + qhandle_t cm; + float len; + vec3_t origin, angles; + vec3_t mins, maxs; + + VectorClear( angles ); + + cm = cg_items[keynum].models[0]; + + // offset the origin y and z to center the model + trap_R_ModelBounds( cm, mins, maxs ); + + origin[2] = -0.5 * ( mins[2] + maxs[2] ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + +// len = 0.5 * ( maxs[2] - mins[2] ); + len = 0.75 * ( maxs[2] - mins[2] ); + origin[0] = len / 0.268; // len / tan( fov/2 ) + + angles[YAW] = 30 * sin( cg.time / 2000.0 );; + + CG_Draw3DModel( x, y, w, h, cg_items[keynum].models[0], 0, origin, angles ); +} + +/* +================ +CG_DrawTeamBackground + +================ +*/ +void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ) { + vec4_t hcolor; + + hcolor[3] = alpha; + if ( team == TEAM_AXIS ) { + hcolor[0] = 1; + hcolor[1] = 0; + hcolor[2] = 0; + } else if ( team == TEAM_ALLIES ) { + hcolor[0] = 0; + hcolor[1] = 0; + hcolor[2] = 1; + } else { + return; + } + trap_R_SetColor( hcolor ); + CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar ); + trap_R_SetColor( NULL ); +} + +/* +=========================================================================================== + + UPPER RIGHT CORNER + +=========================================================================================== +*/ + +#define UPPERRIGHT_X 634 +/* +================== +CG_DrawSnapshot +================== +*/ +static float CG_DrawSnapshot( float y ) { + char *s; + int w; + + s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, + cg.latestSnapshotNum, cgs.serverCommandSequence ); + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + + CG_DrawBigString( UPPERRIGHT_X - w, y + 2, s, 1.0F ); + + return y + BIGCHAR_HEIGHT + 4; +} + +/* +================== +CG_DrawFPS +================== +*/ +#define FPS_FRAMES 4 +static float CG_DrawFPS( float y ) { + char *s; + int w; + static int previousTimes[FPS_FRAMES]; + static int index; + int i, total; + int fps; + static int previous; + int t, frameTime; + vec4_t timerBackground = { 0.16f, 0.2f, 0.17f, 0.8f }; + vec4_t timerBorder = { 0.5f, 0.5f, 0.5f, 0.5f }; + vec4_t tclr = { 0.625f, 0.625f, 0.6f, 1.0f }; + + // don't use serverTime, because that will be drifting to + // correct for internet lag changes, timescales, timedemos, etc + t = trap_Milliseconds(); + frameTime = t - previous; + previous = t; + + previousTimes[index % FPS_FRAMES] = frameTime; + index++; + if ( index > FPS_FRAMES ) { + // average multiple frames together to smooth changes out a bit + total = 0; + for ( i = 0 ; i < FPS_FRAMES ; i++ ) { + total += previousTimes[i]; + } + if ( !total ) { + total = 1; + } + fps = 1000 * FPS_FRAMES / total; + + s = va( "%i FPS", fps ); + w = CG_Text_Width_Ext( s, 0.19f, 0, &cgs.media.limboFont1 ); + + CG_FillRect( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, timerBackground ); + CG_DrawRect_FixedBorder( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, 1, timerBorder ); + + CG_Text_Paint_Ext( UPPERRIGHT_X - w, y + 11, 0.19f, 0.19f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } + + return y + 12 + 4; +} + +/* +================= +CG_DrawTimer +================= +*/ + +static float CG_DrawTimer( float y ) { + char *s; + int w; + int mins, seconds, tens; + int msec; + char *rt; + vec4_t color = { 0.625f, 0.625f, 0.6f, 1.0f }; + vec4_t timerBackground = { 0.16f, 0.2f, 0.17f, 0.8f }; + vec4_t timerBorder = { 0.5f, 0.5f, 0.5f, 0.5f }; + + rt = ( cgs.gametype != GT_WOLF_LMS && cg_drawReinforcementTime.integer > 0 ) ? + va( "^F%d%s", CG_CalculateReinfTime( qfalse ), ( ( cgs.timelimit <= 0.0f ) ? "" : " " ) ) : ""; + + msec = ( cgs.timelimit * 60.f * 1000.f ) - ( cg.time - cgs.levelStartTime ); + + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + + if ( cgs.gamestate != GS_PLAYING ) { + //% s = va( "%s^*WARMUP", rt ); + s = "^*WARMUP"; // ydnar: don't draw reinforcement time in warmup mode + color[3] = fabs( sin( cg.time * 0.002 ) ); + } else if ( msec < 0 && cgs.timelimit > 0.0f ) { + s = va( "^N0:00" ); + color[3] = fabs( sin( cg.time * 0.002 ) ); + } else { + if ( cgs.timelimit <= 0.0f ) { + s = va( "%s", rt ); + } else { + s = va( "%s^*%i:%i%i", rt, mins, tens, seconds ); + } + + color[3] = 1.f; + } + + w = CG_Text_Width_Ext( s, 0.19f, 0, &cgs.media.limboFont1 ); + + CG_FillRect( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, timerBackground ); + CG_DrawRect_FixedBorder( UPPERRIGHT_X - w - 2, y, w + 5, 12 + 2, 1, timerBorder ); + + CG_Text_Paint_Ext( UPPERRIGHT_X - w, y + 11, 0.19f, 0.19f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); + + return y + 12 + 4; +} + +// START xkan, 8/29/2002 +int CG_BotIsSelected( int clientNum ) { + int i; + + for ( i = 0; i < MAX_NUM_BUDDY; i++ ) + { + if ( cg.selectedBotClientNumber[i] == 0 ) { + return 0; + } else if ( cg.selectedBotClientNumber[i] == clientNum ) { + return 1; + } + } + return 0; +} +// END xkan, 8/29/2002 + +/* +================= +CG_DrawTeamOverlay +================= +*/ + +int maxCharsBeforeOverlay; + +#define TEAM_OVERLAY_MAXNAME_WIDTH 16 +#define TEAM_OVERLAY_MAXLOCATION_WIDTH 20 + +/* +===================== +CG_DrawUpperRight + +===================== +*/ +static void CG_DrawUpperRight( void ) { + float y; + + if ( !cg_drawFireteamOverlay.integer ) { + return; + } + + y = 20 + 100 + 32; + + if ( CG_IsOnFireteam( cg.clientNum ) ) { + rectDef_t rect = { 10, 10, 100, 100 }; + CG_DrawFireTeamOverlay( &rect ); + } else { +// CG_DrawTeamOverlay( 0 ); + } + + if ( !( cg.snap->ps.pm_flags & PMF_LIMBO ) && ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) && + ( cgs.autoMapExpanded || ( !cgs.autoMapExpanded && ( cg.time - cgs.autoMapExpandTime < 250.f ) ) ) ) { + return; + } + + if ( cg_drawRoundTimer.integer ) { + y = CG_DrawTimer( y ); + } + + if ( cg_drawFPS.integer ) { + y = CG_DrawFPS( y ); + } + + if ( cg_drawSnapshot.integer ) { + y = CG_DrawSnapshot( y ); + } +} + +/* +=========================================================================================== + + LOWER RIGHT CORNER + +=========================================================================================== +*/ + +#define CHATLOC_X 160 +#define CHATLOC_Y 478 +#define CHATLOC_TEXT_X ( CHATLOC_X + 0.25f * TINYCHAR_WIDTH ) + +/* +================= +CG_DrawTeamInfo +================= +*/ +static void CG_DrawTeamInfo( void ) { + int w, h; + int i, len; + vec4_t hcolor; + int chatHeight; + float alphapercent; + float lineHeight = 9.f; + + int chatWidth = 640 - CHATLOC_X - 100; + + if ( cg_teamChatHeight.integer < TEAMCHAT_HEIGHT ) { + chatHeight = cg_teamChatHeight.integer; + } else { + chatHeight = TEAMCHAT_HEIGHT; + } + + if ( chatHeight <= 0 ) { + return; // disabled + } + + if ( cgs.teamLastChatPos != cgs.teamChatPos ) { + if ( cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_teamChatTime.integer ) { + cgs.teamLastChatPos++; + } + + h = ( cgs.teamChatPos - cgs.teamLastChatPos ) * lineHeight; + + w = 0; + + for ( i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++ ) { + len = CG_Text_Width_Ext( cgs.teamChatMsgs[i % chatHeight], 0.2f, 0, &cgs.media.limboFont2 ); + if ( len > w ) { + w = len; + } + } + w *= TINYCHAR_WIDTH; + w += TINYCHAR_WIDTH * 2; + + for ( i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i-- ) { + alphapercent = 1.0f - ( cg.time - cgs.teamChatMsgTimes[i % chatHeight] ) / (float)( cg_teamChatTime.integer ); + if ( alphapercent > 1.0f ) { + alphapercent = 1.0f; + } else if ( alphapercent < 0.f ) { + alphapercent = 0.f; + } + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { + hcolor[0] = 1; + hcolor[1] = 0; + hcolor[2] = 0; + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + hcolor[0] = 0; + hcolor[1] = 0; + hcolor[2] = 1; + } else { + hcolor[0] = 0; + hcolor[1] = 1; + hcolor[2] = 0; + } + + hcolor[3] = 0.33f * alphapercent; + + trap_R_SetColor( hcolor ); + CG_DrawPic( CHATLOC_X, CHATLOC_Y - ( cgs.teamChatPos - i ) * lineHeight, chatWidth, lineHeight, cgs.media.teamStatusBar ); + + hcolor[0] = hcolor[1] = hcolor[2] = 1.0; + hcolor[3] = alphapercent; + trap_R_SetColor( hcolor ); + + CG_Text_Paint_Ext( CHATLOC_TEXT_X, CHATLOC_Y - ( cgs.teamChatPos - i - 1 ) * lineHeight - 1, 0.2f, 0.2f, hcolor, cgs.teamChatMsgs[i % chatHeight], 0, 0, 0, &cgs.media.limboFont2 ); + } + } +} + +const char* CG_PickupItemText( int item ) { + if ( bg_itemlist[ item ].giType == IT_HEALTH ) { + if ( bg_itemlist[ item ].world_model[2] ) { // this is a multi-stage item + // FIXME: print the correct amount for multi-stage + return va( "a %s", bg_itemlist[ item ].pickup_name ); + } else { + return va( "%i %s", bg_itemlist[ item ].quantity, bg_itemlist[ item ].pickup_name ); + } + } else if ( bg_itemlist[ item ].giType == IT_TEAM ) { + return "an Objective"; + } else { + if ( bg_itemlist[ item ].pickup_name[0] == 'a' || bg_itemlist[ item ].pickup_name[0] == 'A' ) { + return va( "an %s", bg_itemlist[ item ].pickup_name ); + } else { + return va( "a %s", bg_itemlist[ item ].pickup_name ); + } + } +} + +/* +================= +CG_DrawNotify +================= +*/ +#define NOTIFYLOC_Y 42 // bottom end +#define NOTIFYLOC_X 0 +#define NOTIFYLOC_Y_SP 128 + +static void CG_DrawNotify( void ) { + int w, h; + int i, len; + vec4_t hcolor; + int chatHeight; + float alphapercent; + char var[MAX_TOKEN_CHARS]; + float notifytime = 1.0f; + int yLoc; + + return; + + yLoc = NOTIFYLOC_Y; + + trap_Cvar_VariableStringBuffer( "con_notifytime", var, sizeof( var ) ); + notifytime = atof( var ) * 1000; + + if ( notifytime <= 100.f ) { + notifytime = 100.0f; + } + + chatHeight = NOTIFY_HEIGHT; + + if ( cgs.notifyLastPos != cgs.notifyPos ) { + if ( cg.time - cgs.notifyMsgTimes[cgs.notifyLastPos % chatHeight] > notifytime ) { + cgs.notifyLastPos++; + } + + h = ( cgs.notifyPos - cgs.notifyLastPos ) * TINYCHAR_HEIGHT; + + w = 0; + + for ( i = cgs.notifyLastPos; i < cgs.notifyPos; i++ ) { + len = CG_DrawStrlen( cgs.notifyMsgs[i % chatHeight] ); + if ( len > w ) { + w = len; + } + } + w *= TINYCHAR_WIDTH; + w += TINYCHAR_WIDTH * 2; + + if ( maxCharsBeforeOverlay <= 0 ) { + maxCharsBeforeOverlay = 80; + } + + for ( i = cgs.notifyPos - 1; i >= cgs.notifyLastPos; i-- ) { + alphapercent = 1.0f - ( ( cg.time - cgs.notifyMsgTimes[i % chatHeight] ) / notifytime ); + if ( alphapercent > 0.5f ) { + alphapercent = 1.0f; + } else { + alphapercent *= 2; + } + + if ( alphapercent < 0.f ) { + alphapercent = 0.f; + } + + hcolor[0] = hcolor[1] = hcolor[2] = 1.0; + hcolor[3] = alphapercent; + trap_R_SetColor( hcolor ); + + CG_DrawStringExt( NOTIFYLOC_X + TINYCHAR_WIDTH, + yLoc - ( cgs.notifyPos - i ) * TINYCHAR_HEIGHT, + cgs.notifyMsgs[i % chatHeight], hcolor, qfalse, qfalse, + TINYCHAR_WIDTH, TINYCHAR_HEIGHT, maxCharsBeforeOverlay ); + } + } +} + +/* +=============================================================================== + +LAGOMETER + +=============================================================================== +*/ + +#define LAG_SAMPLES 128 + + +typedef struct { + int frameSamples[LAG_SAMPLES]; + int frameCount; + int snapshotFlags[LAG_SAMPLES]; + int snapshotSamples[LAG_SAMPLES]; + int snapshotCount; +} lagometer_t; + +lagometer_t lagometer; + +/* +============== +CG_AddLagometerFrameInfo + +Adds the current interpolate / extrapolate bar for this frame +============== +*/ +void CG_AddLagometerFrameInfo( void ) { + int offset; + + offset = cg.time - cg.latestSnapshotTime; + lagometer.frameSamples[ lagometer.frameCount & ( LAG_SAMPLES - 1 ) ] = offset; + lagometer.frameCount++; +} + +/* +============== +CG_AddLagometerSnapshotInfo + +Each time a snapshot is received, log its ping time and +the number of snapshots that were dropped before it. + +Pass NULL for a dropped packet. +============== +*/ +void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) { + // dropped packet + if ( !snap ) { + lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = -1; + lagometer.snapshotCount++; + return; + } + + // add this snapshot's info + lagometer.snapshotSamples[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->ping; + lagometer.snapshotFlags[ lagometer.snapshotCount & ( LAG_SAMPLES - 1 ) ] = snap->snapFlags; + lagometer.snapshotCount++; +} + +/* +============== +CG_DrawDisconnect + +Should we draw something differnet for long lag vs no packets? +============== +*/ +static void CG_DrawDisconnect( void ) { + float x, y; + int cmdNum; + usercmd_t cmd; + const char *s; + int w; // bk010215 - FIXME char message[1024]; + + // OSP - dont draw if a demo and we're running at a different timescale + if ( cg.demoPlayback && cg_timescale.value != 1.0f ) { + return; + } + + // ydnar: don't draw if the server is respawning + if ( cg.serverRespawning ) { + return; + } + + // draw the phone jack if we are completely past our buffers + cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; + trap_GetUserCmd( cmdNum, &cmd ); + if ( cmd.serverTime <= cg.snap->ps.commandTime + || cmd.serverTime > cg.time ) { // special check for map_restart // bk 0102165 - FIXME + return; + } + + // also add text in center of screen + s = CG_TranslateString( "Connection Interrupted" ); // bk 010215 - FIXME + w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH; + CG_DrawBigString( 320 - w / 2, 100, s, 1.0F ); + + // blink the icon + if ( ( cg.time >> 9 ) & 1 ) { + return; + } + + x = 640 - 48; + y = 480 - 200; + + CG_DrawPic( x, y, 48, 48, cgs.media.disconnectIcon ); +} + + +#define MAX_LAGOMETER_PING 900 +#define MAX_LAGOMETER_RANGE 300 + +/* +============== +CG_DrawLagometer +============== +*/ +static void CG_DrawLagometer( void ) { + int a, x, y, i; + float v; + float ax, ay, aw, ah, mid, range; + int color; + float vscale; + + if ( !cg_lagometer.integer || cgs.localServer ) { +// if(0) { + CG_DrawDisconnect(); + return; + } + + // + // draw the graph + // + x = 640 - 48; + y = 480 - 200; + + trap_R_SetColor( NULL ); + CG_DrawPic( x, y, 48, 48, cgs.media.lagometerShader ); + + ax = x; + ay = y; + aw = 48; + ah = 48; + CG_AdjustFrom640( &ax, &ay, &aw, &ah ); + + color = -1; + range = ah / 3; + mid = ay + range; + + vscale = range / MAX_LAGOMETER_RANGE; + + // draw the frame interpoalte / extrapolate graph + for ( a = 0 ; a < aw ; a++ ) { + i = ( lagometer.frameCount - 1 - a ) & ( LAG_SAMPLES - 1 ); + v = lagometer.frameSamples[i]; + v *= vscale; + if ( v > 0 ) { + if ( color != 1 ) { + color = 1; + trap_R_SetColor( colorYellow ); + } + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic( ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } else if ( v < 0 ) { + if ( color != 2 ) { + color = 2; + trap_R_SetColor( colorBlue ); + } + v = -v; + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic( ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } + } + + // draw the snapshot latency / drop graph + range = ah / 2; + vscale = range / MAX_LAGOMETER_PING; + + for ( a = 0 ; a < aw ; a++ ) { + i = ( lagometer.snapshotCount - 1 - a ) & ( LAG_SAMPLES - 1 ); + v = lagometer.snapshotSamples[i]; + if ( v > 0 ) { + if ( lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED ) { + if ( color != 5 ) { + color = 5; // YELLOW for rate delay + trap_R_SetColor( colorYellow ); + } + } else { + if ( color != 3 ) { + color = 3; + trap_R_SetColor( colorGreen ); + } + } + v = v * vscale; + if ( v > range ) { + v = range; + } + trap_R_DrawStretchPic( ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader ); + } else if ( v < 0 ) { + if ( color != 4 ) { + color = 4; // RED for dropped snapshots + trap_R_SetColor( colorRed ); + } + trap_R_DrawStretchPic( ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader ); + } + } + + trap_R_SetColor( NULL ); + + if ( cg_nopredict.integer +#ifdef ALLOW_GSYNC + || cg_synchronousClients.integer +#endif // ALLOW_GSYNC + ) { + CG_DrawBigString( ax, ay, "snc", 1.0 ); + } + + CG_DrawDisconnect(); +} + + +void CG_DrawLivesLeft( void ) { + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return; + } + + if ( cg.snap->ps.persistant[PERS_RESPAWNS_LEFT] < 0 ) { + return; + } + + CG_DrawPic( 4, 360, 48, 24, cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ? cgs.media.hudAlliedHelmet : cgs.media.hudAxisHelmet ); + + CG_DrawField( 44, 360, 3, cg.snap->ps.persistant[PERS_RESPAWNS_LEFT], 14, 20, qtrue, qtrue ); +} + +/* +=============================================================================== + +CENTER PRINTING + +=============================================================================== +*/ + + +/* +============== +CG_CenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +#define CP_LINEWIDTH 56 // NERVE - SMF + +void CG_CenterPrint( const char *str, int y, int charWidth ) { + char *s; + int i, len; // NERVE - SMF + qboolean neednewline = qfalse; // NERVE - SMF + int priority = 0; + + // NERVE - SMF - don't draw if this print message is less important + if ( cg.centerPrintTime && priority < cg.centerPrintPriority ) { + return; + } + + Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); + cg.centerPrintPriority = priority; // NERVE - SMF + + // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth + len = strlen( cg.centerPrint ); + for ( i = 0; i < len; i++ ) { + + // NOTE: subtract a few chars here so long words still get displayed properly + if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { + neednewline = qtrue; + } + if ( cg.centerPrint[i] == ' ' && neednewline ) { + cg.centerPrint[i] = '\n'; + neednewline = qfalse; + } + } + // -NERVE - SMF + + cg.centerPrintTime = cg.time; + cg.centerPrintY = y; + cg.centerPrintCharWidth = charWidth; + + // count the number of lines for centering + cg.centerPrintLines = 1; + s = cg.centerPrint; + while ( *s ) { + if ( *s == '\n' ) { + cg.centerPrintLines++; + } + s++; + } +} + +// NERVE - SMF +/* +============== +CG_PriorityCenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +void CG_PriorityCenterPrint( const char *str, int y, int charWidth, int priority ) { + char *s; + int i, len; // NERVE - SMF + qboolean neednewline = qfalse; // NERVE - SMF + + // NERVE - SMF - don't draw if this print message is less important + if ( cg.centerPrintTime && priority < cg.centerPrintPriority ) { + return; + } + + Q_strncpyz( cg.centerPrint, str, sizeof( cg.centerPrint ) ); + cg.centerPrintPriority = priority; // NERVE - SMF + + // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth + len = strlen( cg.centerPrint ); + for ( i = 0; i < len; i++ ) { + + // NOTE: subtract a few chars here so long words still get displayed properly + if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { + neednewline = qtrue; + } + if ( cg.centerPrint[i] == ' ' && neednewline ) { + cg.centerPrint[i] = '\n'; + neednewline = qfalse; + } + } + // -NERVE - SMF + + cg.centerPrintTime = cg.time + 2000; + cg.centerPrintY = y; + cg.centerPrintCharWidth = charWidth; + + // count the number of lines for centering + cg.centerPrintLines = 1; + s = cg.centerPrint; + while ( *s ) { + if ( *s == '\n' ) { + cg.centerPrintLines++; + } + s++; + } +} +// -NERVE - SMF + +/* +=================== +CG_DrawCenterString +=================== +*/ +static void CG_DrawCenterString( void ) { + char *start; + int l; + int x, y, w; + float *color; + + if ( !cg.centerPrintTime ) { + return; + } + + color = CG_FadeColor( cg.centerPrintTime, 1000 * cg_centertime.value ); + if ( !color ) { + cg.centerPrintTime = 0; + cg.centerPrintPriority = 0; + return; + } + + trap_R_SetColor( color ); + + start = cg.centerPrint; + + y = cg.centerPrintY - cg.centerPrintLines * BIGCHAR_HEIGHT / 2; + + while ( 1 ) { + char linebuffer[1024]; + + for ( l = 0; l < CP_LINEWIDTH; l++ ) { // NERVE - SMF - added CP_LINEWIDTH + if ( !start[l] || start[l] == '\n' ) { + break; + } + linebuffer[l] = start[l]; + } + linebuffer[l] = 0; + + w = cg.centerPrintCharWidth * CG_DrawStrlen( linebuffer ); + + x = ( SCREEN_WIDTH - w ) / 2; + + CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, cg.centerPrintCharWidth, (int)( cg.centerPrintCharWidth * 1.5 ), 0 ); + + y += cg.centerPrintCharWidth * 1.5; + + while ( *start && ( *start != '\n' ) ) { + start++; + } + if ( !*start ) { + break; + } + start++; + } + + trap_R_SetColor( NULL ); +} + + + +/* +================================================================================ + +CROSSHAIRS + +================================================================================ +*/ + +/* +============== +CG_DrawWeapReticle +============== +*/ +static void CG_DrawWeapReticle( void ) { + vec4_t color = {0, 0, 0, 1}; + qboolean fg, garand, k43; + + // DHM - Nerve :: So that we will draw reticle + if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) { + garand = (qboolean)( cg.snap->ps.weapon == WP_GARAND_SCOPE ); + k43 = (qboolean)( cg.snap->ps.weapon == WP_K43_SCOPE ); + fg = (qboolean)( cg.snap->ps.weapon == WP_FG42SCOPE ); + } else { + fg = (qboolean)( cg.weaponSelect == WP_FG42SCOPE ); + garand = (qboolean)( cg.weaponSelect == WP_GARAND_SCOPE ); + k43 = (qboolean)( cg.weaponSelect == WP_K43_SCOPE ); + } + + if ( fg ) { + // sides + CG_FillRect( 0, 0, 80, 480, color ); + CG_FillRect( 560, 0, 80, 480, color ); + + // center + if ( cgs.media.reticleShaderSimple ) { + CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); + } + +/* if(cgs.media.reticleShaderSimpleQ) { + trap_R_DrawStretchPic( x, 0, w, h, 0, 0, 1, 1, cgs.media.reticleShaderSimpleQ ); // tl + trap_R_DrawStretchPic( x+w, 0, w, h, 1, 0, 0, 1, cgs.media.reticleShaderSimpleQ ); // tr + trap_R_DrawStretchPic( x, h, w, h, 0, 1, 1, 0, cgs.media.reticleShaderSimpleQ ); // bl + trap_R_DrawStretchPic( x+w, h, w, h, 1, 1, 0, 0, cgs.media.reticleShaderSimpleQ ); // br + }*/ + + // hairs + CG_FillRect( 84, 239, 150, 3, color ); // left + CG_FillRect( 234, 240, 173, 1, color ); // horiz center + CG_FillRect( 407, 239, 150, 3, color ); // right + + + CG_FillRect( 319, 2, 3, 151, color ); // top center top + CG_FillRect( 320, 153, 1, 114, color ); // top center bot + + CG_FillRect( 320, 241, 1, 87, color ); // bot center top + CG_FillRect( 319, 327, 3, 151, color ); // bot center bot + } else if ( garand ) { + // sides + CG_FillRect( 0, 0, 80, 480, color ); + CG_FillRect( 560, 0, 80, 480, color ); + + // center + if ( cgs.media.reticleShaderSimple ) { + CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); + } + + // hairs + CG_FillRect( 84, 239, 177, 2, color ); // left + CG_FillRect( 320, 242, 1, 58, color ); // center top + CG_FillRect( 319, 300, 2, 178, color ); // center bot + CG_FillRect( 380, 239, 177, 2, color ); // right + } else if ( k43 ) { + // sides + CG_FillRect( 0, 0, 80, 480, color ); + CG_FillRect( 560, 0, 80, 480, color ); + + // center + if ( cgs.media.reticleShaderSimple ) { + CG_DrawPic( 80, 0, 480, 480, cgs.media.reticleShaderSimple ); + } + + // hairs + CG_FillRect( 84, 239, 177, 2, color ); // left + CG_FillRect( 320, 242, 1, 58, color ); // center top + CG_FillRect( 319, 300, 2, 178, color ); // center bot + CG_FillRect( 380, 239, 177, 2, color ); // right + } +} + +/* +============== +CG_DrawMortarReticle +============== +*/ +static void CG_DrawMortarReticle( void ) { + vec4_t color = { 1.f, 1.f, 1.f, .5f }; + vec4_t color_back = { 0.f, 0.f, 0.f, .25f }; + vec4_t color_extends = { .77f, .73f, .1f, 1.f }; + vec4_t color_lastfire = { .77f, .1f, .1f, 1.f }; + //vec4_t color_firerequest = { .23f, 1.f, .23f, 1.f }; + vec4_t color_firerequest = { 1.f, 1.f, 1.f, 1.f }; + float offset, localOffset; + int i, min, majorOffset, val, printval, fadeTime; + char *s; + float angle, angleMin, angleMax; + qboolean hasRightTarget, hasLeftTarget; + + // Background + CG_FillRect( 136, 236, 154, 38, color_back ); + CG_FillRect( 290, 160, 60, 208, color_back ); + CG_FillRect( 350, 236, 154, 38, color_back ); + + // Horizontal bar + + // bottom + CG_FillRect( 140, 264, 150, 1, color ); // left + CG_FillRect( 350, 264, 150, 1, color ); // right + + // 10 units - 5 degrees + // total of 360 units + // nothing displayed between 150 and 210 units + // 360 / 10 = 36 bits, means 36 * 5 = 180 degrees + // that means left is cg.predictedPlayerState.viewangles[YAW] - .5f * 180 + angle = 360 - AngleNormalize360( cg.predictedPlayerState.viewangles[YAW] - 90.f ); + + offset = ( 5.f / 65536 ) * ( (int)( angle * ( 65536 / 5.f ) ) & 65535 ); + min = (int)( AngleNormalize360( angle - .5f * 180 ) / 15.f ) * 15; + majorOffset = (int)( floor( (int)floor( AngleNormalize360( angle - .5f * 180 ) ) % 15 ) / 5.f ); + + for ( val = i = 0; i < 36; i++ ) { + localOffset = i * 10.f + ( offset * 2.f ); + + if ( localOffset >= 150 && localOffset <= 210 ) { + if ( i % 3 == majorOffset ) { + val++; + } + continue; + } + + if ( i % 3 == majorOffset ) { + printval = min - val * 15 + 180; + + // rain - old tertiary abuse was nasty and had undefined result + if ( printval < 0 ) { + printval += 360; + } else if ( printval >= 360 ) { + printval -= 360; + } + + s = va( "%i", printval ); + //CG_Text_Paint_Ext( 140 + localOffset - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 244, .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); + //CG_FillRect( 140 + localOffset, 248, 1, 16, color); + CG_Text_Paint_Ext( 500 - localOffset - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 244, .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); + CG_FillRect( 500 - localOffset, 248, 1, 16, color ); + val++; + } else { + //CG_FillRect( 140 + localOffset, 256, 1, 8, color); + CG_FillRect( 500 - localOffset, 256, 1, 8, color ); + } + } + + // the extremes + // 30 degrees plus a 15 degree border + angleMin = AngleNormalize360( 360 - ( cg.pmext.mountedWeaponAngles[YAW] - 90.f ) - ( 30.f + 15.f ) ); + angleMax = AngleNormalize360( 360 - ( cg.pmext.mountedWeaponAngles[YAW] - 90.f ) + ( 30.f + 15.f ) ); + + // right + localOffset = ( AngleNormalize360( angle - angleMin ) / 5.f ) * 10.f; + //CG_FillRect( 320 + localOffset, 252, 2, 18, color_extends); + CG_FillRect( 320 - localOffset, 252, 2, 18, color_extends ); + + // left + localOffset = ( AngleNormalize360( angleMax - angle ) / 5.f ) * 10.f; + //CG_FillRect( 320 - localOffset, 252, 2, 18, color_extends); + CG_FillRect( 320 + localOffset, 252, 2, 18, color_extends ); + + // last fire pos + fadeTime = 0; + if ( cg.lastFiredWeapon == WP_MORTAR_SET && cg.mortarImpactTime >= -1 ) { + fadeTime = cg.time - ( cg.predictedPlayerEntity.muzzleFlashTime + 5000 ); + + if ( fadeTime < 3000 ) { + float lastfireAngle; + + if ( fadeTime > 0 ) { + color_lastfire[3] = 1.f - ( fadeTime / 3000.f ); + } + + lastfireAngle = AngleNormalize360( 360 - ( cg.mortarFireAngles[YAW] - 90.f ) ); + + localOffset = ( ( AngleSubtract( angle, lastfireAngle ) ) / 5.f ) * 10.f; + //CG_FillRect( 320 + localOffset, 252, 2, 18, color_lastfire); + CG_FillRect( 320 - localOffset, 252, 2, 18, color_lastfire ); + } + } + + // mortar attack requests + hasRightTarget = hasLeftTarget = qfalse; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + int requestFadeTime = cg.time - ( cg.artilleryRequestTime[i] + 25000 ); + + if ( requestFadeTime < 5000 ) { + vec3_t dir; + float yaw; + float attackRequestAngle; + + VectorSubtract( cg.artilleryRequestPos[i], cg.predictedPlayerEntity.lerpOrigin, dir ); + + // ripped this out of vectoangles + if ( dir[1] == 0 && dir[0] == 0 ) { + yaw = 0; + } else { + if ( dir[0] ) { + yaw = ( atan2( dir[1], dir[0] ) * 180 / M_PI ); + } else if ( dir[1] > 0 ) { + yaw = 90; + } else { + yaw = 270; + } + if ( yaw < 0 ) { + yaw += 360; + } + } + + if ( requestFadeTime > 0 ) { + color_firerequest[3] = 1.f - ( requestFadeTime / 5000.f ); + } + + attackRequestAngle = AngleNormalize360( 360 - ( yaw - 90.f ) ); + + yaw = AngleSubtract( attackRequestAngle, angleMin ); + + if ( yaw < 0 ) { + if ( !hasLeftTarget ) { + //CG_FillRect( 136 + 2, 236 + 38 - 6, 4, 4, color_firerequest ); + + trap_R_SetColor( color_firerequest ); + CG_DrawPic( 136 + 2, 236 + 38 - 10 + 1, 8, 8, cgs.media.ccMortarTargetArrow ); + trap_R_SetColor( NULL ); + + hasLeftTarget = qtrue; + } + } else if ( yaw > 90 ) { + if ( !hasRightTarget ) { + //CG_FillRect( 350 + 154 - 6, 236 + 38 - 6, 4, 4, color_firerequest ); + + trap_R_SetColor( color_firerequest ); + CG_DrawPic( 350 + 154 - 10, 236 + 38 - 10 + 1, -8, 8, cgs.media.ccMortarTargetArrow ); + trap_R_SetColor( NULL ); + + hasRightTarget = qtrue; + } + } else { + localOffset = ( ( AngleSubtract( angle, attackRequestAngle ) ) / 5.f ) * 10.f; + //CG_FillRect( 320 + localOffset - 3, 264 - 3, 6, 6, color_firerequest ); + + trap_R_SetColor( color_firerequest ); + //CG_DrawPic( 320 + localOffset - 8, 264 - 8, 16, 16, cgs.media.ccMortarTarget ); + CG_DrawPic( 320 - localOffset - 8, 264 - 8, 16, 16, cgs.media.ccMortarTarget ); + trap_R_SetColor( NULL ); + } + } + } + + /*s = va( "%.2f (%i / %i)",AngleNormalize360(angle - .5f * 180), majorOffset, min ); + CG_Text_Paint( 140, 224, .25f, color, s, 0, 0, 0 ); + s = va( "%.2f",AngleNormalize360(angle) ); + CG_Text_Paint( 320 - .5f * CG_Text_Width( s, .25f, 0), 224, .25f, color, s, 0, 0, 0 ); + s = va( "%.2f", AngleNormalize360(angle + .5f * 180) ); + CG_Text_Paint( 500 - CG_Text_Width( s, .25f, 0 ), 224, .25f, color, s, 0, 0, 0 );*/ + + // Vertical bar + + // sides + CG_FillRect( 295, 164, 1, 200, color ); // left + CG_FillRect( 345, 164, 1, 200, color ); // right + + // 10 units - 2.5 degrees + // total of 200 units + // 200 / 10 = 20 bits, means 20 * 2.5 = 50 degrees + // that means left is cg.predictedPlayerState.viewangles[PITCH] - .5f * 50 + angle = AngleNormalize180( 360 - ( cg.predictedPlayerState.viewangles[PITCH] - 60 ) ); + + offset = ( 2.5f / 65536 ) * ( (int)( angle * ( 65536 / 2.5f ) ) & 65535 ); + min = floor( ( angle + .5f * 50 ) / 10.f ) * 10; + majorOffset = (int)( floor( (int)( ( angle + .5f * 50 ) * 10.f ) % 100 ) / 25.f ); + + for ( val = i = 0; i < 20; i++ ) { + localOffset = i * 10.f + ( offset * 4.f ); + + /*if( localOffset >= 150 && localOffset <= 210 ) { + if( i % 3 == majorOffset) + val++; + continue; + }*/ + + if ( i % 4 == majorOffset ) { + printval = min - val * 10; + + // rain - old tertiary abuse was nasty and had undefined result + if ( printval <= -180 ) { + printval += 360; + } else if ( printval >= 180 ) { + printval -= 180; + } + + s = va( "%i", printval ); + CG_Text_Paint_Ext( 320 - .5f * CG_Text_Width_Ext( s, .15f, 0, &cgs.media.limboFont1 ), 164 + localOffset + .5f * CG_Text_Height_Ext( s, .15f, 0, &cgs.media.limboFont1 ), .15f, .15f, color, s, 0, 0, 0, &cgs.media.limboFont1 ); + CG_FillRect( 295 + 1, 164 + localOffset, 12, 1, color ); + CG_FillRect( 345 - 12, 164 + localOffset, 12, 1, color ); + val++; + } else { + CG_FillRect( 295 + 1, 164 + localOffset, 8, 1, color ); + CG_FillRect( 345 - 8, 164 + localOffset, 8, 1, color ); + } + } + + // the extremes + // 30 degrees up + // 20 degrees down + angleMin = AngleNormalize180( 360 - ( cg.pmext.mountedWeaponAngles[PITCH] - 60 ) ) - 20.f; + angleMax = AngleNormalize180( 360 - ( cg.pmext.mountedWeaponAngles[PITCH] - 60 ) ) + 30.f; + + // top + localOffset = angleMax - angle; + if ( localOffset < 0 ) { + localOffset = 0; + } + localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; + if ( localOffset < 100 ) { + CG_FillRect( 295 - 2, 264 - localOffset, 6, 2, color_extends ); + CG_FillRect( 345 - 4 + 1, 264 - localOffset, 6, 2, color_extends ); + } + + // bottom + localOffset = angle - angleMin; + if ( localOffset < 0 ) { + localOffset = 0; + } + localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; + if ( localOffset < 100 ) { + CG_FillRect( 295 - 2, 264 + localOffset, 6, 2, color_extends ); + CG_FillRect( 345 - 4 + 1, 264 + localOffset, 6, 2, color_extends ); + } + + // last fire pos + if ( cg.lastFiredWeapon == WP_MORTAR_SET && cg.mortarImpactTime >= -1 ) { + if ( fadeTime < 3000 ) { + float lastfireAngle; + + lastfireAngle = AngleNormalize180( 360 - ( cg.mortarFireAngles[PITCH] - 60 ) ); + + if ( lastfireAngle > angle ) { + localOffset = lastfireAngle - angle; + if ( localOffset < 0 ) { + localOffset = 0; + } + localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; + if ( localOffset < 100 ) { + CG_FillRect( 295 - 2, 264 - localOffset, 6, 2, color_lastfire ); + CG_FillRect( 345 - 4 + 1, 264 - localOffset, 6, 2, color_lastfire ); + } + } else { + localOffset = angle - lastfireAngle; + if ( localOffset < 0 ) { + localOffset = 0; + } + localOffset = ( AngleNormalize360( localOffset ) / 2.5f ) * 10.f; + if ( localOffset < 100 ) { + CG_FillRect( 295 - 2, 264 + localOffset, 6, 2, color_lastfire ); + CG_FillRect( 345 - 4 + 1, 264 + localOffset, 6, 2, color_lastfire ); + } + } + } + } + + /*s = va( "%.2f (%i / %i)", angle + .5f * 50, majorOffset, min ); + CG_Text_Paint( 348, 164, .25f, color, s, 0, 0, 0 ); + s = va( "%.2f",angle ); + CG_Text_Paint( 348, 264, .25f, color, s, 0, 0, 0 ); + s = va( "%.2f", angle - .5f * 50 ); + CG_Text_Paint( 348, 364, .25f, color, s, 0, 0, 0 );*/ +} + +/* +============== +CG_DrawBinocReticle +============== +*/ +static void CG_DrawBinocReticle( void ) { + // an alternative. This gives nice sharp lines at the expense of a few extra polys + vec4_t color; + color[0] = color[1] = color[2] = 0; + color[3] = 1; + + if ( cgs.media.binocShaderSimple ) { + CG_DrawPic( 0, 0, 640, 480, cgs.media.binocShaderSimple ); + } + + CG_FillRect( 146, 239, 348, 1, color ); + + CG_FillRect( 188, 234, 1, 13, color ); // ll + CG_FillRect( 234, 226, 1, 29, color ); // l + CG_FillRect( 274, 234, 1, 13, color ); // lr + CG_FillRect( 320, 213, 1, 55, color ); // center + CG_FillRect( 360, 234, 1, 13, color ); // rl + CG_FillRect( 406, 226, 1, 29, color ); // r + CG_FillRect( 452, 234, 1, 13, color ); // rr +} + +void CG_FinishWeaponChange( int lastweap, int newweap ); // JPW NERVE + + +/* +================= +CG_DrawCrosshair +================= +*/ +static void CG_DrawCrosshair( void ) { + float w, h; + qhandle_t hShader; + float f; + float x, y; + int weapnum; // DHM - Nerve + + if ( cg.renderingThirdPerson ) { + return; + } + + // using binoculars + if ( cg.zoomedBinoc ) { + CG_DrawBinocReticle(); + return; + } + + // DHM - Nerve :: show reticle in limbo and spectator + if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) { + weapnum = cg.snap->ps.weapon; + } else { + weapnum = cg.weaponSelect; + } + + + switch ( weapnum ) { + + // weapons that get no reticle + case WP_NONE: // no weapon, no crosshair + if ( cg.zoomedBinoc ) { + CG_DrawBinocReticle(); + } + + if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + return; + } + break; + + // special reticle for weapon + case WP_FG42SCOPE: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + if ( !BG_PlayerMounted( cg.snap->ps.eFlags ) ) { + // JPW NERVE -- don't let players run with rifles -- speed 80 == crouch, 128 == walk, 256 == run + if ( VectorLengthSquared( cg.snap->ps.velocity ) > SQR( 127 ) ) { + if ( cg.snap->ps.weapon == WP_FG42SCOPE ) { + CG_FinishWeaponChange( WP_FG42SCOPE, WP_FG42 ); + } + if ( cg.snap->ps.weapon == WP_GARAND_SCOPE ) { + CG_FinishWeaponChange( WP_GARAND_SCOPE, WP_GARAND ); + } + if ( cg.snap->ps.weapon == WP_K43_SCOPE ) { + CG_FinishWeaponChange( WP_K43_SCOPE, WP_K43 ); + } + } + + // OSP + if ( cg.mvTotalClients < 1 || cg.snap->ps.stats[STAT_HEALTH] > 0 ) { + CG_DrawWeapReticle(); + } + + return; + } + break; + default: + break; + } + + if ( cg.predictedPlayerState.eFlags & EF_PRONE_MOVING ) { + return; + } + + // FIXME: spectators/chasing? + if ( cg.predictedPlayerState.weapon == WP_MORTAR_SET && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + CG_DrawMortarReticle(); + return; + } + + if ( cg_drawCrosshair.integer < 0 ) { //----(SA) moved down so it doesn't keep the scoped weaps from drawing reticles + return; + } + + // no crosshair while leaning + if ( cg.snap->ps.leanf ) { + return; + } + + // TAT 1/10/2003 - Don't draw crosshair if have exit hintcursor + if ( cg.snap->ps.serverCursorHint >= HINT_EXIT && cg.snap->ps.serverCursorHint <= HINT_NOEXIT ) { + return; + } + + // set color based on health + if ( cg_crosshairHealth.integer ) { + vec4_t hcolor; + + CG_ColorForHealth( hcolor ); + trap_R_SetColor( hcolor ); + } else { + trap_R_SetColor( cg.xhairColor ); + } + + w = h = cg_crosshairSize.value; + + // RF, crosshair size represents aim spread + f = (float)( ( cg_crosshairPulse.integer == 0 ) ? 0 : cg.snap->ps.aimSpreadScale / 255.0 ); + w *= ( 1 + f * 2.0 ); + h *= ( 1 + f * 2.0 ); + + x = cg_crosshairX.integer; + y = cg_crosshairY.integer; + CG_AdjustFrom640( &x, &y, &w, &h ); + + hShader = cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ]; + + trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, hShader ); + + if ( cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ) { + w = h = cg_crosshairSize.value; + x = cg_crosshairX.integer; + y = cg_crosshairY.integer; + CG_AdjustFrom640( &x, &y, &w, &h ); + + if ( cg_crosshairHealth.integer == 0 ) { + trap_R_SetColor( cg.xhairColorAlt ); + } + + trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ); + } +} + +static void CG_DrawNoShootIcon( void ) { + float x, y, w, h; + float *color; + + if ( cg.predictedPlayerState.eFlags & EF_PRONE && cg.snap->ps.weapon == WP_PANZERFAUST ) { + trap_R_SetColor( colorRed ); + } else if ( cg.crosshairClientNoShoot + // xkan, 1/6/2003 - don't shoot friend or civilian + || cg.snap->ps.serverCursorHint == HINT_PLYR_NEUTRAL + || cg.snap->ps.serverCursorHint == HINT_PLYR_FRIEND ) { + color = CG_FadeColor( cg.crosshairClientTime, 1000 ); + + if ( !color ) { + trap_R_SetColor( NULL ); + return; + } else { + trap_R_SetColor( color ); + } + } else { + return; + } + + w = h = 48.f; + + x = cg_crosshairX.integer + 1; + y = cg_crosshairY.integer + 1; + CG_AdjustFrom640( &x, &y, &w, &h ); + + // FIXME precache + trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), y + 0.5 * ( cg.refdef_current->height - h ), w, h, 0, 0, 1, 1, cgs.media.friendShader ); +} + +/* +================= +CG_ScanForCrosshairEntity +================= + +Returns the distance to the entity + +*/ +static float CG_ScanForCrosshairEntity( float * zChange, qboolean * hitClient ) { + trace_t trace; +// gentity_t *traceEnt; + vec3_t start, end; + float dist; + centity_t* cent; + + // We haven't hit a client yet + *hitClient = qfalse; + + VectorCopy( cg.refdef.vieworg, start ); + VectorMA( start, 8192, cg.refdef.viewaxis[0], end ); //----(SA) changed from 8192 + + cg.crosshairClientNoShoot = qfalse; + + CG_Trace( &trace, start, NULL, NULL, end, cg.snap->ps.clientNum, CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_ITEM ); + + // How far from start to end of trace? + dist = VectorDistance( start, trace.endpos ); + + // How far up or down are we looking? + *zChange = trace.endpos[2] - start[2]; + + if ( trace.entityNum >= MAX_CLIENTS ) { + if ( cg_entities[trace.entityNum].currentState.eFlags & EF_TAGCONNECT ) { + trace.entityNum = cg_entities[trace.entityNum].tagParent; + } + + // is a tank with a healthbar + // this might have some side-effects, but none right now as the script_mover is the only one that sets effect1Time + if ( ( cg_entities[trace.entityNum].currentState.eType == ET_MOVER && cg_entities[trace.entityNum].currentState.effect1Time ) || + cg_entities[trace.entityNum].currentState.eType == ET_CONSTRUCTIBLE_MARKER ) { + // update the fade timer + cg.crosshairClientNum = trace.entityNum; + cg.crosshairClientTime = cg.time; + cg.identifyClientRequest = cg.crosshairClientNum; + } + + // Default: We're not looking at a client + cg.crosshairNotLookingAtClient = qtrue; + + return dist; + } + +// traceEnt = &g_entities[trace.entityNum]; + + // Reset the draw time for the SP crosshair + cg.crosshairSPClientTime = cg.time; + + // Default: We're not looking at a client + cg.crosshairNotLookingAtClient = qfalse; + + // We hit a client + *hitClient = qtrue; + + // update the fade timer + cg.crosshairClientNum = trace.entityNum; + cg.crosshairClientTime = cg.time; + if ( cg.crosshairClientNum != cg.snap->ps.identifyClient && cg.crosshairClientNum != ENTITYNUM_WORLD ) { + cg.identifyClientRequest = cg.crosshairClientNum; + } + + cent = &cg_entities[cg.crosshairClientNum]; + + if ( cent && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) { + if ( cgs.clientinfo[cg.crosshairClientNum].team == cgs.clientinfo[cg.clientNum].team ) { + cg.crosshairClientNoShoot = qtrue; + } + } + + return dist; +} + + + +#define CH_KNIFE_DIST 48 // from g_weapon.c +#define CH_LADDER_DIST 100 +#define CH_WATER_DIST 100 +#define CH_BREAKABLE_DIST 64 +#define CH_DOOR_DIST 96 + +#define CH_DIST 100 //128 // use the largest value from above + +/* +============== +CG_CheckForCursorHints + concept in progress... +============== +*/ +void CG_CheckForCursorHints( void ) { + trace_t trace; + vec3_t start, end; + centity_t *tracent; + vec3_t pforward, eforward; + float dist; + + + if ( cg.renderingThirdPerson ) { + return; + } + + if ( cg.snap->ps.serverCursorHint ) { // server is dictating a cursor hint, use it. + cg.cursorHintTime = cg.time; + cg.cursorHintFade = 500; // fade out time + cg.cursorHintIcon = cg.snap->ps.serverCursorHint; + cg.cursorHintValue = cg.snap->ps.serverCursorHintVal; + return; + } + + // From here on it's client-side cursor hints. So if the server isn't sending that info (as an option) + // then it falls into here and you can get basic cursorhint info if you want, but not the detailed info + // the server sends. + + // the trace + VectorCopy( cg.refdef_current->vieworg, start ); + VectorMA( start, CH_DIST, cg.refdef_current->viewaxis[0], end ); + +// CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_ALL &~CONTENTS_MONSTERCLIP); + CG_Trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_PLAYERSOLID ); + + if ( trace.fraction == 1 ) { + return; + } + + dist = trace.fraction * CH_DIST; + + tracent = &cg_entities[ trace.entityNum ]; + + // Arnout: invisible entities don't show hints + if ( trace.entityNum >= MAX_CLIENTS && + ( tracent->currentState.powerups == STATE_INVISIBLE || + tracent->currentState.powerups == STATE_UNDERCONSTRUCTION ) ) { + return; + } + + // + // world + // + if ( trace.entityNum == ENTITYNUM_WORLD ) { + if ( ( trace.surfaceFlags & SURF_LADDER ) && !( cg.snap->ps.pm_flags & PMF_LADDER ) ) { + if ( dist <= CH_LADDER_DIST ) { + cg.cursorHintIcon = HINT_LADDER; + cg.cursorHintTime = cg.time; + cg.cursorHintFade = 500; + cg.cursorHintValue = 0; + } + } + + + } else if ( trace.entityNum < MAX_CLIENTS ) { // people + + // knife + if ( trace.entityNum < MAX_CLIENTS && ( cg.snap->ps.weapon == WP_KNIFE ) ) { + if ( dist <= CH_KNIFE_DIST ) { + + AngleVectors( cg.snap->ps.viewangles, pforward, NULL, NULL ); + AngleVectors( tracent->lerpAngles, eforward, NULL, NULL ); + + if ( DotProduct( eforward, pforward ) > 0.6f ) { // from behind(-ish) + cg.cursorHintIcon = HINT_KNIFE; + cg.cursorHintTime = cg.time; + cg.cursorHintFade = 100; + cg.cursorHintValue = 0; + } + } + } + } +} + + + +/* +===================== +CG_DrawCrosshairNames +===================== +*/ +static void CG_DrawCrosshairNames( void ) { + float *color; + char *name; + float w; + const char *s, *playerClass; + int playerHealth = 0; + vec4_t c; + float barFrac; + qboolean drawStuff = qfalse; + const char *playerRank; + qboolean isTank = qfalse; + int maxHealth = 1; + int i; + + // Distance to the entity under the crosshair + float dist; + float zChange; + + qboolean hitClient = qfalse; + + if ( cg_drawCrosshair.integer < 0 ) { + return; + } + + // scan the known entities to see if the crosshair is sighted on one + dist = CG_ScanForCrosshairEntity( &zChange, &hitClient ); + + if ( cg.renderingThirdPerson ) { + return; + } + + // draw the name of the player being looked at + color = CG_FadeColor( cg.crosshairClientTime, 1000 ); + + if ( !color ) { + trap_R_SetColor( NULL ); + return; + } + + // NERVE - SMF + if ( cg.crosshairClientNum > MAX_CLIENTS ) { + if ( !cg_drawCrosshairNames.integer ) { + return; + } + + if ( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) { + if ( cg_entities[cg.crosshairClientNum].currentState.eType == ET_MOVER && cg_entities[cg.crosshairClientNum].currentState.effect1Time ) { + isTank = qtrue; + + playerHealth = cg_entities[cg.crosshairClientNum].currentState.dl_intensity; + maxHealth = 255; + + s = Info_ValueForKey( CG_ConfigString( CS_SCRIPT_MOVER_NAMES ), va( "%i", cg.crosshairClientNum ) ); + if ( !*s ) { + return; + } + + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); + } else if ( cg_entities[cg.crosshairClientNum].currentState.eType == ET_CONSTRUCTIBLE_MARKER ) { + s = Info_ValueForKey( CG_ConfigString( CS_CONSTRUCTION_NAMES ), va( "%i", cg.crosshairClientNum ) ); + if ( *s ) { + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); + } + return; + } + } + + if ( !isTank ) { + return; + } + } else if ( cgs.clientinfo[cg.crosshairClientNum].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) { + if ( ( cg_entities[cg.crosshairClientNum].currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) && cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) { + if ( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR && + cgs.clientinfo[cg.snap->ps.clientNum].skill[SK_SIGNALS] >= 4 && cgs.clientinfo[cg.snap->ps.clientNum].cls == PC_FIELDOPS ) { + s = CG_TranslateString( "Disguised Enemy!" ); + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); + return; + } else if ( dist > 512 ) { + if ( !cg_drawCrosshairNames.integer ) { + return; + } + + drawStuff = qtrue; + + // determine player class + playerClass = BG_ClassLetterForNumber( ( cg_entities[ cg.crosshairClientNum ].currentState.powerups >> PW_OPS_CLASS_1 ) & 6 ); + + name = cgs.clientinfo[ cg.crosshairClientNum ].disguiseName; + + playerRank = cgs.clientinfo[ cg.crosshairClientNum ].team != TEAM_AXIS ? rankNames_Axis[cgs.clientinfo[cg.crosshairClientNum].disguiseRank] : rankNames_Allies[cgs.clientinfo[cg.crosshairClientNum].disguiseRank]; + s = va( "[%s] %s %s", CG_TranslateString( playerClass ), playerRank, name ); + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + + // draw the name and class + CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); + + // set the health + // rain - #480 - make sure it's the health for the right entity; + // if it's not, use the clientinfo health (which is updated + // by tinfo) + if ( cg.crosshairClientNum == cg.snap->ps.identifyClient ) { + playerHealth = cg.snap->ps.identifyClientHealth; + } else { + playerHealth = cgs.clientinfo[ cg.crosshairClientNum ].health; + } + + maxHealth = 100; + } else { + // rain - #480 - don't show the name after you look away, should this be + // a disguised covert + cg.crosshairClientTime = 0; + return; + } + } else { + return; + } + } + + if ( !cg_drawCrosshairNames.integer ) { + return; + } + + // Mad Doc - TDF + // changed this from early-exiting if true, to only executing most stuff if false. We want to + // show debug info regardless + + // we only want to see players on our team + if ( !isTank && !( cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR && cgs.clientinfo[ cg.crosshairClientNum ].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) ) { + drawStuff = qtrue; + + // determine player class + playerClass = BG_ClassLetterForNumber( cg_entities[ cg.crosshairClientNum ].currentState.teamNum ); + + name = cgs.clientinfo[ cg.crosshairClientNum ].name; + + playerRank = cgs.clientinfo[cg.crosshairClientNum].team == TEAM_AXIS ? rankNames_Axis[cgs.clientinfo[cg.crosshairClientNum].rank] : rankNames_Allies[cgs.clientinfo[cg.crosshairClientNum].rank]; + s = va( "[%s] %s %s", CG_TranslateString( playerClass ), playerRank, name ); + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + + // draw the name and class + CG_DrawSmallStringColor( 320 - w / 2, 170, s, color ); + + // set the health + if ( cg.crosshairClientNum == cg.snap->ps.identifyClient ) { + playerHealth = cg.snap->ps.identifyClientHealth; + } else { + playerHealth = cgs.clientinfo[ cg.crosshairClientNum ].health; + } + + maxHealth = 100; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.snap->ps.clientNum].team ) { + continue; + } + + if ( cgs.clientinfo[i].cls != PC_MEDIC ) { + continue; + } + + maxHealth += 10; + + if ( maxHealth >= 125 ) { + maxHealth = 125; + break; + } + } + + if ( cgs.clientinfo[ cg.crosshairClientNum ].skill[SK_BATTLE_SENSE] >= 3 ) { + maxHealth += 15; + } + + if ( cgs.clientinfo[ cg.crosshairClientNum ].cls == PC_MEDIC ) { + maxHealth *= 1.12f; + } + } + + // draw the health bar +// if ( isTank || (cg.crosshairClientNum == cg.snap->ps.identifyClient && drawStuff && cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR ) ) + { + vec4_t bgcolor; + + barFrac = (float)playerHealth / maxHealth; + + if ( barFrac > 1.0 ) { + barFrac = 1.0; + } else if ( barFrac < 0 ) { + barFrac = 0; + } + + c[0] = 1.0f; + c[1] = c[2] = barFrac; + c[3] = ( 0.25 + barFrac * 0.5 ) * color[3]; + + Vector4Set( bgcolor, 1.f, 1.f, 1.f, .25f * color[3] ); + + CG_FilledBar( 320 - 110 /*w*/ / 2, 190, 110, 10, c, NULL, bgcolor, barFrac, 16 ); + } + + // -NERVE - SMF + if ( isTank ) { + return; + } + + if ( drawStuff ) { + trap_R_SetColor( NULL ); + } +} + + + +//============================================================================== + +/* +================= +CG_DrawSpectator +================= +*/ +static void CG_DrawSpectator( void ) { + CG_DrawBigString( 320 - 9 * 8, 440, CG_TranslateString( "SPECTATOR" ), 1.f ); +} + +/* +================= +CG_DrawVote +================= +*/ +static void CG_DrawVote( void ) { + char *s; + char str1[32], str2[32]; + float color[4] = { 1, 1, 0, 1 }; + int sec; + + if ( cgs.complaintEndTime > cg.time && !cg.demoPlayback && cg_complaintPopUp.integer > 0 && cgs.complaintClient >= 0 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = va( CG_TranslateString( "File complaint against %s for team-killing?" ), cgs.clientinfo[cgs.complaintClient].name ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.applicationEndTime > cg.time && cgs.applicationClient >= 0 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = va( CG_TranslateString( "Accept %s's application to join your fireteam?" ), cgs.clientinfo[cgs.applicationClient].name ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.propositionEndTime > cg.time && cgs.propositionClient >= 0 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = va( CG_TranslateString( "Accept %s's proposition to invite %s to join your fireteam?" ), cgs.clientinfo[cgs.propositionClient2].name, cgs.clientinfo[cgs.propositionClient].name ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.invitationEndTime > cg.time && cgs.invitationClient >= 0 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = va( CG_TranslateString( "Accept %s's invitation to join their fireteam?" ), cgs.clientinfo[cgs.invitationClient].name ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.autoFireteamEndTime > cg.time && cgs.autoFireteamNum == -1 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = "Make Fireteam private?"; + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.autoFireteamCreateEndTime > cg.time && cgs.autoFireteamCreateNum == -1 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = "Create a Fireteam?"; + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.autoFireteamJoinEndTime > cg.time && cgs.autoFireteamJoinNum == -1 ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + s = "Join a Fireteam?"; + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Press '%s' for YES, or '%s' for No" ), str1, str2 ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + + if ( cgs.voteTime ) { + Q_strncpyz( str1, BindingFromName( "vote yes" ), 32 ); + Q_strncpyz( str2, BindingFromName( "vote no" ), 32 ); + + // play a talk beep whenever it is modified + if ( cgs.voteModified ) { + cgs.voteModified = qfalse; + } + + sec = ( VOTE_TIME - ( cg.time - cgs.voteTime ) ) / 1000; + if ( sec < 0 ) { + sec = 0; + } + + if ( !Q_stricmpn( cgs.voteString, "kick", 4 ) ) { + if ( strlen( cgs.voteString ) > 5 ) { + int nameindex; + char buffer[ 128 ]; + Q_strncpyz( buffer, cgs.voteString + 5, sizeof( buffer ) ); + Q_CleanStr( buffer ); + + for ( nameindex = 0; nameindex < MAX_CLIENTS; nameindex++ ) { + if ( !cgs.clientinfo[ nameindex ].infoValid ) { + continue; + } + + if ( !Q_stricmp( cgs.clientinfo[ nameindex ].cleanname, buffer ) ) { + if ( cgs.clientinfo[ nameindex ].team != TEAM_SPECTATOR && cgs.clientinfo[ nameindex ].team != cgs.clientinfo[ cg.clientNum ].team ) { + return; + } + } + } + } + } + + if ( !( cg.snap->ps.eFlags & EF_VOTED ) ) { + s = va( CG_TranslateString( "VOTE(%i): %s" ), sec, cgs.voteString ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + if ( cgs.clientinfo[cg.clientNum].team != TEAM_AXIS && cgs.clientinfo[cg.clientNum].team != TEAM_ALLIES ) { + s = CG_TranslateString( "Cannot vote as Spectator" ); + } else { + s = va( CG_TranslateString( "YES(%s):%i, NO(%s):%i" ), str1, cgs.voteYes, str2, cgs.voteNo ); + } + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 60 ); + return; + } else { + s = va( CG_TranslateString( "YOU VOTED ON: %s" ), cgs.voteString ); + CG_DrawStringExt( 8, 200, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + + s = va( CG_TranslateString( "Y:%i, N:%i" ), cgs.voteYes, cgs.voteNo ); + CG_DrawStringExt( 8, 214, s, color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 20 ); + return; + } + } + + if ( cgs.complaintEndTime > cg.time && !cg.demoPlayback && cg_complaintPopUp.integer > 0 && cgs.complaintClient < 0 ) { + if ( cgs.complaintClient == -1 ) { + s = "Your complaint has been filed"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + if ( cgs.complaintClient == -2 ) { + s = "Complaint dismissed"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + if ( cgs.complaintClient == -3 ) { + s = "Server Host cannot be complained against"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + if ( cgs.complaintClient == -4 ) { + s = "You were team-killed by the Server Host"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + } + + if ( cgs.applicationEndTime > cg.time && cgs.applicationClient < 0 ) { + if ( cgs.applicationClient == -1 ) { + s = "Your application has been submitted"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.applicationClient == -2 ) { + s = "Your application failed"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.applicationClient == -3 ) { + s = "Your application has been approved"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.applicationClient == -4 ) { + s = "Your application reply has been sent"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + } + + if ( cgs.propositionEndTime > cg.time && cgs.propositionClient < 0 ) { + if ( cgs.propositionClient == -1 ) { + s = "Your proposition has been submitted"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.propositionClient == -2 ) { + s = "Your proposition was rejected"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.propositionClient == -3 ) { + s = "Your proposition was accepted"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.propositionClient == -4 ) { + s = "Your proposition reply has been sent"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + } + + if ( cgs.invitationEndTime > cg.time && cgs.invitationClient < 0 ) { + if ( cgs.invitationClient == -1 ) { + s = "Your invitation has been submitted"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.invitationClient == -2 ) { + s = "Your invitation was rejected"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.invitationClient == -3 ) { + s = "Your invitation was accepted"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.invitationClient == -4 ) { + s = "Your invitation reply has been sent"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } + + if ( cgs.invitationClient < 0 ) { + return; + } + } + + if ( ( cgs.autoFireteamEndTime > cg.time && cgs.autoFireteamNum == -2 ) || ( cgs.autoFireteamCreateEndTime > cg.time && cgs.autoFireteamCreateNum == -2 ) || ( cgs.autoFireteamJoinEndTime > cg.time && cgs.autoFireteamJoinNum == -2 ) ) { + s = "Response Sent"; + CG_DrawStringExt( 8, 200, CG_TranslateString( s ), color, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 80 ); + return; + } +} + +/* +================= +CG_DrawIntermission +================= +*/ +static void CG_DrawIntermission( void ) { + // End-of-level autoactions + if ( !cg.demoPlayback ) { + static int doScreenshot = 0, doDemostop = 0; + + if ( !cg.latchAutoActions ) { + cg.latchAutoActions = qtrue; + + if ( cg_autoAction.integer & AA_SCREENSHOT ) { + doScreenshot = cg.time + 1000; + } + + if ( cg_autoAction.integer & AA_STATSDUMP ) { + CG_dumpStats_f(); + } + + if ( ( cg_autoAction.integer & AA_DEMORECORD ) && + ( ( cgs.gametype == GT_WOLF_STOPWATCH && cgs.currentRound == 0 ) || + cgs.gametype != GT_WOLF_STOPWATCH ) ) { + doDemostop = cg.time + 5000; // stats should show up within 5 seconds + } + } + + if ( doScreenshot > 0 && doScreenshot < cg.time ) { + CG_autoScreenShot_f(); + doScreenshot = 0; + } + + if ( doDemostop > 0 && doDemostop < cg.time ) { + trap_SendConsoleCommand( "stoprecord\n" ); + doDemostop = 0; + } + } + + // Intermission view + CG_Debriefing_Draw(); + +/* cg.scoreFadeTime = cg.time; + CG_DrawScoreboard(); +*/ +} + +/* +================= +CG_ActivateLimboMenu + +NERVE - SMF +================= +*/ +static void CG_ActivateLimboMenu( void ) { +/* static qboolean latch = qfalse; + qboolean test; + + // should we open the limbo menu (make allowances for MV clients) + test = ((cg.snap->ps.pm_flags & PMF_LIMBO) || + ( (cg.mvTotalClients < 1 && ( + (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) || + (cg.warmup)) + ) + && cg.snap->ps.pm_type != PM_INTERMISSION ) ); + + + // auto open/close limbo mode + if(cg_popupLimboMenu.integer && !cg.demoPlayback) { + if(test && !latch) { + CG_LimboMenu_f(); + latch = qtrue; + } else if(!test && latch && cg.showGameView) { + CG_EventHandling(CGAME_EVENT_NONE, qfalse); + latch = qfalse; + } + }*/ +} + +/* +================= +CG_DrawSpectatorMessage +================= +*/ +static void CG_DrawSpectatorMessage( void ) { + const char *str, *str2; + float x, y; + static int lastconfigGet = 0; + + if ( !cg_descriptiveText.integer ) { + return; + } + + if ( !( cg.snap->ps.pm_flags & PMF_LIMBO || cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) ) { + return; + } + + if ( cg.time - lastconfigGet > 1000 ) { + Controls_GetConfig(); + + lastconfigGet = cg.time; + } + + x = ( cg.snap->ps.pm_flags & PMF_LIMBO ) ? 170 : 80; + y = 408; + + y -= 2 * TINYCHAR_HEIGHT; + + str2 = BindingFromName( "openlimbomenu" ); + if ( !Q_stricmp( str2, "(openlimbomenu)" ) ) { + str2 = "ESCAPE"; + } + str = va( CG_TranslateString( "Press %s to open Limbo Menu" ), str2 ); + CG_DrawStringExt( 8, 154, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); + + str2 = BindingFromName( "+attack" ); + str = va( CG_TranslateString( "Press %s to follow next player" ), str2 ); + CG_DrawStringExt( 8, 172, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); + +#ifdef MV_SUPPORT + str2 = BindingFromName( "mvactivate" ); + str = va( CG_TranslateString( "- Press %s to %s multiview mode" ), str2, ( ( cg.mvTotalClients > 0 ) ? "disable" : "activate" ) ); + CG_DrawStringExt( x, y, str, colorWhite, qtrue, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 ); + y += TINYCHAR_HEIGHT; +#endif +} + + +float CG_CalculateReinfTime_Float( qboolean menu ) { + team_t team; + int dwDeployTime; + + if ( menu ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) { + team = cgs.ccSelectedTeam == 0 ? TEAM_AXIS : TEAM_ALLIES; + } else { + team = cgs.clientinfo[cg.clientNum].team; + } + } else { + team = cgs.clientinfo[cg.snap->ps.clientNum].team; + } + + dwDeployTime = ( team == TEAM_AXIS ) ? cg_redlimbotime.integer : cg_bluelimbotime.integer; + return ( 1 + ( dwDeployTime - ( ( cgs.aReinfOffset[team] + cg.time - cgs.levelStartTime ) % dwDeployTime ) ) * 0.001f ); +} + +int CG_CalculateReinfTime( qboolean menu ) { + return( (int)CG_CalculateReinfTime_Float( menu ) ); +} + + +/* +================= +CG_DrawLimboMessage +================= +*/ + +#define INFOTEXT_STARTX 8 + +static void CG_DrawLimboMessage( void ) { + float color[4] = { 1, 1, 1, 1 }; + const char *str; + playerState_t *ps; + int y = 118; + + + ps = &cg.snap->ps; + + if ( ps->stats[STAT_HEALTH] > 0 ) { + return; + } + + if ( cg.snap->ps.pm_flags & PMF_LIMBO || cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) { + return; + } + + if ( cg_descriptiveText.integer ) { + str = CG_TranslateString( "You are wounded and waiting for a medic." ); + CG_DrawSmallStringColor( INFOTEXT_STARTX, y, str, color ); + y += 18; + + if ( cgs.gametype == GT_WOLF_LMS ) { + trap_R_SetColor( NULL ); + return; + } + + str = CG_TranslateString( "Press JUMP to go into reinforcement queue." ); + CG_DrawSmallStringColor( INFOTEXT_STARTX, 134, str, color ); + y += 18; + } else if ( cgs.gametype == GT_WOLF_LMS ) { + trap_R_SetColor( NULL ); + return; + } + + // JPW NERVE + str = ( ps->persistant[PERS_RESPAWNS_LEFT] == 0 ) ? CG_TranslateString( "No more reinforcements this round." ) : va( CG_TranslateString( "Reinforcements deploy in %d seconds." ), CG_CalculateReinfTime( qfalse ) ); + + CG_DrawSmallStringColor( INFOTEXT_STARTX, y, str, color ); + y += 18; + // jpw + + trap_R_SetColor( NULL ); +} +// -NERVE - SMF + +/* +================= +CG_DrawFollow +================= +*/ +static qboolean CG_DrawFollow( void ) { + char deploytime[128]; + + // MV following info for mainview + if ( CG_ViewingDraw() ) { + return( qtrue ); + } + + if ( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { + return( qfalse ); + } + + // if in limbo, show different follow message + if ( cg.snap->ps.pm_flags & PMF_LIMBO ) { + if ( cgs.gametype != GT_WOLF_LMS ) { + if ( cg.snap->ps.persistant[PERS_RESPAWNS_LEFT] == 0 ) { + if ( cg.snap->ps.persistant[PERS_RESPAWNS_PENALTY] >= 0 ) { + int deployTime = ( cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_AXIS ) ? cg_redlimbotime.integer : cg_bluelimbotime.integer; + + deployTime *= 0.001f; + + sprintf( deploytime, CG_TranslateString( "Bonus Life! Deploying in %d seconds" ), CG_CalculateReinfTime( qfalse ) + cg.snap->ps.persistant[PERS_RESPAWNS_PENALTY] * deployTime ); + } else { + sprintf( deploytime, CG_TranslateString( "No more deployments this round" ) ); + } + } else { + sprintf( deploytime, CG_TranslateString( "Deploying in %d seconds" ), CG_CalculateReinfTime( qfalse ) ); + } + + CG_DrawStringExt( INFOTEXT_STARTX, 118, deploytime, colorWhite, qtrue, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 80 ); + } + + // Don't display if you're following yourself + if ( cg.snap->ps.clientNum != cg.clientNum ) { + sprintf( deploytime, "(%s %s %s [%s])", CG_TranslateString( "Following" ), + cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ? rankNames_Allies[cgs.clientinfo[cg.snap->ps.clientNum].rank] : rankNames_Axis[cgs.clientinfo[cg.snap->ps.clientNum].rank], + cgs.clientinfo[cg.snap->ps.clientNum].name, + BG_ClassnameForNumber( cgs.clientinfo[cg.snap->ps.clientNum].cls ) ); + + CG_DrawStringExt( INFOTEXT_STARTX, 136, deploytime, colorWhite, qtrue, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 80 ); + } + } else { + CG_DrawStringExt( INFOTEXT_STARTX, 118, CG_TranslateString( "Following" ), colorWhite, qtrue, qtrue, BIGCHAR_WIDTH / 2, BIGCHAR_HEIGHT, 0 ); + + CG_DrawStringExt( 84, 118, va( "%s %s [%s]", + cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ? rankNames_Allies[cgs.clientinfo[cg.snap->ps.clientNum].rank] : rankNames_Axis[cgs.clientinfo[cg.snap->ps.clientNum].rank], + cgs.clientinfo[cg.snap->ps.clientNum].name, BG_ClassnameForNumber( cgs.clientinfo[cg.snap->ps.clientNum].cls ) ), + colorWhite, qtrue, qtrue, BIGCHAR_WIDTH / 2, BIGCHAR_HEIGHT, 0 ); + } + + return( qtrue ); +} + + +/* +================= +CG_DrawWarmup +================= +*/ +static void CG_DrawWarmup( void ) { + int w; + int sec; + int cw; + const char *s, *s1, *s2; + + sec = cg.warmup; + if ( !sec ) { + if ( ( cgs.gamestate == GS_WARMUP && !cg.warmup ) || cgs.gamestate == GS_WAITING_FOR_PLAYERS ) { + cw = 10; + + s1 = va( CG_TranslateString( "^3WARMUP:^7 Waiting on ^2%i^7 %s" ), cgs.minclients, cgs.minclients == 1 ? "player" : "players" ); + w = CG_DrawStrlen( s1 ); + CG_DrawStringExt( 320 - w * 12 / 2, 188, s1, colorWhite, qfalse, qtrue, 12, 18, 0 ); + + if ( !cg.demoPlayback && cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR && + ( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) || ( cg.snap->ps.pm_flags & PMF_LIMBO ) ) ) { + char str1[32]; + Q_strncpyz( str1, BindingFromName( "ready" ), 32 ); + if ( !Q_stricmp( str1, "(?" "?" "?)" ) ) { + s2 = CG_TranslateString( "Type ^3\\ready^* in the console to start" ); + } else { + s2 = va( "Press ^3%s^* to start", str1 ); + s2 = CG_TranslateString( s2 ); + } + w = CG_DrawStrlen( s2 ); + CG_DrawStringExt( 320 - w * cw / 2, 208, s2, colorWhite, qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); + } + +/* if ( !sec ) { + if ( cgs.gamestate == GS_WAITING_FOR_PLAYERS ) { + cw = 10; + + s = CG_TranslateString( "Game Stopped - Waiting for more players" ); + + w = CG_DrawStrlen( s ); + CG_DrawStringExt( 320 - w * 6, 120, s, colorWhite, qfalse, qtrue, 12, 18, 0 ); + + if( cg_gameType.integer != GT_WOLF_LMS ) { + s1 = va( CG_TranslateString( "Waiting for %i players" ), cgs.minclients ); + s2 = CG_TranslateString( "or call a vote to start the match" ); + + w = CG_DrawStrlen( s1 ); + CG_DrawStringExt( 320 - w * cw/2, 160, s1, colorWhite, + qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); + + w = CG_DrawStrlen( s2 ); + CG_DrawStringExt( 320 - w * cw/2, 180, s2, colorWhite, + qfalse, qtrue, cw, (int)(cw * 1.5), 0 ); + } +*/ + return; + } + + return; + } + + sec = ( sec - cg.time ) / 1000; + if ( sec < 0 ) { + sec = 0; + } + + s = va( "%s %i", CG_TranslateString( "(WARMUP) Match begins in:" ), sec + 1 ); + + w = CG_DrawStrlen( s ); + CG_DrawStringExt( 320 - w * 6, 120, s, colorYellow, qfalse, qtrue, 12, 18, 0 ); + + // NERVE - SMF - stopwatch stuff + s1 = ""; + s2 = ""; + + if ( cgs.gametype == GT_WOLF_STOPWATCH ) { + const char *cs; + int defender; + + s = va( "%s %i", CG_TranslateString( "Stopwatch Round" ), cgs.currentRound + 1 ); + + cs = CG_ConfigString( CS_MULTI_INFO ); + defender = atoi( Info_ValueForKey( cs, "defender" ) ); + + if ( !defender ) { + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { + if ( cgs.currentRound == 1 ) { + s1 = "You have been switched to the Axis team"; + s2 = "Keep the Allies from beating the clock!"; + } else { + s1 = "You are on the Axis team"; + } + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + if ( cgs.currentRound == 1 ) { + s1 = "You have been switched to the Allied team"; + s2 = "Try to beat the clock!"; + } else { + s1 = "You are on the Allied team"; + } + } + } else { + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { + if ( cgs.currentRound == 1 ) { + s1 = "You have been switched to the Axis team"; + s2 = "Try to beat the clock!"; + } else { + s1 = "You are on the Axis team"; + } + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + if ( cgs.currentRound == 1 ) { + s1 = "You have been switched to the Allied team"; + s2 = "Keep the Axis from beating the clock!"; + } else { + s1 = "You are on the Allied team"; + } + } + } + + if ( strlen( s1 ) ) { + s1 = CG_TranslateString( s1 ); + } + + if ( strlen( s2 ) ) { + s2 = CG_TranslateString( s2 ); + } + + cw = 10; + + w = CG_DrawStrlen( s ); + CG_DrawStringExt( 320 - w * cw / 2, 140, s, colorWhite, + qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); + + w = CG_DrawStrlen( s1 ); + CG_DrawStringExt( 320 - w * cw / 2, 160, s1, colorWhite, + qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); + + w = CG_DrawStrlen( s2 ); + CG_DrawStringExt( 320 - w * cw / 2, 180, s2, colorWhite, + qfalse, qtrue, cw, (int)( cw * 1.5 ), 0 ); + } +} + +//================================================================================== + +/* +================= +CG_DrawFlashFade +================= +*/ +static void CG_DrawFlashFade( void ) { + static int lastTime; + int elapsed, time; + vec4_t col; + qboolean fBlackout = ( !CG_IsSinglePlayer() && int_ui_blackout.integer > 0 ); + + if ( cgs.fadeStartTime + cgs.fadeDuration < cg.time ) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } else if ( cgs.fadeAlphaCurrent != cgs.fadeAlpha ) { + elapsed = ( time = trap_Milliseconds() ) - lastTime; // we need to use trap_Milliseconds() here since the cg.time gets modified upon reloading + lastTime = time; + if ( elapsed < 500 && elapsed > 0 ) { + if ( cgs.fadeAlphaCurrent > cgs.fadeAlpha ) { + cgs.fadeAlphaCurrent -= ( (float)elapsed / (float)cgs.fadeDuration ); + if ( cgs.fadeAlphaCurrent < cgs.fadeAlpha ) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } + } else { + cgs.fadeAlphaCurrent += ( (float)elapsed / (float)cgs.fadeDuration ); + if ( cgs.fadeAlphaCurrent > cgs.fadeAlpha ) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } + } + } + } + + // OSP - ugh, have to inform the ui that we need to remain blacked out (or not) + if ( int_ui_blackout.integer == 0 ) { + if ( cg.mvTotalClients < 1 && cg.snap->ps.powerups[PW_BLACKOUT] > 0 ) { + trap_Cvar_Set( "ui_blackout", va( "%d", cg.snap->ps.powerups[PW_BLACKOUT] ) ); + } + } else if ( cg.snap->ps.powerups[PW_BLACKOUT] == 0 || cg.mvTotalClients > 0 ) { + trap_Cvar_Set( "ui_blackout", "0" ); + } + + // now draw the fade + if ( cgs.fadeAlphaCurrent > 0.0 || fBlackout ) { + VectorClear( col ); + col[3] = ( fBlackout ) ? 1.0f : cgs.fadeAlphaCurrent; +// CG_FillRect( -10, -10, 650, 490, col ); + CG_FillRect( 0, 0, 640, 480, col ); // why do a bunch of these extend outside 640x480? + + //bani - #127 - bail out if we're a speclocked spectator with cg_draw2d = 0 + if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR && !cg_draw2D.integer ) { + return; + } + + // OSP - Show who is speclocked + if ( fBlackout ) { + int i, nOffset = 90; + char *str, *format = "The %s team is speclocked!"; + char *teams[TEAM_NUM_TEAMS] = { "??", "AXIS", "ALLIES", "???" }; + float color[4] = { 1, 1, 0, 1 }; + + for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) { + if ( cg.snap->ps.powerups[PW_BLACKOUT] & i ) { + str = va( format, teams[i] ); + CG_DrawStringExt( INFOTEXT_STARTX, nOffset, str, color, qtrue, qfalse, 10, 10, 0 ); + nOffset += 12; + } + } + } + } +} + + + +/* +============== +CG_DrawFlashZoomTransition + hide the snap transition from regular view to/from zoomed + + FIXME: TODO: use cg_fade? +============== +*/ +static void CG_DrawFlashZoomTransition( void ) { + vec4_t color; + float frac; + int fadeTime; + + if ( !cg.snap ) { + return; + } + + if ( BG_PlayerMounted( cg.snap->ps.eFlags ) ) { + // don't draw when on mg_42 + // keep the timer fresh so when you remove yourself from the mg42, it'll fade + cg.zoomTime = cg.time; + return; + } + + if ( cg.renderingThirdPerson ) { + return; + } + + fadeTime = 400; + + frac = cg.time - cg.zoomTime; + + if ( frac < fadeTime ) { + frac = frac / (float)fadeTime; + Vector4Set( color, 0, 0, 0, 1.0f - frac ); + CG_FillRect( -10, -10, 650, 490, color ); + } +} + + + +/* +================= +CG_DrawFlashDamage +================= +*/ +static void CG_DrawFlashDamage( void ) { + vec4_t col; + float redFlash; + + if ( !cg.snap ) { + return; + } + + if ( cg.v_dmg_time > cg.time ) { + redFlash = fabs( cg.v_dmg_pitch * ( ( cg.v_dmg_time - cg.time ) / DAMAGE_TIME ) ); + + // blend the entire screen red + if ( redFlash > 5 ) { + redFlash = 5; + } + + VectorSet( col, 0.2, 0, 0 ); + col[3] = 0.7 * ( redFlash / 5.0 ) * ( ( cg_bloodFlash.value > 1.0 ) ? 1.0 : + ( cg_bloodFlash.value < 0.0 ) ? 0.0 : + cg_bloodFlash.value ); + + CG_FillRect( -10, -10, 650, 490, col ); + } +} + + +/* +================= +CG_DrawFlashFire +================= +*/ +static void CG_DrawFlashFire( void ) { + vec4_t col = {1,1,1,1}; + float alpha, max, f; + + if ( !cg.snap ) { + return; + } + + if ( cg.renderingThirdPerson ) { + return; + } + + if ( !cg.snap->ps.onFireStart ) { + cg.v_noFireTime = cg.time; + return; + } + + alpha = (float)( ( FIRE_FLASH_TIME - 1000 ) - ( cg.time - cg.snap->ps.onFireStart ) ) / ( FIRE_FLASH_TIME - 1000 ); + if ( alpha > 0 ) { + if ( alpha >= 1.0 ) { + alpha = 1.0; + } + + // fade in? + f = (float)( cg.time - cg.v_noFireTime ) / FIRE_FLASH_FADEIN_TIME; + if ( f >= 0.0 && f < 1.0 ) { + alpha = f; + } + + max = 0.5 + 0.5 * sin( (float)( ( cg.time / 10 ) % 1000 ) / 1000.0 ); + if ( alpha > max ) { + alpha = max; + } + col[0] = alpha; + col[1] = alpha; + col[2] = alpha; + col[3] = alpha; + trap_R_SetColor( col ); + CG_DrawPic( -10, -10, 650, 490, cgs.media.viewFlashFire[( cg.time / 50 ) % 16] ); + trap_R_SetColor( NULL ); + + trap_S_AddLoopingSound( cg.snap->ps.origin, vec3_origin, cgs.media.flameSound, (int)( 255.0 * alpha ), 0 ); + trap_S_AddLoopingSound( cg.snap->ps.origin, vec3_origin, cgs.media.flameCrackSound, (int)( 255.0 * alpha ), 0 ); + } else { + cg.v_noFireTime = cg.time; + } +} + + + +/* +============== +CG_DrawFlashBlendBehindHUD + screen flash stuff drawn first (on top of world, behind HUD) +============== +*/ +static void CG_DrawFlashBlendBehindHUD( void ) { + CG_DrawFlashZoomTransition(); + CG_DrawFlashFade(); +} + + +/* +================= +CG_DrawFlashBlend + screen flash stuff drawn last (on top of everything) +================= +*/ +static void CG_DrawFlashBlend( void ) { + // Gordon: no flash blends if in limbo or spectator, and in the limbo menu + if ( ( cg.snap->ps.pm_flags & PMF_LIMBO || cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) && cg.showGameView ) { + return; + } + + CG_DrawFlashFire(); + CG_DrawFlashDamage(); +} + +// NERVE - SMF +/* +================= +CG_DrawObjectiveInfo +================= +*/ +#define OID_LEFT 10 +#define OID_TOP 360 + +void CG_ObjectivePrint( const char *str, int charWidth ) { + char *s; + int i, len; // NERVE - SMF + qboolean neednewline = qfalse; // NERVE - SMF + + if ( cg.centerPrintTime ) { + return; + } + + s = CG_TranslateString( str ); + + Q_strncpyz( cg.oidPrint, s, sizeof( cg.oidPrint ) ); + + // NERVE - SMF - turn spaces into newlines, if we've run over the linewidth + len = strlen( cg.oidPrint ); + for ( i = 0; i < len; i++ ) { + + // NOTE: subtract a few chars here so long words still get displayed properly + if ( i % ( CP_LINEWIDTH - 20 ) == 0 && i > 0 ) { + neednewline = qtrue; + } + if ( cg.oidPrint[i] == ' ' && neednewline ) { + cg.oidPrint[i] = '\n'; + neednewline = qfalse; + } + } + // -NERVE - SMF + + cg.oidPrintTime = cg.time; + cg.oidPrintY = OID_TOP; + cg.oidPrintCharWidth = charWidth; + + // count the number of lines for oiding + cg.oidPrintLines = 1; + s = cg.oidPrint; + while ( *s ) { + if ( *s == '\n' ) { + cg.oidPrintLines++; + } + s++; + } +} + +static void CG_DrawObjectiveInfo( void ) { + char *start; + int l; + int x, y, w,h; + int x1, y1, x2, y2; + float *color; + vec4_t backColor; + backColor[0] = 0.2f; + backColor[1] = 0.2f; + backColor[2] = 0.2f; + backColor[2] = 1.f; + + if ( !cg.oidPrintTime ) { + return; + } + + color = CG_FadeColor( cg.oidPrintTime, 250 ); + if ( !color ) { + cg.oidPrintTime = 0; + return; + } + + trap_R_SetColor( color ); + + start = cg.oidPrint; + +// JPW NERVE + // y = cg.oidPrintY - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; + y = 400 - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; + + x1 = 319; + y1 = y - 2; + x2 = 321; +// jpw + + // first just find the bounding rect + while ( 1 ) { + char linebuffer[1024]; + + for ( l = 0; l < CP_LINEWIDTH; l++ ) { + if ( !start[l] || start[l] == '\n' ) { + break; + } + linebuffer[l] = start[l]; + } + linebuffer[l] = 0; + + w = cg.oidPrintCharWidth * CG_DrawStrlen( linebuffer ) + 10; +// JPW NERVE + if ( 320 - w / 2 < x1 ) { + x1 = 320 - w / 2; + x2 = 320 + w / 2; + } + +/* + if ( x1 + w > x2 ) + x2 = x1 + w; +*/ + x = 320 - w / 2; +// jpw + y += cg.oidPrintCharWidth * 1.5; + + while ( *start && ( *start != '\n' ) ) { + start++; + } + if ( !*start ) { + break; + } + start++; + } + + x2 = x2 + 4; + y2 = y - cg.oidPrintCharWidth * 1.5 + 4; + + h = y2 - y1; // JPW NERVE + + VectorCopy( color, backColor ); + backColor[3] = 0.5 * color[3]; + trap_R_SetColor( backColor ); + + CG_DrawPic( x1, y1, x2 - x1, y2 - y1, cgs.media.teamStatusBar ); + + VectorSet( backColor, 0, 0, 0 ); + CG_DrawRect( x1, y1, x2 - x1, y2 - y1, 1, backColor ); + + trap_R_SetColor( color ); + + // do the actual drawing + start = cg.oidPrint; +// y = cg.oidPrintY - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; + y = 400 - cg.oidPrintLines * BIGCHAR_HEIGHT / 2; // JPW NERVE + + + while ( 1 ) { + char linebuffer[1024]; + + for ( l = 0; l < CP_LINEWIDTH; l++ ) { + if ( !start[l] || start[l] == '\n' ) { + break; + } + linebuffer[l] = start[l]; + } + linebuffer[l] = 0; + + w = cg.oidPrintCharWidth * CG_DrawStrlen( linebuffer ); + if ( x1 + w > x2 ) { + x2 = x1 + w; + } + + x = 320 - w / 2; // JPW NERVE + + CG_DrawStringExt( x, y, linebuffer, color, qfalse, qtrue, + cg.oidPrintCharWidth, (int)( cg.oidPrintCharWidth * 1.5 ), 0 ); + + y += cg.oidPrintCharWidth * 1.5; + + while ( *start && ( *start != '\n' ) ) { + start++; + } + if ( !*start ) { + break; + } + start++; + } + + trap_R_SetColor( NULL ); +} + +//================================================================================== + +void CG_DrawTimedMenus() { + if ( cg.voiceTime ) { + int t = cg.time - cg.voiceTime; + if ( t > 2500 ) { + Menus_CloseByName( "voiceMenu" ); + trap_Cvar_Set( "cl_conXOffset", "0" ); + cg.voiceTime = 0; + } + } +} + +/* +================= +CG_Fade +================= +*/ +void CG_Fade( int r, int g, int b, int a, int time, int duration ) { + + // incorporate this into the current fade scheme + + cgs.fadeAlpha = (float)a / 255.0f; + cgs.fadeStartTime = time; + cgs.fadeDuration = duration; + + if ( cgs.fadeStartTime + cgs.fadeDuration <= cg.time ) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } + return; + + + if ( time <= 0 ) { // do instantly + cg.fadeRate = 1; + cg.fadeTime = cg.time - 1; // set cg.fadeTime behind cg.time so it will start out 'done' + } else { + cg.fadeRate = 1.0f / time; + cg.fadeTime = cg.time + time; + } + + cg.fadeColor2[ 0 ] = ( float )r / 255.0f; + cg.fadeColor2[ 1 ] = ( float )g / 255.0f; + cg.fadeColor2[ 2 ] = ( float )b / 255.0f; + cg.fadeColor2[ 3 ] = ( float )a / 255.0f; +} + +/* +================= +CG_ScreenFade +================= +*/ +static void CG_ScreenFade( void ) { + int msec; + int i; + float t, invt; + vec4_t color; + + if ( !cg.fadeRate ) { + return; + } + + msec = cg.fadeTime - cg.time; + if ( msec <= 0 ) { + cg.fadeColor1[ 0 ] = cg.fadeColor2[ 0 ]; + cg.fadeColor1[ 1 ] = cg.fadeColor2[ 1 ]; + cg.fadeColor1[ 2 ] = cg.fadeColor2[ 2 ]; + cg.fadeColor1[ 3 ] = cg.fadeColor2[ 3 ]; + + if ( !cg.fadeColor1[ 3 ] ) { + cg.fadeRate = 0; + return; + } + + CG_FillRect( 0, 0, 640, 480, cg.fadeColor1 ); + + } else { + t = ( float )msec * cg.fadeRate; + invt = 1.0f - t; + + for ( i = 0; i < 4; i++ ) { + color[ i ] = cg.fadeColor1[ i ] * t + cg.fadeColor2[ i ] * invt; + } + + if ( color[ 3 ] ) { + CG_FillRect( 0, 0, 640, 480, color ); + } + } +} + +#if 0 // rain - unused +// JPW NERVE +void CG_Draw2D2( void ) { + qhandle_t weapon; + + trap_R_SetColor( NULL ); + + CG_DrawPic( 0,480, 640, -70, cgs.media.hud1Shader ); + + if ( !BG_PlayerMounted( cg.snap->ps.eFlags ) ) { + switch ( cg.snap->ps.weapon ) { + case WP_COLT: + case WP_LUGER: + weapon = cgs.media.hud2Shader; + break; + case WP_KNIFE: + weapon = cgs.media.hud5Shader; + break; + default: + weapon = cgs.media.hud3Shader; + } + CG_DrawPic( 220,410, 200,-200,weapon ); + } +} +#endif + +/* +================= +CG_DrawCompassIcon + +NERVE - SMF +================= +*/ +void CG_DrawCompassIcon( float x, float y, float w, float h, vec3_t origin, vec3_t dest, qhandle_t shader ) { + float angle, pi2 = M_PI * 2; + vec3_t v1, angles; + float len; + + VectorCopy( dest, v1 ); + VectorSubtract( origin, v1, v1 ); + len = VectorLength( v1 ); + VectorNormalize( v1 ); + vectoangles( v1, angles ); + + if ( v1[0] == 0 && v1[1] == 0 && v1[2] == 0 ) { + return; + } + +// if( cg_drawCompass.integer == 2 ) +// angles[YAW] = AngleSubtract( 90, angles[YAW] ); +// else + angles[YAW] = AngleSubtract( cg.predictedPlayerState.viewangles[YAW], angles[YAW] ); + + angle = ( ( angles[YAW] + 180.f ) / 360.f - ( 0.50 / 2.f ) ) * pi2; + + +// if (!CG_IsSinglePlayer()) { + w /= 2; + h /= 2; + + x += w; + y += h; + + +// if (CG_IsSinglePlayer()) +/* if (0) + { + w = 80; // hardcoded, because it has to fit the art + } + else*/ + { + w = sqrt( ( w * w ) + ( h * h ) ) / 3.f * 2.f * 0.9f; + } + + x = x + ( cos( angle ) * w ); + y = y + ( sin( angle ) * w ); + + len = 1 - min( 1.f, len / 2000.f ); + + + CG_DrawPic( x - ( 14 * len + 4 ) / 2, y - ( 14 * len + 4 ) / 2, 14 * len + 8, 14 * len + 8, shader ); +#ifdef SQUARE_COMPASS +} else { + int iconWidth, iconHeight; + // START Mad Doc - TDF + // talk about fitting a square peg into a round hole... + // we're now putting the compass icons around the square automap instead of the round compass + + while ( angle < 0 ) + angle += pi2; + + while ( angle >= pi2 ) + angle -= pi2; + + + x = x + w / 2; + y = y + h / 2; + w /= 2; // = sqrt( ( w * w ) + ( h * h ) ) / 3.f * 2.f * 0.9f; + + if ( ( angle >= 0 ) && ( angle < M_PI / 4.0 ) ) { + x += w; + y += w * tan( angle ); + + } else if ( ( angle >= M_PI / 4.0 ) && ( angle < 3.0 * M_PI / 4.0 ) ) { + x += w / tan( angle ); + y += w; + } else if ( ( angle >= 3.0 * M_PI / 4.0 ) && ( angle < 5.0 * M_PI / 4.0 ) ) { + x -= w; + y -= w * tan( angle ); + } else if ( ( angle >= 5.0 * M_PI / 4.0 ) && ( angle < 7.0 * M_PI / 4.0 ) ) { + x -= w / tan( angle ); + y -= w; + } else + { + x += w; + y += w * tan( angle ); + + } + + len = 1 - min( 1.f, len / 2000.f ); + iconWidth = 14 * len + 4; // where did this calc. come from? + iconHeight = 14 * len + 4; + + // adjust so that icon is always outside of the map + if ( ( angle > 5.0 * M_PI / 4.0 ) && ( angle < 2 * M_PI ) ) { + + y -= iconHeight; + } + + if ( ( angle >= 3.0 * M_PI / 4.0 ) && ( angle <= 5.0 * M_PI / 4.0 ) ) { + x -= iconWidth; + } + + + CG_DrawPic( x, y, iconWidth, iconHeight, shader ); + + + // END Mad Doc - TDF + +} +#endif +} + +/* +================= +CG_DrawNewCompass +================= +*/ +static void CG_DrawNewCompass( void ) { + float basex, basey; + float basew, baseh; + snapshot_t *snap; + float angle; + int i; + static float lastangle = 0; + static float anglespeed = 0; + float diff; + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + if ( snap->ps.pm_flags & PMF_LIMBO || snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || cg.mvTotalClients > 0 ) { + return; + } + + // Arnout: bit larger + basex = 520 - 16; + basey = 20 - 16; + basew = 100 + 32; + baseh = 100 + 32; + + CG_DrawAutoMap(); + + if ( cgs.autoMapExpanded ) { + if ( cg.time - cgs.autoMapExpandTime < 100.f ) { + basey -= ( ( cg.time - cgs.autoMapExpandTime ) / 100.f ) * 128.f; + } else { + //basey -= 128.f; + return; + } + } else { + if ( cg.time - cgs.autoMapExpandTime <= 150.f ) { + //basey -= 128.f; + return; + } else if ( ( cg.time - cgs.autoMapExpandTime > 150.f ) && ( cg.time - cgs.autoMapExpandTime < 250.f ) ) { + + basey = ( basey - 128.f ) + ( ( cg.time - cgs.autoMapExpandTime - 150.f ) / 100.f ) * 128.f; + } else { + rectDef_t compassHintRect = { 640 - 22, 128, 20, 20 }; + + CG_DrawKeyHint( &compassHintRect, "+mapexpand" ); + } + } + + CG_DrawPic( basex + 4, basey + 4, basew - 8, baseh - 8, cgs.media.compassShader ); + + angle = ( cg.predictedPlayerState.viewangles[YAW] + 180.f ) / 360.f - ( 0.125f ); + diff = AngleSubtract( angle * 360, lastangle * 360 ) / 360.f; + anglespeed /= 1.08f; + anglespeed += diff * 0.01f; + if ( Q_fabs( anglespeed ) < 0.00001f ) { + anglespeed = 0; + } + lastangle += anglespeed; + CG_DrawRotatedPic( basex + 4, basey + 4, basew - 8, baseh - 8, cgs.media.compass2Shader, lastangle ); + +// if( !(cgs.ccFilter & CC_FILTER_REQUESTS) ) { + // draw voice chats + for ( i = 0; i < MAX_CLIENTS; i++ ) { + centity_t *cent = &cg_entities[i]; + + if ( cg.predictedPlayerState.clientNum == i || !cgs.clientinfo[i].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[i].team ) { + continue; + } + + // also draw revive icons if cent is dead and player is a medic + if ( cent->voiceChatSpriteTime < cg.time ) { + continue; + } + + if ( cgs.clientinfo[i].health <= 0 ) { + // reset + cent->voiceChatSpriteTime = cg.time; + continue; + } + + CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cent->voiceChatSprite ); + } +// } + + /*if( !(cgs.ccFilter & CC_FILTER_DESTRUCTIONS) ) { + // draw explosives if an engineer + if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { + for ( i = 0; i < snap->numEntities; i++ ) { + centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; + + if ( cent->currentState.eType != ET_EXPLOSIVE_INDICATOR ) { + continue; + } + + if ( cent->currentState.teamNum == 1 && cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_AXIS ) + continue; + else if ( cent->currentState.teamNum == 2 && cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_ALLIES ) + continue; + + CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cgs.media.compassDestroyShader ); + } + } + }*/ + +// if( !(cgs.ccFilter & CC_FILTER_REQUESTS) ) { + // draw revive medic icons + if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_MEDIC ) { + for ( i = 0; i < snap->numEntities; i++ ) { + entityState_t *ent = &snap->entities[i]; + + if ( ent->eType != ET_PLAYER ) { + continue; + } + + if ( ( ent->eFlags & EF_DEAD ) && ent->number == ent->clientNum ) { + if ( !cgs.clientinfo[ent->clientNum].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[ent->clientNum].team ) { + continue; + } + + CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, ent->pos.trBase, cgs.media.medicReviveShader ); + } + } + } +// } + +/* if( !(cgs.ccFilter & CC_FILTER_DESTRUCTIONS) ) { + // draw constructibles if an engineer + if ( cg.predictedPlayerState.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { + for ( i = 0; i < snap->numEntities; i++ ) { + centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; + + if ( cent->currentState.eType != ET_CONSTRUCTIBLE_INDICATOR ) { + continue; + } + + if ( cent->currentState.teamNum != cg.predictedPlayerState.persistant[PERS_TEAM] && cent->currentState.teamNum != 3 ) + continue; + + CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->lerpOrigin, cgs.media.compassConstructShader ); + } + } + }*/ + +/* if( !(cgs.ccFilter & CC_FILTER_WAYPOINTS) ) { + // draw waypoint icons + for ( i = 0; i < snap->numEntities; i++ ) { + centity_t *cent = &cg_entities[ snap->entities[ i ].number ]; + + if( cent->currentState.eType != ET_WAYPOINT ) { + continue; + } + + // see if the waypoint owner is someone that you accept waypoints from + if( !CG_IsOnSameFireteam( cg.clientNum, cent->currentState.clientNum )) { // TODO: change to fireteam + continue; + } + + switch( cent->currentState.frame ) { + case WAYP_ATTACK: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassAttackShader ); break; + case WAYP_DEFEND: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassDefendShader ); break; + case WAYP_REGROUP: CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, cent->currentState.pos.trBase, cgs.media.waypointCompassRegroupShader ); break; + } + } + }*/ + +// if( !(cgs.ccFilter & CC_FILTER_BUDDIES) ) { + for ( i = 0; i < snap->numEntities; i++ ) { + entityState_t *ent = &snap->entities[i]; + + if ( ent->eType != ET_PLAYER ) { + continue; + } + + if ( ent->eFlags & EF_DEAD ) { + continue; + } + + if ( !cgs.clientinfo[ent->clientNum].infoValid || cg.predictedPlayerState.persistant[PERS_TEAM] != cgs.clientinfo[ent->clientNum].team ) { + continue; + } + + if ( !CG_IsOnSameFireteam( cg.clientNum, ent->clientNum ) ) { + continue; + } + + CG_DrawCompassIcon( basex, basey, basew, baseh, cg.predictedPlayerState.origin, ent->pos.trBase, cgs.media.buddyShader ); + } +// } +} + +static int CG_PlayerAmmoValue( int *ammo, int *clips, int *akimboammo ) { + centity_t *cent; + playerState_t *ps; + int weap; + qboolean skipammo = qfalse; + + *ammo = *clips = *akimboammo = -1; + + if ( cg.snap->ps.clientNum == cg.clientNum ) { + cent = &cg.predictedPlayerEntity; + } else { + cent = &cg_entities[cg.snap->ps.clientNum]; + } + ps = &cg.snap->ps; + + weap = cent->currentState.weapon; + + if ( !weap ) { + return weap; + } + + switch ( weap ) { // some weapons don't draw ammo count text + case WP_AMMO: + case WP_MEDKIT: + case WP_KNIFE: + case WP_PLIERS: + case WP_SMOKE_MARKER: + case WP_DYNAMITE: + case WP_SATCHEL: + case WP_SATCHEL_DET: + case WP_SMOKE_BOMB: + case WP_BINOCULARS: + return weap; + + case WP_LANDMINE: + case WP_MEDIC_SYRINGE: + case WP_MEDIC_ADRENALINE: + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + case WP_FLAMETHROWER: + case WP_MORTAR: + case WP_MORTAR_SET: + case WP_PANZERFAUST: + skipammo = qtrue; + break; + + default: + break; + } + + if ( cg.snap->ps.eFlags & EF_MG42_ACTIVE || cg.snap->ps.eFlags & EF_MOUNTEDTANK ) { + return WP_MOBILE_MG42; + } + + // total ammo in clips + *clips = cg.snap->ps.ammo[BG_FindAmmoForWeapon( weap )]; + + // current clip + *ammo = ps->ammoclip[BG_FindClipForWeapon( weap )]; + + if ( BG_IsAkimboWeapon( weap ) ) { + *akimboammo = ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weap ) )]; + } else { + *akimboammo = -1; + } + + if ( weap == WP_LANDMINE ) { + if ( !cgs.gameManager ) { + *ammo = 0; + } else { + if ( cgs.clientinfo[ps->clientNum].team == TEAM_AXIS ) { + *ammo = cgs.gameManager->currentState.otherEntityNum; + } else { + *ammo = cgs.gameManager->currentState.otherEntityNum2; + } + } + } else if ( weap == WP_MORTAR || weap == WP_MORTAR_SET || weap == WP_PANZERFAUST ) { + *ammo += *clips; + } + + if ( skipammo ) { + *clips = -1; + } + + return weap; +} + +#define HEAD_TURNTIME 10000 +#define HEAD_TURNANGLE 20 +#define HEAD_PITCHANGLE 2.5 +static void CG_DrawPlayerStatusHead( void ) { + hudHeadAnimNumber_t anim; + rectDef_t headRect = { 44, 480 - 92, 62, 80 }; +// rectDef_t headHintRect = { 40, 480 - 22, 20, 20 }; + bg_character_t* character = CG_CharacterForPlayerstate( &cg.snap->ps ); + bg_character_t* headcharacter = BG_GetCharacter( cgs.clientinfo[ cg.snap->ps.clientNum ].team, cgs.clientinfo[ cg.snap->ps.clientNum ].cls ); + + qhandle_t painshader = 0; + + anim = cg.idleAnim; + + if ( cg.weaponFireTime > 500 ) { + anim = HD_ATTACK; + } else if ( cg.time - cg.lastFiredWeaponTime < 500 ) { + anim = HD_ATTACK_END; + } else if ( cg.time - cg.painTime < ( character->hudheadanimations[ HD_PAIN ].numFrames * character->hudheadanimations[ HD_PAIN ].frameLerp ) ) { + anim = HD_PAIN; + } else if ( cg.time > cg.nextIdleTime ) { + cg.nextIdleTime = cg.time + 7000 + rand() % 1000; + if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { + cg.idleAnim = ( rand() % ( HD_DAMAGED_IDLE3 - HD_DAMAGED_IDLE2 + 1 ) ) + HD_DAMAGED_IDLE2; + } else { + cg.idleAnim = ( rand() % ( HD_IDLE8 - HD_IDLE2 + 1 ) ) + HD_IDLE2; + } + + cg.lastIdleTimeEnd = cg.time + character->hudheadanimations[ cg.idleAnim ].numFrames * character->hudheadanimations[ cg.idleAnim ].frameLerp; + } + + if ( cg.snap->ps.stats[ STAT_HEALTH ] < 5 ) { + painshader = cgs.media.hudDamagedStates[3]; + } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 20 ) { + painshader = cgs.media.hudDamagedStates[2]; + } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { + painshader = cgs.media.hudDamagedStates[1]; + } else if ( cg.snap->ps.stats[ STAT_HEALTH ] < 60 ) { + painshader = cgs.media.hudDamagedStates[0]; + } + + if ( cg.time > cg.lastIdleTimeEnd ) { + if ( cg.snap->ps.stats[ STAT_HEALTH ] < 40 ) { + cg.idleAnim = HD_DAMAGED_IDLE1; + } else { + cg.idleAnim = HD_IDLE1; + } + } + + + CG_DrawPlayerHead( &headRect, character, headcharacter, 180, 0, cg.snap->ps.eFlags & EF_HEADSHOT ? qfalse : qtrue, anim, painshader, cgs.clientinfo[ cg.snap->ps.clientNum ].rank, qfalse ); + +// CG_DrawKeyHint( &headHintRect, "openlimbomenu" ); +} + +static void CG_DrawPlayerHealthBar( rectDef_t *rect ) { + vec4_t bgcolour = { 1.f, 1.f, 1.f, 0.3f }; + vec4_t colour; + + int flags = 1 | 4 | 16 | 64; + float frac; + + CG_ColorForHealth( colour ); + colour[3] = 0.5f; + + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].cls == PC_MEDIC ) { + frac = cg.snap->ps.stats[STAT_HEALTH] / ( (float) cg.snap->ps.stats[STAT_MAX_HEALTH] * 1.12f ); + } else { + frac = cg.snap->ps.stats[STAT_HEALTH] / (float) cg.snap->ps.stats[STAT_MAX_HEALTH]; + } + + + CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, colour, NULL, bgcolour, frac, flags ); + + trap_R_SetColor( NULL ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); + CG_DrawPic( rect->x, rect->y + rect->h + 4, rect->w, rect->w, cgs.media.hudHealthIcon ); +} + +static void CG_DrawStaminaBar( rectDef_t *rect ) { + vec4_t bgcolour = { 1.f, 1.f, 1.f, 0.3f }; + vec4_t colour = { 0.1f, 1.0f, 0.1f, 0.5f }; + vec4_t colourlow = { 1.0f, 0.1f, 0.1f, 0.5f }; + vec_t* color = colour; + int flags = 1 | 4 | 16 | 64; + float frac = cg.pmext.sprintTime / (float)SPRINTTIME; + + if ( cg.snap->ps.powerups[PW_ADRENALINE] ) { + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + Vector4Average( colour, colorWhite, sin( cg.time * .005f ), colour ); + } else { + float msec = cg.snap->ps.powerups[PW_ADRENALINE] - cg.time; + + if ( msec < 0 ) { + msec = 0; + } else { + Vector4Average( colour, colorWhite, .5f + sin( .2f * sqrt( msec ) * 2 * M_PI ) * .5f, colour ); + } + } + } else { + if ( frac < 0.25 ) { + color = colourlow; + } + } + + CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, color, NULL, bgcolour, frac, flags ); + + trap_R_SetColor( NULL ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); + CG_DrawPic( rect->x, rect->y + rect->h + 4, rect->w, rect->w, cgs.media.hudSprintIcon ); +} + +static void CG_DrawWeapRecharge( rectDef_t *rect ) { + float barFrac, chargeTime; + int weap, flags; + qboolean fade = qfalse; + + vec4_t bgcolor = { 1.0f, 1.0f, 1.0f, 0.25f }; + vec4_t color; + + flags = 1 | 4 | 16; + + weap = cg.snap->ps.weapon; + +// if( !(cg.snap->ps.eFlags & EF_ZOOMING) ) { +// if ( weap != WP_PANZERFAUST && weap != WP_DYNAMITE && weap != WP_MEDKIT && weap != WP_SMOKE_GRENADE && weap != WP_PLIERS && weap != WP_AMMO ) { +// fade = qtrue; +// } +// } + + // Draw power bar + if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_ENGINEER ) { + chargeTime = cg.engineerChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_MEDIC ) { + chargeTime = cg.medicChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_FIELDOPS ) { + chargeTime = cg.ltChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } else if ( cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_COVERTOPS ) { + chargeTime = cg.covertopsChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } else { + chargeTime = cg.soldierChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } + + barFrac = (float)( cg.time - cg.snap->ps.classWeaponTime ) / chargeTime; + if ( barFrac > 1.0 ) { + barFrac = 1.0; + } + + color[0] = 1.0f; + color[1] = color[2] = barFrac; + color[3] = 0.25 + barFrac * 0.5; + + if ( fade ) { + bgcolor[3] *= 0.4f; + color[3] *= 0.4; + } + + CG_FilledBar( rect->x, rect->y + ( rect->h * 0.1f ), rect->w, rect->h * 0.84f, color, NULL, bgcolor, barFrac, flags ); + + trap_R_SetColor( NULL ); + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hudSprintBar ); + CG_DrawPic( rect->x + ( rect->w * 0.25f ) - 1, rect->y + rect->h + 4, ( rect->w * 0.5f ) + 2, rect->w + 2, cgs.media.hudPowerIcon ); +} + +static void CG_DrawPlayerStatus( void ) { + int value, value2, value3; + char buffer[32]; + int weap; + playerState_t *ps; + rectDef_t rect; +// vec4_t colorFaded = { 1.f, 1.f, 1.f, 0.3f }; + + ps = &cg.snap->ps; + + // Draw weapon icon and overheat bar + rect.x = 640 - 82; + rect.y = 480 - 56; + rect.w = 60; + rect.h = 32; + CG_DrawWeapHeat( &rect, HUD_HORIZONTAL ); + if ( cg.mvTotalClients < 1 && cg_drawWeaponIconFlash.integer == 0 ) { + CG_DrawPlayerWeaponIcon( &rect, qtrue, ITEM_ALIGN_RIGHT, &colorWhite ); + } else { + int ws = ( cg.mvTotalClients > 0 ) ? cgs.clientinfo[cg.snap->ps.clientNum].weaponState : BG_simpleWeaponState( cg.snap->ps.weaponstate ); + CG_DrawPlayerWeaponIcon( &rect, ( ws != WSTATE_IDLE ), ITEM_ALIGN_RIGHT, ( ( ws == WSTATE_SWITCH ) ? &colorWhite : ( ws == WSTATE_FIRE ) ? &colorRed : &colorYellow ) ); + } + + // Draw ammo + weap = CG_PlayerAmmoValue( &value, &value2, &value3 ); + if ( value3 >= 0 ) { + Com_sprintf( buffer, sizeof( buffer ), "%i|%i/%i", value3, value, value2 ); + CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); +// CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); + } else if ( value2 >= 0 ) { + Com_sprintf( buffer, sizeof( buffer ), "%i/%i", value, value2 ); + CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); +// CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); + } else if ( value >= 0 ) { + Com_sprintf( buffer, sizeof( buffer ), "%i", value ); + CG_Text_Paint_Ext( 640 - 22 - CG_Text_Width_Ext( buffer, .25f, 0, &cgs.media.limboFont1 ), 480 - 1 * ( 16 + 2 ) + 12 - 4, .25f, .25f, colorWhite, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); +// CG_DrawPic( 640 - 2 * ( 12 + 2 ) - 16 - 4, 480 - 1 * ( 16 + 2 ) - 4, 16, 16, cgs.media.SPPlayerInfoAmmoIcon ); + } + + +// == + rect.x = 24; + rect.y = 480 - 92; + rect.w = 12; + rect.h = 72; + CG_DrawPlayerHealthBar( &rect ); +// == + +// == + rect.x = 4; + rect.y = 480 - 92; + rect.w = 12; + rect.h = 72; + CG_DrawStaminaBar( &rect ); +// == + +// == + rect.x = 640 - 16; + rect.y = 480 - 92; + rect.w = 12; + rect.h = 72; + CG_DrawWeapRecharge( &rect ); +// == +} + +static void CG_DrawSkillBar( float x, float y, float w, float h, int skill ) { + int i; + float blockheight = ( h - 4 ) / (float)( NUM_SKILL_LEVELS - 1 ); + float draw_y; + vec4_t colour; + float x1, y1, w1, h1; + + draw_y = y + h - blockheight; + for ( i = 0; i < NUM_SKILL_LEVELS - 1; i++ ) { + if ( i >= skill ) { + Vector4Set( colour, 1.f, 1.f, 1.f, .15f ); + } else { + Vector4Set( colour, 0.f, 0.f, 0.f, .4f ); + } + + CG_FillRect( x, draw_y, w, blockheight, colour ); + + if ( i < skill ) { + x1 = x; + y1 = draw_y; + w1 = w; + h1 = blockheight; + CG_AdjustFrom640( &x1, &y1, &w1, &h1 ); + + trap_R_DrawStretchPic( x1, y1, w1, h1, 0, 0, 1.f, 0.5f, cgs.media.limboStar_roll ); + } + + CG_DrawRect_FixedBorder( x, draw_y, w, blockheight, 1, colorBlack ); +// CG_DrawPic( x, draw_y, w, blockheight, cgs.media.hudBorderVert2 ); + draw_y -= ( blockheight + 1 ); + } +} + +#define SKILL_ICON_SIZE 14 + +#define SKILLS_X 112 +#define SKILLS_Y 20 + +#define SKILL_BAR_OFFSET ( 2 * SKILL_BAR_X_INDENT ) +#define SKILL_BAR_X_INDENT 0 +#define SKILL_BAR_Y_INDENT 6 + +#define SKILL_BAR_WIDTH ( SKILL_ICON_SIZE - SKILL_BAR_OFFSET ) +#define SKILL_BAR_X ( SKILL_BAR_OFFSET + SKILL_BAR_X_INDENT + SKILLS_X ) +#define SKILL_BAR_X_SCALE ( SKILL_ICON_SIZE + 2 ) +#define SKILL_ICON_X ( SKILL_BAR_OFFSET + SKILLS_X ) +#define SKILL_ICON_X_SCALE ( SKILL_ICON_SIZE + 2 ) +#define SKILL_BAR_Y ( SKILL_BAR_Y_INDENT - SKILL_BAR_OFFSET - SKILLS_Y ) +#define SKILL_BAR_Y_SCALE ( SKILL_ICON_SIZE + 2 ) +#define SKILL_ICON_Y ( -( SKILL_ICON_SIZE + 2 ) - SKILL_BAR_OFFSET - SKILLS_Y ) + +skillType_t CG_ClassSkillForPosition( clientInfo_t* ci, int pos ) { + switch ( pos ) { + case 0: + return BG_ClassSkillForClass( ci->cls ); + case 1: + return SK_BATTLE_SENSE; + case 2: + return SK_LIGHT_WEAPONS; + } + + return SK_BATTLE_SENSE; +} + +static void CG_DrawPlayerStats( void ) { + int value = 0; + playerState_t *ps; + clientInfo_t *ci; + skillType_t skill; + int i; + const char* str; + float w; + vec_t* clr; + + str = va( "%i", cg.snap->ps.stats[STAT_HEALTH] ); + w = CG_Text_Width_Ext( str, 0.25f, 0, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( SKILLS_X - 28 - w, 480 - 4, 0.25f, 0.25f, colorWhite, str, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( SKILLS_X - 28 + 2, 480 - 4, 0.2f, 0.2f, colorWhite, "HP", 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); + + if ( cgs.gametype == GT_WOLF_LMS ) { + return; + } + + ps = &cg.snap->ps; + ci = &cgs.clientinfo[ ps->clientNum ]; + + + for ( i = 0; i < 3; i++ ) { + skill = CG_ClassSkillForPosition( ci, i ); + + CG_DrawSkillBar( i * SKILL_BAR_X_SCALE + SKILL_BAR_X, 480 - ( 5 * SKILL_BAR_Y_SCALE ) + SKILL_BAR_Y, SKILL_BAR_WIDTH, 4 * SKILL_ICON_SIZE, ci->skill[skill] ); + CG_DrawPic( i * SKILL_ICON_X_SCALE + SKILL_ICON_X, 480 + SKILL_ICON_Y, SKILL_ICON_SIZE, SKILL_ICON_SIZE, cgs.media.skillPics[skill] ); + } + + if ( cg.time - cg.xpChangeTime < 1000 ) { + clr = colorYellow; + } else { + clr = colorWhite; + } + + + str = va( "%i", cg.snap->ps.stats[STAT_XP] ); + w = CG_Text_Width_Ext( str, 0.25f, 0, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( SKILLS_X + 28 - w, 480 - 4, 0.25f, 0.25f, clr, str, 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( SKILLS_X + 28 + 2, 480 - 4, 0.2f, 0.2f, clr, "XP", 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont1 ); + + // draw treasure icon if we have the flag + // rain - #274 - use the playerstate instead of the clientinfo + if ( ps->powerups[PW_REDFLAG] || ps->powerups[PW_BLUEFLAG] ) { + trap_R_SetColor( NULL ); + CG_DrawPic( 640 - 40, 480 - 140 - value, 36, 36, cgs.media.objectiveShader ); + } else if ( ps->powerups[PW_OPS_DISGUISED] ) { // Disguised? + CG_DrawPic( 640 - 40, 480 - 140 - value, 36, 36, ps->persistant[PERS_TEAM] == TEAM_AXIS ? cgs.media.alliedUniformShader : cgs.media.axisUniformShader ); + } +} + +static char statsDebugStrings[6][512]; +static int statsDebugTime[6]; +static int statsDebugTextWidth[6]; +static int statsDebugPos; + +void CG_InitStatsDebug( void ) { + memset( &statsDebugStrings, 0, sizeof( statsDebugStrings ) ); + memset( &statsDebugTime, 0, sizeof( statsDebugTime ) ); + statsDebugPos = -1; +} + +void CG_StatsDebugAddText( const char *text ) { + if ( cg_debugSkills.integer ) { + statsDebugPos++; + + if ( statsDebugPos >= 6 ) { + statsDebugPos = 0; + } + + Q_strncpyz( statsDebugStrings[statsDebugPos], text, 512 ); + statsDebugTime[statsDebugPos] = cg.time; + statsDebugTextWidth[statsDebugPos] = CG_Text_Width_Ext( text, .15f, 0, &cgs.media.limboFont2 ); + + CG_Printf( "%s\n", text ); + } +} + +static void CG_DrawStatsDebug( void ) { + int textWidth = 0; + int i, x, y, w, h; + + if ( !cg_debugSkills.integer ) { + return; + } + + for ( i = 0; i < 6; i++ ) { + if ( statsDebugTime[i] + 9000 > cg.time ) { + if ( statsDebugTextWidth[i] > textWidth ) { + textWidth = statsDebugTextWidth[i]; + } + } + } + + w = textWidth + 6; + h = 9; + x = 640 - w; + y = ( 480 - 5 * ( 12 + 2 ) + 6 - 4 ) - 6 - h; // don't ask + + i = statsDebugPos; + + do { + vec4_t colour; + + if ( statsDebugTime[i] + 9000 <= cg.time ) { + break; + } + + colour[0] = colour[1] = colour[2] = .5f; + if ( cg.time - statsDebugTime[i] > 5000 ) { + colour[3] = .5f - .5f * ( ( cg.time - statsDebugTime[i] - 5000 ) / 4000.f ); + } else { + colour[3] = .5f ; + } + CG_FillRect( x, y, w, h, colour ); + + colour[0] = colour[1] = colour[2] = 1.f; + if ( cg.time - statsDebugTime[i] > 5000 ) { + colour[3] = 1.f - ( ( cg.time - statsDebugTime[i] - 5000 ) / 4000.f ); + } else { + colour[3] = 1.f ; + } + CG_Text_Paint_Ext( 640.f - 3 - statsDebugTextWidth[i], y + h - 2, .15f, .15f, colour, statsDebugStrings[i], 0, 0, ITEM_TEXTSTYLE_NORMAL, &cgs.media.limboFont2 ); + + y -= h; + + i--; + if ( i < 0 ) { + i = 6 - 1; + } + } while ( i != statsDebugPos ); +} + +//bani +void CG_DrawDemoRecording( void ) { + char status[1024]; + char demostatus[128]; + char wavestatus[128]; + + if ( !cl_demorecording.integer && !cl_waverecording.integer ) { + return; + } + + if ( !cg_recording_statusline.integer ) { + return; + } + + if ( cl_demorecording.integer ) { + Com_sprintf( demostatus, sizeof( demostatus ), " demo %s: %ik ", cl_demofilename.string, cl_demooffset.integer / 1024 ); + } else { + strncpy( demostatus, "", sizeof( demostatus ) ); + } + + if ( cl_waverecording.integer ) { + Com_sprintf( wavestatus, sizeof( demostatus ), " audio %s: %ik ", cl_wavefilename.string, cl_waveoffset.integer / 1024 ); + } else { + strncpy( wavestatus, "", sizeof( wavestatus ) ); + } + + Com_sprintf( status, sizeof( status ), "RECORDING%s%s", demostatus, wavestatus ); + + CG_Text_Paint_Ext( 5, cg_recording_statusline.integer, 0.2f, 0.2f, colorWhite, status, 0, 0, 0, &cgs.media.limboFont2 ); +} + +/* +================= +CG_Draw2D +================= +*/ +static void CG_Draw2D( void ) { + CG_ScreenFade(); + + // Arnout: no 2d when in esc menu + // FIXME: do allow for quickchat (bleh) + // Gordon: Removing for now +/* if( trap_Key_GetCatcher() & KEYCATCH_UI ) { + return; + }*/ + + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + CG_DrawIntermission(); + return; + } else { + if ( cgs.dbShowing ) { + CG_Debriefing_Shutdown(); + } + } + + if ( cg.editingSpeakers ) { + CG_SpeakerEditorDraw(); + return; + } + + //bani - #127 - no longer cheat protected, we draw crosshair/reticle in non demoplayback + if ( cg_draw2D.integer == 0 ) { + if ( cg.demoPlayback ) { + return; + } + CG_DrawCrosshair(); + CG_DrawFlashFade(); + return; + } + + if ( !cg.cameraMode ) { + CG_DrawFlashBlendBehindHUD(); + + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + CG_DrawSpectator(); + CG_DrawCrosshair(); + CG_DrawCrosshairNames(); + + // NERVE - SMF - we need to do this for spectators as well + CG_DrawTeamInfo(); + } else { + // don't draw any status if dead + if ( cg.snap->ps.stats[STAT_HEALTH] > 0 || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { + + CG_DrawCrosshair(); + + CG_DrawCrosshairNames(); + + CG_DrawNoShootIcon(); + +// CG_DrawPickupItem(); + } + + CG_DrawTeamInfo(); + + if ( cg_drawStatus.integer ) { + Menu_PaintAll(); + CG_DrawTimedMenus(); + } + } + + CG_DrawVote(); + + CG_DrawLagometer(); + } + + // don't draw center string if scoreboard is up + if ( !CG_DrawScoreboard() ) { + if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + rectDef_t rect; + + if ( cg.snap->ps.stats[STAT_HEALTH] > 0 ) { + CG_DrawPlayerStatusHead(); + CG_DrawPlayerStatus(); + CG_DrawPlayerStats(); + } + + CG_DrawLivesLeft(); + + // Cursor hint + rect.w = rect.h = 48; + rect.x = .5f * SCREEN_WIDTH - .5f * rect.w; + rect.y = 260; + CG_DrawCursorhint( &rect ); + + // Stability bar + rect.x = 50; + rect.y = 208; + rect.w = 10; + rect.h = 64; + CG_DrawWeapStability( &rect ); + + // Stats Debugging + CG_DrawStatsDebug(); + } + + if ( !cg_paused.integer ) { + CG_DrawUpperRight(); + } + + CG_DrawCenterString(); + CG_DrawPMItems(); + CG_DrawPMItemsBig(); + + CG_DrawFollow(); + CG_DrawWarmup(); + + CG_DrawNotify(); + + if ( cg_drawCompass.integer ) { + CG_DrawNewCompass(); + } + + CG_DrawObjectiveInfo(); + + CG_DrawSpectatorMessage(); + + CG_DrawLimboMessage(); + } else { + if ( cgs.eventHandling != CGAME_EVENT_NONE ) { +// qboolean old = cg.showGameView; + +// cg.showGameView = qfalse; + // draw cursor + trap_R_SetColor( NULL ); + CG_DrawPic( cgDC.cursorx - 14, cgDC.cursory - 14, 32, 32, cgs.media.cursorIcon ); +// cg.showGameView = old; + } + } + + if ( cg.showFireteamMenu ) { + CG_Fireteams_Draw(); + } + + // Info overlays + CG_DrawOverlays(); + + // OSP - window updates + CG_windowDraw(); + + // Ridah, draw flash blends now + CG_DrawFlashBlend(); + + CG_DrawDemoRecording(); +} + +// NERVE - SMF +void CG_StartShakeCamera( float p ) { + cg.cameraShakeScale = p; + + cg.cameraShakeLength = 1000 * ( p * p ); + cg.cameraShakeTime = cg.time + cg.cameraShakeLength; + cg.cameraShakePhase = crandom() * M_PI; // start chain in random dir +} + +void CG_ShakeCamera() { + float x, val; + + if ( cg.time > cg.cameraShakeTime ) { + cg.cameraShakeScale = 0; // JPW NERVE all pending explosions resolved, so reset shakescale + return; + } + + // JPW NERVE starts at 1, approaches 0 over time + x = ( cg.cameraShakeTime - cg.time ) / cg.cameraShakeLength; + + // ydnar: move the camera + #if 0 + // up/down + val = sin( M_PI * 8 * x + cg.cameraShakePhase ) * x * 18.0f * cg.cameraShakeScale; + cg.refdefViewAngles[0] += val; + + // left/right + val = sin( M_PI * 15 * x + cg.cameraShakePhase ) * x * 16.0f * cg.cameraShakeScale; + cg.refdefViewAngles[1] += val; + #else + // move + val = sin( M_PI * 7 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; + cg.refdef.vieworg[ 2 ] += val; + val = sin( M_PI * 13 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; + cg.refdef.vieworg[ 1 ] += val; + val = cos( M_PI * 17 * x + cg.cameraShakePhase ) * x * 4.0f * cg.cameraShakeScale; + cg.refdef.vieworg[ 0 ] += val; + #endif + + AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); +} +// -NERVE - SMF + +void CG_DrawMiscGamemodels( void ) { + int i, j; + refEntity_t ent; + int drawn = 0; + + memset( &ent, 0, sizeof( ent ) ); + + ent.reType = RT_MODEL; + ent.nonNormalizedAxes = qtrue; + + // ydnar: static gamemodels don't project shadows + ent.renderfx = RF_NOSHADOW; + + for ( i = 0; i < cg.numMiscGameModels; i++ ) { + if ( cgs.miscGameModels[i].radius ) { + if ( CG_CullPointAndRadius( cgs.miscGameModels[i].org, cgs.miscGameModels[i].radius ) ) { + continue; + } + } + + if ( !trap_R_inPVS( cg.refdef_current->vieworg, cgs.miscGameModels[i].org ) ) { + continue; + } + + VectorCopy( cgs.miscGameModels[i].org, ent.origin ); + VectorCopy( cgs.miscGameModels[i].org, ent.oldorigin ); + VectorCopy( cgs.miscGameModels[i].org, ent.lightingOrigin ); + +/* { + vec3_t v; + vec3_t vu = { 0.f, 0.f, 1.f }; + vec3_t vl = { 0.f, 1.f, 0.f }; + vec3_t vf = { 1.f, 0.f, 0.f }; + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, cgs.miscGameModels[i].radius, vu, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, cgs.miscGameModels[i].radius, vf, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, cgs.miscGameModels[i].radius, vl, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, -cgs.miscGameModels[i].radius, vu, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, -cgs.miscGameModels[i].radius, vf, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + + VectorCopy( cgs.miscGameModels[i].org, v ); + VectorMA( v, -cgs.miscGameModels[i].radius, vl, v ); + CG_RailTrail2( NULL, cgs.miscGameModels[i].org, v ); + }*/ + + for ( j = 0; j < 3; j++ ) { + VectorCopy( cgs.miscGameModels[i].axes[j], ent.axis[j] ); + } + ent.hModel = cgs.miscGameModels[i].model; + + trap_R_AddRefEntityToScene( &ent ); + + drawn++; + } +} + +/* +===================== +CG_DrawActive + +Perform all drawing needed to completely fill the screen +===================== +*/ +void CG_DrawActive( stereoFrame_t stereoView ) { + float separation; + vec3_t baseOrg; + + // optionally draw the info screen instead + if ( !cg.snap ) { + CG_DrawInformation( qfalse ); + return; + } + + // optionally draw the tournement scoreboard instead + /*if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && + ( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) { + CG_DrawTourneyScoreboard(); + return; + }*/ + + switch ( stereoView ) { + case STEREO_CENTER: + separation = 0; + break; + case STEREO_LEFT: + separation = -cg_stereoSeparation.value / 2; + break; + case STEREO_RIGHT: + separation = cg_stereoSeparation.value / 2; + break; + default: + separation = 0; + CG_Error( "CG_DrawActive: Undefined stereoView" ); + } + + + // clear around the rendered view if sized down + CG_TileClear(); + + // offset vieworg appropriately if we're doing stereo separation + VectorCopy( cg.refdef_current->vieworg, baseOrg ); + if ( separation != 0 ) { + VectorMA( cg.refdef_current->vieworg, -separation, cg.refdef_current->viewaxis[1], cg.refdef_current->vieworg ); + } + + cg.refdef_current->glfog.registered = 0; // make sure it doesn't use fog from another scene + + CG_ActivateLimboMenu(); + +// if( cgs.ccCurrentCamObjective == -1 ) { +// if( cg.showGameView ) { +// CG_FillRect( 0, 0, 640, 480, colorBlack ); +// CG_LimboPanel_Draw(); +// return; +// } +// } + + if ( cg.showGameView ) { + float x, y, w, h; + x = LIMBO_3D_X; + y = LIMBO_3D_Y; + w = LIMBO_3D_W; + h = LIMBO_3D_H; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + cg.refdef_current->x = x; + cg.refdef_current->y = y; + cg.refdef_current->width = w; + cg.refdef_current->height = h; + + CG_Letterbox( ( LIMBO_3D_W / 640.f ) * 100, ( LIMBO_3D_H / 480.f ) * 100, qfalse ); + } + + CG_ShakeCamera(); // NERVE - SMF + + // Gordon + CG_PB_RenderPolyBuffers(); + + // Gordon + CG_DrawMiscGamemodels(); + + if ( !( cg.limboEndCinematicTime > cg.time && cg.showGameView ) ) { + trap_R_RenderScene( cg.refdef_current ); + } + + // restore original viewpoint if running stereo + if ( separation != 0 ) { + VectorCopy( baseOrg, cg.refdef_current->vieworg ); + } + + if ( !cg.showGameView ) { + // draw status bar and other floating elements + CG_Draw2D(); + } else { + CG_LimboPanel_Draw(); + } +} diff --git a/src/cgame/cg_drawtools.c b/src/cgame/cg_drawtools.c new file mode 100644 index 0000000..8c7beb5 --- /dev/null +++ b/src/cgame/cg_drawtools.c @@ -0,0 +1,1357 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_drawtools.c -- helper functions called by cg_draw, cg_scoreboard, cg_info, etc +#include "cg_local.h" + +/* +================ +CG_AdjustFrom640 + +Adjusted for resolution and screen aspect ratio +================ +*/ +void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { +#if 0 + // adjust for wide screens + if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { + *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); + } +#endif + +/* if ( (cg.showGameView) && cg.refdef_current->width ) { + float xscale = ( ( cg.refdef_current->width / cgs.screenXScale ) / 640.f ); + float yscale = ( ( cg.refdef_current->height / cgs.screenYScale ) / 480.f ); + + (*x) = (*x) * xscale + ( cg.refdef_current->x / cgs.screenXScale ); + (*y) = (*y) * yscale + ( cg.refdef_current->y / cgs.screenYScale ); + (*w) *= xscale; + (*h) *= yscale; + }*/ + + // scale for screen sizes + *x *= cgs.screenXScale; + *y *= cgs.screenYScale; + *w *= cgs.screenXScale; + *h *= cgs.screenYScale; +} + +/* +================ +CG_FillRect + +Coordinates are 640*480 virtual values +================= +*/ +void CG_FillRect( float x, float y, float width, float height, const float *color ) { + trap_R_SetColor( color ); + + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 1, cgs.media.whiteShader ); + + trap_R_SetColor( NULL ); +} + +/* +============== +CG_FillRectGradient +============== +*/ +void CG_FillRectGradient( float x, float y, float width, float height, const float *color, const float *gradcolor, int gradientType ) { + trap_R_SetColor( color ); + + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPicGradient( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader, gradcolor, gradientType ); + + trap_R_SetColor( NULL ); +} + + +/* +============== +CG_HorizontalPercentBar + Generic routine for pretty much all status indicators that show a fractional + value to the palyer by virtue of how full a drawn box is. + +flags: + left - 1 + center - 2 // direction is 'right' by default and orientation is 'horizontal' + vert - 4 + nohudalpha - 8 // don't adjust bar's alpha value by the cg_hudalpha value + bg - 16 // background contrast box (bg set with bgColor of 'NULL' means use default bg color (1,1,1,0.25) + spacing - 32 // some bars use different sorts of spacing when drawing both an inner and outer box + + lerp color - 256 // use an average of the start and end colors to set the fill color +============== +*/ + + +// TODO: these flags will be shared, but it was easier to work on stuff if I wasn't changing header files a lot +#define BAR_LEFT 0x0001 +#define BAR_CENTER 0x0002 +#define BAR_VERT 0x0004 +#define BAR_NOHUDALPHA 0x0008 +#define BAR_BG 0x0010 +// different spacing modes for use w/ BAR_BG +#define BAR_BGSPACING_X0Y5 0x0020 +#define BAR_BGSPACING_X0Y0 0x0040 + +#define BAR_LERP_COLOR 0x0100 + +#define BAR_BORDERSIZE 2 + +void CG_FilledBar( float x, float y, float w, float h, float *startColor, float *endColor, const float *bgColor, float frac, int flags ) { + vec4_t backgroundcolor = {1, 1, 1, 0.25f}, colorAtPos; // colorAtPos is the lerped color if necessary + int indent = BAR_BORDERSIZE; + + if ( frac > 1 ) { + frac = 1.f; + } + if ( frac < 0 ) { + frac = 0; + } + + if ( ( flags & BAR_BG ) && bgColor ) { // BAR_BG set, and color specified, use specified bg color + Vector4Copy( bgColor, backgroundcolor ); + } + + if ( flags & BAR_LERP_COLOR ) { + Vector4Average( startColor, endColor, frac, colorAtPos ); + } + + // background + if ( ( flags & BAR_BG ) ) { + // draw background at full size and shrink the remaining box to fit inside with a border. (alternate border may be specified by a BAR_BGSPACING_xx) + CG_FillRect( x, + y, + w, + h, + backgroundcolor ); + + if ( flags & BAR_BGSPACING_X0Y0 ) { // fill the whole box (no border) + + } else if ( flags & BAR_BGSPACING_X0Y5 ) { // spacing created for weapon heat + indent *= 3; + y += indent; + h -= ( 2 * indent ); + + } else { // default spacing of 2 units on each side + x += indent; + y += indent; + w -= ( 2 * indent ); + h -= ( 2 * indent ); + } + } + + + // adjust for horiz/vertical and draw the fractional box + if ( flags & BAR_VERT ) { + if ( flags & BAR_LEFT ) { // TODO: remember to swap colors on the ends here + y += ( h * ( 1 - frac ) ); + } else if ( flags & BAR_CENTER ) { + y += ( h * ( 1 - frac ) / 2 ); + } + + if ( flags & BAR_LERP_COLOR ) { + CG_FillRect( x, y, w, h * frac, colorAtPos ); + } else { +// CG_FillRectGradient ( x, y, w, h * frac, startColor, endColor, 0 ); + CG_FillRect( x, y, w, h * frac, startColor ); + } + + } else { + + if ( flags & BAR_LEFT ) { // TODO: remember to swap colors on the ends here + x += ( w * ( 1 - frac ) ); + } else if ( flags & BAR_CENTER ) { + x += ( w * ( 1 - frac ) / 2 ); + } + + if ( flags & BAR_LERP_COLOR ) { + CG_FillRect( x, y, w * frac, h, colorAtPos ); + } else { +// CG_FillRectGradient ( x, y, w * frac, h, startColor, endColor, 0 ); + CG_FillRect( x, y, w * frac, h, startColor ); + } + } + +} + + +/* +================= +CG_HorizontalPercentBar +================= +*/ +void CG_HorizontalPercentBar( float x, float y, float width, float height, float percent ) { + vec4_t bgcolor = {0.5f, 0.5f, 0.5f, 0.3f}, + color = {1.0f, 1.0f, 1.0f, 0.3f}; + CG_FilledBar( x, y, width, height, color, NULL, bgcolor, percent, BAR_BG | BAR_NOHUDALPHA ); +} + + +/* +================ +CG_DrawSides + +Coords are virtual 640x480 +================ +*/ +void CG_DrawSides( float x, float y, float w, float h, float size ) { + CG_AdjustFrom640( &x, &y, &w, &h ); + size *= cgs.screenXScale; + trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); +} + +void CG_DrawTopBottom( float x, float y, float w, float h, float size ) { + CG_AdjustFrom640( &x, &y, &w, &h ); + size *= cgs.screenYScale; + trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); +} + +void CG_DrawSides_NoScale( float x, float y, float w, float h, float size ) { + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader ); +} + +void CG_DrawTopBottom_NoScale( float x, float y, float w, float h, float size ) { + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); + trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader ); +} + +/* +================ +UI_DrawRect + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) { + trap_R_SetColor( color ); + + CG_DrawTopBottom( x, y, width, height, size ); + CG_DrawSides( x, y, width, height, size ); + + trap_R_SetColor( NULL ); +} + +void CG_DrawRect_FixedBorder( float x, float y, float width, float height, int border, const float *color ) { + trap_R_SetColor( color ); + + CG_DrawTopBottom_NoScale( x, y, width, height, border ); + CG_DrawSides_NoScale( x, y, width, height, border ); + + trap_R_SetColor( NULL ); +} + + +/* +================ +CG_DrawPicST + +Allows passing of st co-ords + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawPicST( float x, float y, float width, float height, float s0, float t0, float s1, float t1, qhandle_t hShader ) { + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPic( x, y, width, height, s0, t0, s1, t1, hShader ); +} + +/* +================ +CG_DrawPic + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { + float s0; + float s1; + float t0; + float t1; + + if ( width < 0 ) { // flip about vertical + width = -width; + s0 = 1; + s1 = 0; + } else { + s0 = 0; + s1 = 1; + } + + if ( height < 0 ) { // flip about horizontal + height = -height; + t0 = 1; + t1 = 0; + } else { + t0 = 0; + t1 = 1; + } + + CG_AdjustFrom640( &x, &y, &width, &height ); + trap_R_DrawStretchPic( x, y, width, height, s0, t0, s1, t1, hShader ); +} + +// NERVE - SMF +/* +================ +CG_DrawRotatedPic + +Coordinates are 640*480 virtual values +================= +*/ +void CG_DrawRotatedPic( float x, float y, float width, float height, qhandle_t hShader, float angle ) { + + CG_AdjustFrom640( &x, &y, &width, &height ); + + trap_R_DrawRotatedPic( x, y, width, height, 0, 0, 1, 1, hShader, angle ); +} +// -NERVE - SMF + +/* +=============== +CG_DrawChar + +Coordinates and size in 640*480 virtual screen size +=============== +*/ +void CG_DrawChar( int x, int y, int width, int height, int ch ) { + int row, col; + float frow, fcol; + float size; + float ax, ay, aw, ah; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + ax = x; + ay = y; + aw = width; + ah = height; + CG_AdjustFrom640( &ax, &ay, &aw, &ah ); + + row = ch >> 4; + col = ch & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + trap_R_DrawStretchPic( ax, ay, aw, ah, + fcol, frow, + fcol + size, frow + size, + cgs.media.charsetShader ); +} + +/* +=============== +CG_DrawChar2 + +Coordinates and size in 640*480 virtual screen size +=============== +*/ +void CG_DrawChar2( int x, int y, int width, int height, int ch ) { + int row, col; + float frow, fcol; + float size; + float ax, ay, aw, ah; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + ax = x; + ay = y; + aw = width; + ah = height; + CG_AdjustFrom640( &ax, &ay, &aw, &ah ); + + row = ch >> 4; + col = ch & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + trap_R_DrawStretchPic( ax, ay, aw, ah, + fcol, frow, + fcol + size, frow + size, + cgs.media.menucharsetShader ); +} + +// JOSEPH 4-25-00 +/* +================== +CG_DrawStringExt + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { + vec4_t color; + const char *s; + int xx; + int cnt; + + if ( maxChars <= 0 ) { + maxChars = 32767; // do them all! + + } + // draw the drop shadow + if ( shadow ) { + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + trap_R_SetColor( color ); + s = string; + xx = x; + cnt = 0; + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + CG_DrawChar( xx + 1, y + 1, charWidth, charHeight, *s ); + cnt++; + xx += charWidth; + s++; + } + } + + // draw the colored text + s = string; + xx = x; + cnt = 0; + trap_R_SetColor( setColor ); + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( color, setColor, sizeof( color ) ); + } else { + memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) ); + color[3] = setColor[3]; + } + trap_R_SetColor( color ); + } + s += 2; + continue; + } + CG_DrawChar( xx, y, charWidth, charHeight, *s ); + xx += charWidth; + cnt++; + s++; + } + trap_R_SetColor( NULL ); +} + +/*================== +CG_DrawStringExt2 + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ + +//Gordon: Modified to have configurable drop shadow offset +void CG_DrawStringExt_Shadow( int x, int y, const char *string, const float *setColor, + qboolean forceColor, int shadow, int charWidth, int charHeight, int maxChars ) { + vec4_t color; + const char *s; + int xx; + int cnt; + + if ( maxChars <= 0 ) { + maxChars = 32767; // do them all! + + } + // draw the drop shadow + if ( shadow ) { + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + trap_R_SetColor( color ); + s = string; + xx = x; + cnt = 0; + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + CG_DrawChar2( xx + ( ( charWidth < 12 ) ? 1 : 2 ), y + ( ( charHeight < 12 ) ? 1 : 2 ), charWidth, charHeight, *s ); + cnt++; + xx += charWidth; + s++; + } + } + + // draw the colored text + s = string; + xx = x; + cnt = 0; + trap_R_SetColor( setColor ); + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( color, setColor, sizeof( color ) ); + } else { + memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) ); + color[3] = setColor[3]; + } + trap_R_SetColor( color ); + } + s += 2; + continue; + } + CG_DrawChar2( xx, y, charWidth, charHeight, *s ); + xx += charWidth; + cnt++; + s++; + } + trap_R_SetColor( NULL ); +} + +void CG_DrawStringExt2( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { + CG_DrawStringExt_Shadow( x, y, string, setColor, forceColor, shadow ? 2 : 0, charWidth, charHeight, maxChars ); +} + +/*================== +CG_DrawStringExt3 + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void CG_DrawStringExt3( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { + vec4_t color; + const char *s; + int xx; + int cnt; + + if ( maxChars <= 0 ) { + maxChars = 32767; // do them all! + + } + s = string; + xx = 0; + + while ( *s ) { + xx += charWidth; + s++; + } + + x -= xx; + + s = string; + xx = x; + + // draw the drop shadow + if ( shadow ) { + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + trap_R_SetColor( color ); + s = string; + xx = x; + cnt = 0; + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + CG_DrawChar2( xx + ( ( charWidth < 12 ) ? 1 : 2 ), y + ( ( charHeight < 12 ) ? 1 : 2 ), charWidth, charHeight, *s ); + cnt++; + xx += charWidth; + s++; + } + } + + // draw the colored text + s = string; + xx = x; + cnt = 0; + trap_R_SetColor( setColor ); + while ( *s && cnt < maxChars ) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( color, setColor, sizeof( color ) ); + } else { + memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) ); + color[3] = setColor[3]; + } + trap_R_SetColor( color ); + } + s += 2; + continue; + } + CG_DrawChar2( xx, y, charWidth, charHeight, *s ); + xx += charWidth; + cnt++; + s++; + } + trap_R_SetColor( NULL ); +} + +/* +================== +CG_DrawStringExt2 + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +/*void CG_DrawStringExt2( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ) { + vec4_t color; + const char *s; + int xx; + int cnt; + + if (maxChars <= 0) + maxChars = 32767; // do them all! + + // draw the drop shadow + if (shadow) { + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + trap_R_SetColor( color ); + s = string; + xx = x; + cnt = 0; + while ( *s && cnt < maxChars) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + CG_DrawChar2( xx + 2, y + 2, charWidth, charHeight, *s ); + cnt++; + xx += charWidth; + s++; + } + } + + // draw the colored text + s = string; + xx = x; + cnt = 0; + trap_R_SetColor( setColor ); + while ( *s && cnt < maxChars) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) ); + color[3] = setColor[3]; + trap_R_SetColor( color ); + } + s += 2; + continue; + } + CG_DrawChar2( xx, y, charWidth, charHeight, *s ); + xx += charWidth; + cnt++; + s++; + } + trap_R_SetColor( NULL ); +}*/ + +void CG_DrawBigString( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + //CG_DrawStringExt( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); + CG_DrawStringExt2( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} + +void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { + //CG_DrawStringExt( x, y, s, color, qtrue, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); + CG_DrawStringExt2( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} +// END JOSEPH + +// JOSEPH 4-25-00 +void CG_DrawBigString2( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + CG_DrawStringExt3( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} + +void CG_DrawBigStringColor2( int x, int y, const char *s, vec4_t color ) { + CG_DrawStringExt3( x, y, s, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0 ); +} +// END JOSEPH + +void CG_DrawSmallString( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + CG_DrawStringExt( x, y, s, color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); +} + +void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) { + CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); +} + +/* +================= +CG_DrawStrlen + +Returns character count, skiping color escape codes +================= +*/ +int CG_DrawStrlen( const char *str ) { + const char *s = str; + int count = 0; + + while ( *s ) { + if ( Q_IsColorString( s ) ) { + s += 2; + } else { + count++; + s++; + } + } + + return count; +} + +/* +============= +CG_TileClearBox + +This repeats a 64*64 tile graphic to fill the screen around a sized down +refresh window. +============= +*/ +static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) { + float s1, t1, s2, t2; + s1 = x / 64.0; + t1 = y / 64.0; + s2 = ( x + w ) / 64.0; + t2 = ( y + h ) / 64.0; + trap_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader ); +} + + + +/* +============== +CG_TileClear + +Clear around a sized down screen +============== +*/ +void CG_TileClear( void ) { + int top, bottom, left, right; + int w, h; + + w = cgs.glconfig.vidWidth; + h = cgs.glconfig.vidHeight; + + if ( cg.refdef.x == 0 && cg.refdef.y == 0 && + cg.refdef.width == w && cg.refdef.height == h ) { + return; // full screen rendering + } + + top = cg.refdef.y; + bottom = top + cg.refdef.height - 1; + left = cg.refdef.x; + right = left + cg.refdef.width - 1; + + // clear above view screen + CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader ); + + // clear below view screen + CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader ); + + // clear left of view screen + CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader ); + + // clear right of view screen + CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader ); +} + + + +/* +================ +CG_FadeColor +================ +*/ +float *CG_FadeColor( int startMsec, int totalMsec ) { + static vec4_t color; + int t; + + if ( startMsec == 0 ) { + return NULL; + } + + t = cg.time - startMsec; + + if ( t >= totalMsec ) { + return NULL; + } + + // fade out + if ( totalMsec - t < FADE_TIME ) { + color[3] = ( totalMsec - t ) * 1.0 / FADE_TIME; + } else { + color[3] = 1.0; + } + color[0] = color[1] = color[2] = 1.f; + + return color; +} + + +/* +================ +CG_TeamColor +================ +*/ +float *CG_TeamColor( int team ) { + static vec4_t red = {1, 0.2, 0.2, 1}; + static vec4_t blue = {0.2, 0.2, 1, 1}; + static vec4_t other = {1, 1, 1, 1}; + static vec4_t spectator = {0.7, 0.7, 0.7, 1}; + + switch ( team ) { + case TEAM_AXIS: + return red; + case TEAM_ALLIES: + return blue; + case TEAM_SPECTATOR: + return spectator; + default: + return other; + } +} + + +/* +================= +CG_GetColorForHealth +================= +*/ +void CG_GetColorForHealth( int health, vec4_t hcolor ) { + // calculate the total points of damage that can + // be sustained at the current health / armor level + if ( health <= 0 ) { + VectorClear( hcolor ); // black + hcolor[3] = 1; + return; + } + + // set the color based on health + hcolor[0] = 1.0; + hcolor[3] = 1.0; + if ( health >= 100 ) { + hcolor[2] = 1.0; + } else if ( health < 66 ) { + hcolor[2] = 0; + } else { + hcolor[2] = ( health - 66 ) / 33.0; + } + + if ( health > 60 ) { + hcolor[1] = 1.0; + } else if ( health < 30 ) { + hcolor[1] = 0; + } else { + hcolor[1] = ( health - 30 ) / 30.0; + } +} + +/* +================= +CG_ColorForHealth +================= +*/ +void CG_ColorForHealth( vec4_t hcolor ) { + int health; + + // calculate the total points of damage that can + // be sustained at the current health / armor level + health = cg.snap->ps.stats[STAT_HEALTH]; + if ( health <= 0 ) { + VectorClear( hcolor ); // black + hcolor[3] = 1; + return; + } + + // set the color based on health + hcolor[0] = 1.0; + hcolor[3] = 1.0; + if ( health >= 100 ) { + hcolor[2] = 1.0; + } else if ( health < 66 ) { + hcolor[2] = 0; + } else { + hcolor[2] = ( health - 66 ) / 33.0; + } + + if ( health > 60 ) { + hcolor[1] = 1.0; + } else if ( health < 30 ) { + hcolor[1] = 0; + } else { + hcolor[1] = ( health - 30 ) / 30.0; + } +} + + + + +/* +================= +UI_DrawProportionalString2 +================= +*/ +static int propMap[128][3] = { + {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + + {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, + + {0, 0, PROP_SPACE_WIDTH}, // SPACE + {11, 122, 7}, // ! + {154, 181, 14}, // " + {55, 122, 17}, // # + {79, 122, 18}, // $ + {101, 122, 23}, // % + {153, 122, 18}, // & + {9, 93, 7}, // ' + {207, 122, 8}, // ( + {230, 122, 9}, // ) + {177, 122, 18}, // * + {30, 152, 18}, // + + {85, 181, 7}, // , + {34, 93, 11}, // - + {110, 181, 6}, // . + {130, 152, 14}, // / + + {22, 64, 17}, // 0 + {41, 64, 12}, // 1 + {58, 64, 17}, // 2 + {78, 64, 18}, // 3 + {98, 64, 19}, // 4 + {120, 64, 18}, // 5 + {141, 64, 18}, // 6 + {204, 64, 16}, // 7 + {162, 64, 17}, // 8 + {182, 64, 18}, // 9 + {59, 181, 7}, // : + {35,181, 7}, // ; + {203, 152, 14}, // < + {56, 93, 14}, // = + {228, 152, 14}, // > + {177, 181, 18}, // ? + + {28, 122, 22}, // @ + {5, 4, 18}, // A + {27, 4, 18}, // B + {48, 4, 18}, // C + {69, 4, 17}, // D + {90, 4, 13}, // E + {106, 4, 13}, // F + {121, 4, 18}, // G + {143, 4, 17}, // H + {164, 4, 8}, // I + {175, 4, 16}, // J + {195, 4, 18}, // K + {216, 4, 12}, // L + {230, 4, 23}, // M + {6, 34, 18}, // N + {27, 34, 18}, // O + + {48, 34, 18}, // P + {68, 34, 18}, // Q + {90, 34, 17}, // R + {110, 34, 18}, // S + {130, 34, 14}, // T + {146, 34, 18}, // U + {166, 34, 19}, // V + {185, 34, 29}, // W + {215, 34, 18}, // X + {234, 34, 18}, // Y + {5, 64, 14}, // Z + {60, 152, 7}, // [ + {106, 151, 13}, // '\' + {83, 152, 7}, // ] + {128, 122, 17}, // ^ + {4, 152, 21}, // _ + + {134, 181, 5}, // ' + {5, 4, 18}, // A + {27, 4, 18}, // B + {48, 4, 18}, // C + {69, 4, 17}, // D + {90, 4, 13}, // E + {106, 4, 13}, // F + {121, 4, 18}, // G + {143, 4, 17}, // H + {164, 4, 8}, // I + {175, 4, 16}, // J + {195, 4, 18}, // K + {216, 4, 12}, // L + {230, 4, 23}, // M + {6, 34, 18}, // N + {27, 34, 18}, // O + + {48, 34, 18}, // P + {68, 34, 18}, // Q + {90, 34, 17}, // R + {110, 34, 18}, // S + {130, 34, 14}, // T + {146, 34, 18}, // U + {166, 34, 19}, // V + {185, 34, 29}, // W + {215, 34, 18}, // X + {234, 34, 18}, // Y + {5, 64, 14}, // Z + {153, 152, 13}, // { + {11, 181, 5}, // | + {180, 152, 13}, // } + {79, 93, 17}, // ~ + {0, 0, -1} // DEL +}; + +static int propMapB[26][3] = { + {11, 12, 33}, + {49, 12, 31}, + {85, 12, 31}, + {120, 12, 30}, + {156, 12, 21}, + {183, 12, 21}, + {207, 12, 32}, + + {13, 55, 30}, + {49, 55, 13}, + {66, 55, 29}, + {101, 55, 31}, + {135, 55, 21}, + {158, 55, 40}, + {204, 55, 32}, + + {12, 97, 31}, + {48, 97, 31}, + {82, 97, 30}, + {118, 97, 30}, + {153, 97, 30}, + {185, 97, 25}, + {213, 97, 30}, + + {11, 139, 32}, + {42, 139, 51}, + {93, 139, 32}, + {126, 139, 31}, + {158, 139, 25}, +}; + +#define PROPB_GAP_WIDTH 4 +#define PROPB_SPACE_WIDTH 12 +#define PROPB_HEIGHT 36 + +/* +================= +UI_DrawBannerString +================= +*/ +static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color ) { + const char* s; + unsigned char ch; + float ax; + float ay; + float aw; + float ah; + float frow; + float fcol; + float fwidth; + float fheight; + + // draw the colored text + trap_R_SetColor( color ); + + ax = x * cgs.screenXScale + cgs.screenXBias; + ay = y * cgs.screenYScale; + + s = str; + while ( *s ) + { + ch = *s & 127; + if ( ch == ' ' ) { + ax += ( (float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH ) * cgs.screenXScale; + } else if ( ch >= 'A' && ch <= 'Z' ) { + ch -= 'A'; + fcol = (float)propMapB[ch][0] / 256.0f; + frow = (float)propMapB[ch][1] / 256.0f; + fwidth = (float)propMapB[ch][2] / 256.0f; + fheight = (float)PROPB_HEIGHT / 256.0f; + aw = (float)propMapB[ch][2] * cgs.screenXScale; + ah = (float)PROPB_HEIGHT * cgs.screenYScale; + trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + fwidth, frow + fheight, cgs.media.charsetPropB ); + ax += ( aw + (float)PROPB_GAP_WIDTH * cgs.screenXScale ); + } + s++; + } + + trap_R_SetColor( NULL ); +} + +void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) { + const char * s; + int ch; + int width; + vec4_t drawcolor; + + // find the width of the drawn text + s = str; + width = 0; + while ( *s ) { + ch = *s; + if ( ch == ' ' ) { + width += PROPB_SPACE_WIDTH; + } else if ( ch >= 'A' && ch <= 'Z' ) { + width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH; + } + s++; + } + width -= PROPB_GAP_WIDTH; + + switch ( style & UI_FORMATMASK ) { + case UI_CENTER: + x -= width / 2; + break; + + case UI_RIGHT: + x -= width; + break; + + case UI_LEFT: + default: + break; + } + + if ( style & UI_DROPSHADOW ) { + drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; + drawcolor[3] = color[3]; + UI_DrawBannerString2( x + 2, y + 2, str, drawcolor ); + } + + UI_DrawBannerString2( x, y, str, color ); +} + + +int UI_ProportionalStringWidth( const char* str ) { + const char * s; + int ch; + int charWidth; + int width; + + s = str; + width = 0; + while ( *s ) { + ch = *s & 127; + charWidth = propMap[ch][2]; + if ( charWidth != -1 ) { + width += charWidth; + width += PROP_GAP_WIDTH; + } + s++; + } + + width -= PROP_GAP_WIDTH; + return width; +} + +static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset ) { + const char* s; + unsigned char ch; + float ax; + float ay; + float aw; + float ah; + float frow; + float fcol; + float fwidth; + float fheight; + + // draw the colored text + trap_R_SetColor( color ); + + ax = x * cgs.screenXScale + cgs.screenXBias; + ay = y * cgs.screenYScale; + + s = str; + while ( *s ) + { + ch = *s & 127; + if ( ch == ' ' ) { + aw = (float)PROP_SPACE_WIDTH * cgs.screenXScale * sizeScale; + } else if ( propMap[ch][2] != -1 ) { + fcol = (float)propMap[ch][0] / 256.0f; + frow = (float)propMap[ch][1] / 256.0f; + fwidth = (float)propMap[ch][2] / 256.0f; + fheight = (float)PROP_HEIGHT / 256.0f; + aw = (float)propMap[ch][2] * cgs.screenXScale * sizeScale; + ah = (float)PROP_HEIGHT * cgs.screenYScale * sizeScale; + trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + fwidth, frow + fheight, charset ); + } else { + aw = 0; + } + + ax += ( aw + (float)PROP_GAP_WIDTH * cgs.screenXScale * sizeScale ); + s++; + } + + trap_R_SetColor( NULL ); +} + +/* +================= +UI_ProportionalSizeScale +================= +*/ +float UI_ProportionalSizeScale( int style ) { + if ( style & UI_SMALLFONT ) { + return 0.75; + } + if ( style & UI_EXSMALLFONT ) { + return 0.4; + } + + return 1.00; +} + + +/* +================= +UI_DrawProportionalString +================= +*/ +void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) { + vec4_t drawcolor; + int width; + float sizeScale; + + sizeScale = UI_ProportionalSizeScale( style ); + + switch ( style & UI_FORMATMASK ) { + case UI_CENTER: + width = UI_ProportionalStringWidth( str ) * sizeScale; + x -= width / 2; + break; + + case UI_RIGHT: + width = UI_ProportionalStringWidth( str ) * sizeScale; + x -= width; + break; + + case UI_LEFT: + default: + break; + } + + if ( style & UI_DROPSHADOW ) { + drawcolor[0] = drawcolor[1] = drawcolor[2] = 0; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x + 2, y + 2, str, drawcolor, sizeScale, cgs.media.charsetProp ); + } + + if ( style & UI_INVERSE ) { + drawcolor[0] = color[0] * 0.8; + drawcolor[1] = color[1] * 0.8; + drawcolor[2] = color[2] * 0.8; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetProp ); + return; + } + + // JOSEPH 12-29-99 + if ( style & UI_PULSE ) { + //drawcolor[0] = color[0] * 0.8; + //drawcolor[1] = color[1] * 0.8; + //drawcolor[2] = color[2] * 0.8; + drawcolor[3] = color[3]; + UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); + + drawcolor[0] = color[0]; + drawcolor[1] = color[1]; + drawcolor[2] = color[2]; + drawcolor[3] = 0.5 + 0.5 * sin( cg.time / PULSE_DIVISOR ); + UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, cgs.media.charsetPropGlow ); + return; + } + // END JOSEPH + + UI_DrawProportionalString2( x, y, str, color, sizeScale, cgs.media.charsetProp ); +} + +#define MAX_VA_STRING 32000 + +char* CG_TranslateString( const char *string ) { + static char staticbuf[2][MAX_VA_STRING]; + static int bufcount = 0; + char *buf; + + // some code expects this to return a copy always, even + // if none is needed for translation, so always supply + // another buffer + + buf = staticbuf[bufcount++ % 2]; + + trap_TranslateString( string, buf ); + + return buf; +} diff --git a/src/cgame/cg_effects.c b/src/cgame/cg_effects.c new file mode 100644 index 0000000..f2f365a --- /dev/null +++ b/src/cgame/cg_effects.c @@ -0,0 +1,1671 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_effects.c -- these functions generate localentities, usually as a result +// of event processing + +#include "cg_local.h" + + +/* +================== +CG_BubbleTrail + +Bullets shot underwater +================== +*/ +void CG_BubbleTrail( vec3_t start, vec3_t end, float size, float spacing ) { + vec3_t move; + vec3_t vec; + float len; + int i; + + VectorCopy( start, move ); + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + + // advance a random amount first + i = rand() % (int)spacing; + VectorMA( move, i, vec, move ); + + VectorScale( vec, spacing, vec ); + + for ( ; i < len; i += spacing ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leFlags = LEF_PUFF_DONT_SCALE; + le->leType = LE_MOVE_SCALE_FADE; + le->startTime = cg.time; + le->endTime = cg.time + 1000 + random() * 250; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + re = &le->refEntity; + re->shaderTime = cg.time / 1000.0f; + + re->reType = RT_SPRITE; + re->rotation = 0; +// re->radius = 3; + re->radius = size; // (SA) + re->customShader = cgs.media.waterBubbleShader; + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + re->shaderRGBA[3] = 0xff; + + le->color[3] = 1.0; + + le->pos.trType = TR_LINEAR; + le->pos.trTime = cg.time; + VectorCopy( move, le->pos.trBase ); + le->pos.trDelta[0] = crandom() * 3; + le->pos.trDelta[1] = crandom() * 3; +// le->pos.trDelta[2] = crandom()*5 + 6; + le->pos.trDelta[2] = crandom() * 5 + 20; // (SA) + + VectorAdd( move, vec, move ); + } +} + +/* +===================== +CG_SmokePuff + +Adds a smoke puff or blood trail localEntity. + +(SA) boy, it would be nice to have an acceleration vector for this as well. + big velocity vector with a negative acceleration for deceleration, etc. + (breath could then come out of a guys mouth at the rate he's walking/running and it + would slow down once it's created) +===================== +*/ + +//----(SA) modified +localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, + float radius, + float r, float g, float b, float a, + float duration, + int startTime, + int fadeInTime, + int leFlags, + qhandle_t hShader ) { + static int seed = 0x92; + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leFlags = leFlags; + le->radius = radius; + + re = &le->refEntity; + re->rotation = Q_random( &seed ) * 360; + re->radius = radius; + re->shaderTime = startTime / 1000.0f; + + le->leType = LE_MOVE_SCALE_FADE; + le->startTime = startTime; + le->endTime = startTime + duration; + le->fadeInTime = fadeInTime; + if ( fadeInTime > startTime ) { + le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); + } else { + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + } + le->color[0] = r; + le->color[1] = g; + le->color[2] = b; + le->color[3] = a; + + + le->pos.trType = TR_LINEAR; + le->pos.trTime = startTime; + VectorCopy( vel, le->pos.trDelta ); + VectorCopy( p, le->pos.trBase ); + + VectorCopy( p, re->origin ); + re->customShader = hShader; + + // rage pro can't alpha fade, so use a different shader + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + re->customShader = cgs.media.smokePuffRageProShader; + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + re->shaderRGBA[3] = 0xff; + } else { + re->shaderRGBA[0] = le->color[0] * 0xff; + re->shaderRGBA[1] = le->color[1] * 0xff; + re->shaderRGBA[2] = le->color[2] * 0xff; + re->shaderRGBA[3] = 0xff; + } +// JPW NERVE + if ( cg_fxflags & 1 ) { + re->customShader = getTestShader(); + re->rotation = 180; + } +// jpw + + re->reType = RT_SPRITE; + re->radius = le->radius; + + return le; +} + +/* +================== +CG_SpawnEffect + +Player teleporting in or out +================== +*/ +void CG_SpawnEffect( vec3_t org ) { + localEntity_t *le; + refEntity_t *re; + + return; // (SA) don't play spawn in effect right now + + le = CG_AllocLocalEntity(); + le->leFlags = 0; + le->leType = LE_FADE_RGB; + le->startTime = cg.time; + le->endTime = cg.time + 500; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; + + re = &le->refEntity; + + re->reType = RT_MODEL; + re->shaderTime = cg.time / 1000.0f; + + re->customShader = cgs.media.teleportEffectShader; + re->hModel = cgs.media.teleportEffectModel; + AxisClear( re->axis ); + + VectorCopy( org, re->origin ); + re->origin[2] -= 24; +} + +qhandle_t getTestShader( void ) { + switch ( rand() % 2 ) { + case 0: + return cgs.media.nerveTestShader; + break; + case 1: + default: + return cgs.media.idTestShader; + break; + } +} + +/* +==================== +CG_MakeExplosion +==================== +*/ +localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, + qhandle_t hModel, qhandle_t shader, + int msec, qboolean isSprite ) { + float ang; + localEntity_t *ex; + int offset; + vec3_t tmpVec, newOrigin; + + if ( msec <= 0 ) { + CG_Error( "CG_MakeExplosion: msec = %i", msec ); + } + + // skew the time a bit so they aren't all in sync + offset = rand() & 63; + + ex = CG_AllocLocalEntity(); + if ( isSprite ) { + ex->leType = LE_SPRITE_EXPLOSION; + + // randomly rotate sprite orientation + ex->refEntity.rotation = rand() % 360; + VectorScale( dir, 16, tmpVec ); + VectorAdd( tmpVec, origin, newOrigin ); + } else { + ex->leType = LE_EXPLOSION; + VectorCopy( origin, newOrigin ); + + // set axis with random rotate + if ( !dir ) { + AxisClear( ex->refEntity.axis ); + } else { + ang = rand() % 360; + VectorCopy( dir, ex->refEntity.axis[0] ); + RotateAroundDirection( ex->refEntity.axis, ang ); + } + } + + ex->startTime = cg.time - offset; + ex->endTime = ex->startTime + msec; + + // bias the time so all shader effects start correctly + ex->refEntity.shaderTime = ex->startTime / 1000.0f; + + ex->refEntity.hModel = hModel; + ex->refEntity.customShader = shader; + + // set origin + VectorCopy( newOrigin, ex->refEntity.origin ); + VectorCopy( newOrigin, ex->refEntity.oldorigin ); + + // Ridah, move away from the wall as the sprite expands + ex->pos.trType = TR_LINEAR; + ex->pos.trTime = cg.time; + VectorCopy( newOrigin, ex->pos.trBase ); + VectorScale( dir, 48, ex->pos.trDelta ); + // done. + + ex->color[0] = ex->color[1] = ex->color[2] = 1.0; + + return ex; +} + +/* +================= +CG_AddBloodTrails +================= +*/ +void CG_AddBloodTrails( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity; + int i; + + for ( i = 0; i < count; i++ ) { + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + VectorSet( velocity, dir[0] + crandom() * randScale, dir[1] + crandom() * randScale, dir[2] + crandom() * randScale ); + VectorScale( velocity, (float)speed, velocity ); + + le->leType = LE_BLOOD; + le->startTime = cg.time; + le->endTime = le->startTime + duration; // DHM - Nerve :: (removed) - (int)(0.5 * random() * duration); + le->lastTrailTime = cg.time; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->pos.trType = TR_GRAVITY_LOW; + VectorCopy( origin, le->pos.trBase ); + VectorMA( le->pos.trBase, 2 + random() * 4, dir, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + le->bounceFactor = 0.9; + } +} + +/* +================= +CG_Bleed + +This is the spurt of blood when a character gets hit +================= +*/ +void CG_Bleed( vec3_t origin, int entityNum ) { +#define BLOOD_SPURT_COUNT 4 + int i,j; + centity_t *cent; + + if ( !cg_blood.integer ) { + return; + } + +#ifdef SAVEGAME_SUPPORT + if ( cg_reloading.integer ) { + // to dangerous, since we call playerangles() in here, which calls the animation system, which might not be setup yet + return; + } +#endif // SAVEGAME_SUPPORT + + cent = &cg_entities[entityNum]; + + // Ridah, blood spurts + if ( entityNum != cg.snap->ps.clientNum ) { + vec3_t vhead, vbody, bOrigin, dir, vec, pvec, ndir; + + CG_GetBleedOrigin( vhead, vbody, entityNum ); + + // project the impact point onto the vector defined by torso -> head + ProjectPointOntoVector( origin, vbody, vhead, bOrigin ); + + // if it's below the waste, or above the head, clamp + VectorSubtract( vhead, vbody, vec ); + VectorSubtract( bOrigin, vbody, pvec ); + if ( DotProduct( pvec, vec ) < 0 ) { + VectorCopy( vbody, bOrigin ); + } else { + VectorSubtract( bOrigin, vhead, pvec ); + if ( DotProduct( pvec, vec ) > 0 ) { + VectorCopy( vhead, bOrigin ); + } + } + + // spawn some blood trails, heading out towards the impact point + VectorSubtract( origin, bOrigin, dir ); + VectorNormalize( dir ); + + { + float len; + vec3_t vec; + + VectorSubtract( bOrigin, vhead, vec ); + len = VectorLength( vec ); + + if ( len > 8 ) { + VectorMA( bOrigin, 8, dir, bOrigin ); + } + } + + // DHM - Nerve :: Made minor adjustments + for ( i = 0; i < BLOOD_SPURT_COUNT; i++ ) { + VectorCopy( dir, ndir ); + for ( j = 0; j < 3; j++ ) + ndir[j] += crandom() * 0.3; + VectorNormalize( ndir ); + CG_AddBloodTrails( bOrigin, ndir, + 100, // speed + 450 + (int)( crandom() * 50 ), // duration + 2 + rand() % 2, // count + 0.1 ); // rand scale + } + } +} + +/* +================== +CG_LaunchGib +================== +*/ +void CG_LaunchGib( centity_t *cent, vec3_t origin, vec3_t angles, vec3_t velocity, qhandle_t hModel, float sizeScale, int breakCount ) { + localEntity_t *le; + refEntity_t *re; + int i; + + if ( !cg_blood.integer ) { + return; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + // le->endTime = le->startTime + 60000 + random() * 60000; + le->endTime = le->startTime + 20000 + ( crandom() * 5000 ); + le->breakCount = breakCount; + le->sizeScale = sizeScale; + + VectorCopy( angles, le->angles.trBase ); + VectorCopy( origin, re->origin ); + AnglesToAxis( angles, re->axis ); + if ( sizeScale != 1.0 ) { + for ( i = 0; i < 3; i++ ) VectorScale( re->axis[i], sizeScale, re->axis[i] ); + } + re->hModel = hModel; + + // re->fadeStartTime = le->endTime - 3000; + re->fadeStartTime = le->endTime - 1000; + re->fadeEndTime = le->endTime; + + le->leBounceSoundType = LEBS_BLOOD; + le->leMarkType = LEMT_BLOOD; + le->pos.trType = TR_GRAVITY; + + le->angles.trDelta[0] = ( 10 + ( rand() & 50 ) ) - 30; +// le->angles.trDelta[0] = (100 + (rand()&500)) - 300; // pitch + le->angles.trDelta[1] = ( 100 + ( rand() & 500 ) ) - 300; // (SA) this is the safe one right now (yaw) turn the others up when I have tumbling things landing properly + le->angles.trDelta[2] = ( 10 + ( rand() & 50 ) ) - 30; +// le->angles.trDelta[2] = (100 + (rand()&500)) - 300; // roll + + le->bounceFactor = 0.3; + + VectorCopy( origin, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + + le->angles.trType = TR_LINEAR; + + le->angles.trTime = cg.time; + + le->ownerNum = cent->currentState.number; + + // Ridah, if the player is on fire, then spawn some flaming gibs + if ( cent && CG_EntOnFire( cent ) ) { + le->onFireStart = cent->currentState.onFireStart; + le->onFireEnd = re->fadeEndTime + 1000; + } +} + +//#define GIB_VELOCITY 250 +//#define GIB_JUMP 250 + +#define GIB_VELOCITY 75 +#define GIB_JUMP 250 + + +/* +============== +CG_LoseHat +============== +*/ +void CG_LoseHat( centity_t *cent, vec3_t dir ) { + clientInfo_t *ci; + int clientNum; +// int i, count, tagIndex, gibIndex; + int tagIndex; + vec3_t origin, velocity; + bg_character_t *character; + + clientNum = cent->currentState.clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + CG_Error( "Bad clientNum on player entity" ); + } + ci = &cgs.clientinfo[ clientNum ]; + character = CG_CharacterForClientinfo( ci, cent ); + + // don't launch anything if they don't have one + if ( !character->accModels[ACC_HAT] ) { + return; + } + + tagIndex = CG_GetOriginForTag( cent, ¢->pe.headRefEnt, "tag_mouth", 0, origin, NULL ); + + velocity[0] = dir[0] * ( 0.75 + random() ) * GIB_VELOCITY; + velocity[1] = dir[1] * ( 0.75 + random() ) * GIB_VELOCITY; + velocity[2] = GIB_JUMP - 50 + dir[2] * ( 0.5 + random() ) * GIB_VELOCITY; + + { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 20000 + ( crandom() * 5000 ); + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + re->hModel = character->accModels[ACC_HAT]; + re->customSkin = character->accSkins[ACC_HAT]; + + re->fadeStartTime = le->endTime - 1000; + re->fadeEndTime = le->endTime; + + // (SA) FIXME: origin of hat md3 is offset from center. need to center the origin when you toss it + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + // spin it a bit + le->angles.trType = TR_LINEAR; + VectorCopy( tv( 0, 0, 0 ), le->angles.trBase ); + le->angles.trDelta[0] = 0; + le->angles.trDelta[1] = ( 100 + ( rand() & 500 ) ) - 300; +// le->angles.trDelta[2] = 0; + le->angles.trDelta[2] = 400; // (SA) this is set with a very particular value to try to get it + // to flip exactly once before landing (based on player alive + // (standing) and on level ground) and will be unnecessary when + // I have things landing properly on their own + + le->angles.trTime = cg.time; + + le->bounceFactor = 0.2; + + // Ridah, if the player is on fire, then make the hat on fire + if ( cent && CG_EntOnFire( cent ) ) { + le->onFireStart = cent->currentState.onFireStart; + le->onFireEnd = cent->currentState.onFireEnd + 4000; + } + } +} + +/* +====================== +CG_GetOriginForTag + + places the position of the tag into "org" + + returns the index of the tag it used, so we can cycle through tag's with the same name +====================== +*/ +int CG_GetOriginForTag( centity_t *cent, refEntity_t *parent, char *tagName, int startIndex, vec3_t org, vec3_t axis[3] ) { + int i; + orientation_t lerped; + int retval; + + // lerp the tag + retval = trap_R_LerpTag( &lerped, parent, tagName, startIndex ); + + if ( retval < 0 ) { + return retval; + } + + VectorCopy( parent->origin, org ); + + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( org, lerped.origin[i], parent->axis[i], org ); + } + + if ( axis ) { + // had to cast away the const to avoid compiler problems... + MatrixMultiply( lerped.axis, ( (refEntity_t *)parent )->axis, axis ); + } + + return retval; +} + +/* +=================== +CG_GibPlayer + +Generated a bunch of gibs launching out from the bodies location +=================== +*/ +#define MAXJUNCTIONS 8 + +void CG_GibPlayer( centity_t *cent, vec3_t playerOrigin, vec3_t gdir ) { + int i, count = 0, tagIndex, gibIndex; + vec3_t origin, velocity, dir; + trace_t trace; + qboolean foundtag; + clientInfo_t *ci; + int clientNum; + bg_character_t *character; + vec4_t projection, color; + + // Rafael + // BloodCloud + qboolean newjunction[MAXJUNCTIONS]; + vec3_t junctionOrigin[MAXJUNCTIONS]; + int junction; + int j; + vec3_t axis[3], angles; + + char *JunctiongibTags[] = { + // leg tag + "tag_footright", + "tag_footleft", + "tag_legright", + "tag_legleft", + + // torsotags + "tag_armright", + "tag_armleft", + + "tag_torso", + "tag_chest" + }; + + char *ConnectTags[] = { + // legs tags + "tag_legright", + "tag_legleft", + "tag_torso", + "tag_torso", + + // torso tags + "tag_chest", + "tag_chest", + + "tag_chest", + "tag_torso", + }; + + char *gibTags[] = { + // tags in the legs + "tag_footright", + "tag_footleft", + "tag_legright", + "tag_legleft", + "tag_torso", + + // tags in the torso + "tag_chest", + "tag_armright", + "tag_armleft", + "tag_head", + NULL + }; + + if ( cg_blood.integer ) { + // Rafael + for ( i = 0; i < MAXJUNCTIONS; i++ ) + newjunction[i] = qfalse; + + clientNum = cent->currentState.clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + CG_Error( "Bad clientNum on player entity" ); + } + ci = &cgs.clientinfo[ clientNum ]; + character = CG_CharacterForClientinfo( ci, cent ); + + // Ridah, fetch the various positions of the tag_gib*'s + // and spawn the gibs from the correct places (especially the head) + for ( gibIndex = 0, count = 0, foundtag = qtrue; foundtag && gibIndex < MAX_GIB_MODELS && gibTags[gibIndex]; gibIndex++ ) { + + refEntity_t *re = 0; + + foundtag = qfalse; + + if ( !character->gibModels[gibIndex] ) { + continue; + } + + re = ¢->pe.bodyRefEnt; + + for ( tagIndex = 0; ( tagIndex = CG_GetOriginForTag( cent, re, gibTags[gibIndex], tagIndex, origin, axis ) ) >= 0; count++, tagIndex++ ) { + + foundtag = qtrue; + + VectorSubtract( origin, re->origin, dir ); + VectorNormalize( dir ); + + // spawn a gib + velocity[0] = dir[0] * ( 0.5 + random() ) * GIB_VELOCITY * 0.3; + velocity[1] = dir[1] * ( 0.5 + random() ) * GIB_VELOCITY * 0.3; + velocity[2] = GIB_JUMP + dir[2] * ( 0.5 + random() ) * GIB_VELOCITY * 0.5; + + VectorMA( velocity, GIB_VELOCITY, gdir, velocity ); + AxisToAngles( axis, angles ); + + CG_LaunchGib( cent, origin, angles, velocity, character->gibModels[gibIndex], 1.0, 0 ); + + for ( junction = 0; junction < MAXJUNCTIONS; junction++ ) { + if ( !Q_stricmp( gibTags[gibIndex], JunctiongibTags[junction] ) ) { + VectorCopy( origin, junctionOrigin[junction] ); + newjunction[junction] = qtrue; + } + } + } + } + + for ( i = 0; i < MAXJUNCTIONS; i++ ) { + if ( newjunction[i] == qtrue ) { + for ( j = 0; j < MAXJUNCTIONS; j++ ) { + if ( !Q_stricmp( JunctiongibTags[j], ConnectTags[i] ) ) { + if ( newjunction[j] == qtrue ) { + // spawn a blood cloud somewhere on the vec from + VectorSubtract( junctionOrigin[i], junctionOrigin[j], dir ); + CG_ParticleBloodCloud( cent, junctionOrigin[i], dir ); + } + } + } + } + } + + // Ridah, spawn a bunch of blood dots around the place + #define GIB_BLOOD_DOTS 3 + for ( i = 0, count = 0; i < GIB_BLOOD_DOTS * 2; i++ ) { + // TTimo: unused + //static vec3_t mins = {-10,-10,-10}; + //static vec3_t maxs = { 10, 10, 10}; + + if ( i > 0 ) { + velocity[0] = ( ( i % 2 ) * 2 - 1 ) * ( 40 + 40 * random() ); + velocity[1] = ( ( ( i / 2 ) % 2 ) * 2 - 1 ) * ( 40 + 40 * random() ); + velocity[2] = ( ( ( i < GIB_BLOOD_DOTS ) * 2 ) - 1 ) * 40; + } else { + VectorClear( velocity ); + velocity[2] = -64; + } + + VectorAdd( playerOrigin, velocity, origin ); + + CG_Trace( &trace, playerOrigin, NULL, NULL, origin, -1, CONTENTS_SOLID ); + if ( trace.fraction < 1.0 ) { + //% BG_GetMarkDir( velocity, trace.plane.normal, velocity ); + //% CG_ImpactMark( cgs.media.bloodDotShaders[rand()%5], trace.endpos, velocity, random()*360, + //% 1,1,1,1, qtrue, 30, qfalse, cg_bloodTime.integer * 1000 ); + #if 0 + BG_GetMarkDir( velocity, trace.plane.normal, projection ); + VectorSubtract( vec3_origin, projection, projection ); + projection[ 3 ] = 64; + VectorMA( trace.endpos, -8.0f, projection, markOrigin ); + CG_ImpactMark( cgs.media.bloodDotShaders[ rand() % 5 ], markOrigin, projection, 30.0f, random() * 360.0f, 1.0f, 1.0f, 1.0f, 1.0f, cg_bloodTime.integer * 1000 ); + #else + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = 30.0f; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( cgs.media.bloodDotShaders[ rand() % 5 ], 1, (vec3_t*) trace.endpos, projection, color, + cg_bloodTime.integer * 1000, ( cg_bloodTime.integer * 1000 ) >> 4 ); + #endif + + if ( count++ > GIB_BLOOD_DOTS ) { + break; + } + } + } + } + + if ( !( cent->currentState.eFlags & EF_HEADSHOT ) ) { // (SA) already lost hat while living + CG_LoseHat( cent, tv( 0, 0, 1 ) ); + } +} + + +/* +============== +CG_SparklerSparks +============== +*/ +void CG_SparklerSparks( vec3_t origin, int count ) { +// these effect the look of the, umm, effect + int FUSE_SPARK_LIFE = 100; + int FUSE_SPARK_LENGTH = 30; +// these are calculated from the above + int FUSE_SPARK_SPEED = ( FUSE_SPARK_LENGTH * 1000 / FUSE_SPARK_LIFE ); + + int i; + localEntity_t *le; + refEntity_t *re; + + for ( i = 0; i < count; i++ ) { + + // spawn the spark + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FUSE_SPARK; + le->startTime = cg.time; + le->endTime = cg.time + FUSE_SPARK_LIFE; + le->lastTrailTime = cg.time; + + VectorCopy( origin, re->origin ); + + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + VectorSet( le->pos.trDelta, crandom(), crandom(), crandom() ); + VectorNormalize( le->pos.trDelta ); + VectorScale( le->pos.trDelta, FUSE_SPARK_SPEED, le->pos.trDelta ); + le->pos.trTime = cg.time; + + } +} + +// just a bunch of numbers we can use for pseudo-randomizing based on time +#define NUMRANDTABLE 257 +unsigned short randtable[NUMRANDTABLE] = +{ + 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 +}; + +#define LT_MS 100 // random number will change every LT_MS millseconds +#define LT_RANDMAX ( (unsigned short)0xffff ) + +float lt_random( int thisrandseed, int t ) { + return (float)randtable[( thisrandseed + t + ( cg.time / LT_MS ) * ( cg.time / LT_MS ) ) % NUMRANDTABLE] / (float)LT_RANDMAX; +} + +float lt_crandom( int thisrandseed, int t ) { + return ( ( 2.0 * ( (float)randtable[( thisrandseed + t + ( cg.time / LT_MS ) * ( cg.time / LT_MS ) ) % NUMRANDTABLE] / (float)LT_RANDMAX ) ) - 1.0 ); +} + +/* +================ +CG_ProjectedSpotLight +================ +*/ +void CG_ProjectedSpotLight( vec3_t start, vec3_t dir ) { + vec3_t end; + trace_t tr; + float alpha, radius; + vec4_t projection; + + + VectorMA( start, 1000, dir, end ); + CG_Trace( &tr, start, NULL, NULL, end, -1, CONTENTS_SOLID ); + if ( tr.fraction == 1.0 ) { + return; + } + // + alpha = ( 1.0 - tr.fraction ); + if ( alpha > 1.0 ) { + alpha = 1.0; + } + // + radius = 32 + 64 * tr.fraction; + //% VectorNegate( dir, projection ); + //% CG_ImpactMark( cgs.media.spotLightShader, tr.endpos, projection, 0, alpha, alpha, alpha, 1.0, qfalse, radius, qtrue, -2 ); + + VectorCopy( dir, projection ); + projection[ 3 ] = radius * 2.0f; + CG_ImpactMark( cgs.media.spotLightShader, tr.endpos, projection, radius, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1 ); +} + + +#define MAX_SPOT_SEGS 20 +#define MAX_SPOT_RANGE 2000 +/* +============== +CG_Spotlight + segs: number of sides on tube. - 999 is a valid value and just means, 'cap to max' (MAX_SPOT_SEGS) or use lod scheme + range: effective range of beam + startWidth: will be optimized for '0' as a value (true cone) to not use quads and not cap the start circle + + -- flags -- + SL_NOTRACE - don't do a trace check for shortening the beam, always draw at full 'range' length + SL_TRACEWORLDONLY - go through everything but the world + SL_NODLIGHT - don't put a dlight at the end + SL_NOSTARTCAP - dont' cap the start circle + SL_LOCKTRACETORANGE - only trace out as far as the specified range (rather than to max spot range) + SL_NOFLARE - don't draw a flare when the light is pointing at the camera + SL_NOIMPACT - don't draw the impact mark on hit surfaces + SL_LOCKUV - lock the texture coordinates at the 'true' length of the requested beam. + SL_NOCORE - don't draw the center 'core' beam + + + + + + + I know, this is a bit kooky right now. It evolved big, but now that I know what it should do, it'll get + crunched down to a bunch of table driven stuff. once it works, I'll make it work well... + +============== +*/ + +void CG_Spotlight( centity_t *cent, float *color, vec3_t realstart, vec3_t lightDir, int segs, float range, int startWidth, float coneAngle, int flags ) { + int i, j; + vec3_t start, traceEnd; + vec3_t right, up; + vec3_t v1, v2; + vec3_t startvec, endvec; // the vectors to rotate around lightDir to create the circles + vec3_t conevec; + vec3_t start_points[MAX_SPOT_SEGS], end_points[MAX_SPOT_SEGS]; + vec3_t coreright; + polyVert_t verts[MAX_SPOT_SEGS * 4]; // x4 for 4 verts per poly + polyVert_t plugVerts[MAX_SPOT_SEGS]; + vec3_t endCenter; + polyVert_t coreverts[4]; + trace_t tr; + float alpha; + float radius = 0.0; // TTimo might be used uninitialized + float coreEndRadius; + qboolean capStart = qtrue; + float hitDist; // the actual distance of the trace impact (0 is no hit) + float beamLen; // actual distance of the drawn beam + float endAlpha = 0.0; + vec4_t colorNorm; // normalized color vector + refEntity_t ent; + vec3_t angles; + vec4_t projection; + + VectorCopy( realstart, start ); + + // normalize color + colorNorm[3] = 0; // store normalize multiplier in alpha index + for ( i = 0; i < 3; i++ ) { + if ( color[i] > colorNorm[3] ) { + colorNorm[3] = color[i]; // find largest color value in RGB + } + } + + if ( colorNorm[3] != 1 ) { // it needs to be boosted + VectorMA( color, 1.0 / colorNorm[3], color, colorNorm ); // FIXME: div by 0 + } else { + VectorCopy( color, colorNorm ); + } + colorNorm[3] = color[3]; + + + if ( flags & SL_NOSTARTCAP ) { + capStart = qfalse; + } + + if ( startWidth == 0 ) { // cone, not cylinder + capStart = qfalse; + } + + if ( flags & SL_LOCKTRACETORANGE ) { + VectorMA( start, range, lightDir, traceEnd ); // trace out to 'range' + } else { + VectorMA( start, MAX_SPOT_RANGE, lightDir, traceEnd ); // trace all the way out to max dist + } + + // first trace to see if anything is hit + if ( flags & SL_NOTRACE ) { + tr.fraction = 1.0; // force no hit + } else { + if ( flags & SL_TRACEWORLDONLY ) { + CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, CONTENTS_SOLID ); + } else { + CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, MASK_SHOT ); + } +// CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, MASK_ALL &~(CONTENTS_MONSTERCLIP|CONTENTS_AREAPORTAL|CONTENTS_CLUSTERPORTAL)); + } + + + if ( tr.fraction < 1.0 ) { + hitDist = beamLen = MAX_SPOT_RANGE * tr.fraction; + if ( beamLen > range ) { + beamLen = range; + } + } else { + hitDist = 0; + beamLen = range; + } + + + if ( flags & SL_LOCKUV ) { + if ( beamLen < range ) { + endAlpha = 255.0f * ( color[3] - ( color[3] * beamLen / range ) ); + } + } + + + if ( segs >= MAX_SPOT_SEGS ) { + segs = MAX_SPOT_SEGS - 1; + } + + // TODO: adjust segs based on r_lodbias + // TODO: move much of this to renderer + + +// model at base + if ( cent->currentState.modelindex ) { + memset( &ent, 0, sizeof( ent ) ); + ent.frame = 0; + ent.oldframe = 0; + ent.backlerp = 0; + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + ent.hModel = cgs.gameModels[cent->currentState.modelindex]; + // AnglesToAxis( cent->lerpAngles, ent.axis ); + vectoangles( lightDir, angles ); + AnglesToAxis( angles, ent.axis ); + trap_R_AddRefEntityToScene( &ent ); + memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); + + // push start out a bit so the beam fits to the front of the base model + VectorMA( start, 14, lightDir, start ); + } + +//// BEAM + + PerpendicularVector( up, lightDir ); + CrossProduct( lightDir, up, right ); + + // find first vert of the start + VectorScale( right, startWidth, startvec ); + // find the first vert of the end + RotatePointAroundVector( conevec, up, lightDir, -coneAngle ); + VectorMA( startvec, beamLen, conevec, endvec ); // this applies the offset of the start diameter before finding the end points + + VectorScale( lightDir, beamLen, endCenter ); + VectorSubtract( endCenter, endvec, coreverts[3].xyz ); // get a vector of the radius out at the end for the core to use + coreEndRadius = VectorLength( coreverts[3].xyz ); +#define CORESCALE 0.6f + +// +// generate the flat beam 'core' +// + if ( !( flags & SL_NOCORE ) ) { + VectorSubtract( start, cg.refdef_current->vieworg, v1 ); + VectorNormalize( v1 ); + VectorSubtract( traceEnd, cg.refdef_current->vieworg, v2 ); + VectorNormalize( v2 ); + CrossProduct( v1, v2, coreright ); + VectorNormalize( coreright ); + + memset( &coreverts[0], 0, 4 * sizeof( polyVert_t ) ); + VectorMA( start, startWidth * 0.5f, coreright, coreverts[0].xyz ); + VectorMA( start, -startWidth * 0.5f, coreright, coreverts[1].xyz ); + VectorMA( endCenter, -coreEndRadius * CORESCALE, coreright, coreverts[2].xyz ); + VectorAdd( start, coreverts[2].xyz, coreverts[2].xyz ); + VectorMA( endCenter, coreEndRadius * CORESCALE, coreright, coreverts[3].xyz ); + VectorAdd( start, coreverts[3].xyz, coreverts[3].xyz ); + + for ( i = 0; i < 4; i++ ) { + coreverts[i].modulate[0] = color[0] * 200.0f; + coreverts[i].modulate[1] = color[1] * 200.0f; + coreverts[i].modulate[2] = color[2] * 200.0f; + coreverts[i].modulate[3] = color[3] * 200.0f; + if ( i > 1 ) { + coreverts[i].modulate[3] = 0; + } + } + + trap_R_AddPolyToScene( cgs.media.spotLightBeamShader, 4, &coreverts[0] ); + } + + +// +// generate the beam cylinder +// + + + + for ( i = 0; i <= segs; i++ ) { + RotatePointAroundVector( start_points[i], lightDir, startvec, ( 360.0f / (float)segs ) * i ); + VectorAdd( start_points[i], start, start_points[i] ); + + RotatePointAroundVector( end_points[i], lightDir, endvec, ( 360.0f / (float)segs ) * i ); + VectorAdd( end_points[i], start, end_points[i] ); + } + + for ( i = 0; i < segs; i++ ) { + + j = ( i * 4 ); + + VectorCopy( start_points[i], verts[( i * 4 )].xyz ); + verts[j].st[0] = 0; + verts[j].st[1] = 1; + verts[j].modulate[0] = color[0] * 255.0f; + verts[j].modulate[1] = color[1] * 255.0f; + verts[j].modulate[2] = color[2] * 255.0f; + verts[j].modulate[3] = color[3] * 255.0f; + j++; + + VectorCopy( end_points[i], verts[j].xyz ); + verts[j].st[0] = 0; + verts[j].st[1] = 0; + verts[j].modulate[0] = color[0] * 255.0f; + verts[j].modulate[1] = color[1] * 255.0f; + verts[j].modulate[2] = color[2] * 255.0f; + verts[j].modulate[3] = endAlpha; + j++; + + VectorCopy( end_points[i + 1], verts[j].xyz ); + verts[j].st[0] = 1; + verts[j].st[1] = 0; + verts[j].modulate[0] = color[0] * 255.0f; + verts[j].modulate[1] = color[1] * 255.0f; + verts[j].modulate[2] = color[2] * 255.0f; + verts[j].modulate[3] = endAlpha; + j++; + + VectorCopy( start_points[i + 1], verts[j].xyz ); + verts[j].st[0] = 1; + verts[j].st[1] = 1; + verts[j].modulate[0] = color[0] * 255.0f; + verts[j].modulate[1] = color[1] * 255.0f; + verts[j].modulate[2] = color[2] * 255.0f; + verts[j].modulate[3] = color[3] * 255.0f; + + if ( capStart ) { + VectorCopy( start_points[i], plugVerts[i].xyz ); + plugVerts[i].st[0] = 0; + plugVerts[i].st[1] = 0; + plugVerts[i].modulate[0] = color[0] * 255.0f; + plugVerts[i].modulate[1] = color[1] * 255.0f; + plugVerts[i].modulate[2] = color[2] * 255.0f; + plugVerts[i].modulate[3] = color[3] * 255.0f; + } + } + + trap_R_AddPolysToScene( cgs.media.spotLightBeamShader, 4, &verts[0], segs ); + + + // plug up the start circle + if ( capStart ) { + trap_R_AddPolyToScene( cgs.media.spotLightBeamShader, segs, &plugVerts[0] ); + } + + + // show the endpoint + + if ( !( flags & SL_NOIMPACT ) ) { + if ( hitDist ) { + VectorMA( startvec, hitDist, conevec, endvec ); + + alpha = 0.3f; + radius = coreEndRadius * ( hitDist / beamLen ); + + //% VectorNegate( lightDir, proj ); + //% CG_ImpactMark( cgs.media.spotLightShader, tr.endpos, proj, 0, colorNorm[0], colorNorm[1], colorNorm[2], alpha, qfalse, radius, qtrue, -1 ); + + VectorCopy( lightDir, projection ); + projection[ 3 ] = radius * 2.0f; + CG_ImpactMark( cgs.media.spotLightShader, tr.endpos, projection, radius, colorNorm[ 0 ], colorNorm[ 1 ], colorNorm[ 2 ], 1.0f, 1.0f, -1 ); + } + } + + + + // add d light at end + if ( !( flags & SL_NODLIGHT ) ) { + vec3_t dlightLoc; + VectorMA( tr.endpos, 0, lightDir, dlightLoc ); // back away from the hit + trap_R_AddLightToScene( dlightLoc, radius * 2, 0.3, 1.0, 1.0, 1.0, 0, 0 ); + } + + + + // draw flare at source + if ( !( flags & SL_NOFLARE ) ) { + qboolean lightInEyes = qfalse; + vec3_t camloc, dirtolight; + float dot, deg, dist; + float flarescale = 0.0; // TTimo: might be used uninitialized + + // get camera position and direction to lightsource + VectorCopy( cg.snap->ps.origin, camloc ); + camloc[2] += cg.snap->ps.viewheight; + VectorSubtract( start, camloc, dirtolight ); + dist = VectorNormalize( dirtolight ); + + // first use dot to determine if it's facing the camera + dot = DotProduct( lightDir, dirtolight ); + + // it's facing the camera, find out how closely and trace to see if the source can be seen + + deg = RAD2DEG( M_PI - acos( dot ) ); + if ( deg <= 35 ) { // start flare a bit before the camera gets inside the cylinder + lightInEyes = qtrue; + flarescale = 1 - ( deg / 35 ); + } + + if ( lightInEyes ) { // the dot check succeeded, now do a trace + CG_Trace( &tr, start, NULL, NULL, camloc, -1, MASK_ALL & ~( CONTENTS_MONSTERCLIP | CONTENTS_AREAPORTAL | CONTENTS_CLUSTERPORTAL ) ); + if ( tr.fraction != 1 ) { + lightInEyes = qfalse; + } + + } + + if ( lightInEyes ) { + float coronasize = flarescale; + if ( dist < 512 ) { // make even bigger if you're close enough + coronasize *= ( 512.0f / dist ); + } + + trap_R_AddCoronaToScene( start, colorNorm[0], colorNorm[1], colorNorm[2], coronasize, cent->currentState.number, qtrue ); + } else { + // even though it's off, still need to add it, but turned off so it can fade in/out properly + trap_R_AddCoronaToScene( start, colorNorm[0], colorNorm[1], colorNorm[2], 0, cent->currentState.number, qfalse ); + } + } + +} + + + +/* +============== +CG_RumbleEfx +============== +*/ +void CG_RumbleEfx( float pitch, float yaw ) { + float pitchRecoilAdd, pitchAdd; + float yawRandom; + vec3_t recoil; + + // + pitchRecoilAdd = 0; + pitchAdd = 0; + yawRandom = 0; + // + + if ( pitch < 1 ) { + pitch = 1; + } + + pitchRecoilAdd = pow( random(),8 ) * ( 10 + VectorLength( cg.snap->ps.velocity ) / 5 ); + pitchAdd = ( rand() % (int)pitch ) - ( pitch * 0.5 ); //5 + yawRandom = yaw; //2 + + pitchRecoilAdd *= 0.5; + pitchAdd *= 0.5; + yawRandom *= 0.5; + + // calc the recoil + + // xkan, 11/04/2002 - the following used to be "recoil[YAW] = crandom()*yawRandom()" + // but that seems to skew the effect either to the left or to the right for long streches + // of time. The idea here is to keep it skewed for short period of time and then switches + // to the other direction - switch the sign of recoil[YAW] when random() < 0.05 and keep the + // sign otherwise. This seems better at balancing out the effect. + if ( cg.kickAVel[YAW] > 0 ) { + if ( random() < 0.05 ) { + recoil[YAW] = -random() * yawRandom; + } else { + recoil[YAW] = random() * yawRandom; + } + } else if ( cg.kickAVel[YAW] < 0 ) { + if ( random() < 0.05 ) { + recoil[YAW] = random() * yawRandom; + } else { + recoil[YAW] = -random() * yawRandom; + } + } else + { + if ( random() < 0.5 ) { + recoil[YAW] = random() * yawRandom; + } else { + recoil[YAW] = -random() * yawRandom; + } + } + + recoil[ROLL] = -recoil[YAW]; // why not + recoil[PITCH] = -pitchAdd; + // scale it up a bit (easier to modify this while tweaking) + VectorScale( recoil, 30, recoil ); + // set the recoil + VectorCopy( recoil, cg.kickAVel ); + // set the recoil + cg.recoilPitch -= pitchRecoilAdd; +} + +#define MAX_SMOKESPRITES 512 +#define SMOKEBOMB_DISTANCEBETWEENSPRITES 16.f +#define SMOKEBOMB_SPAWNRATE 10 +#define SMOKEBOMB_SMOKEVELOCITY ( ( 640.f - 16.f ) / 8 ) / 1000.f // units per msec + +typedef struct smokesprite_s { + struct smokesprite_s *next; + struct smokesprite_s *prev; // this one is only valid for alloced smokesprites + + vec3_t pos; + vec4_t colour; + + vec3_t dir; + float dist; + float size; + + centity_t *smokebomb; +} smokesprite_t; + +static smokesprite_t SmokeSprites[MAX_SMOKESPRITES]; +static int SmokeSpriteCount = 0; +static smokesprite_t *firstfreesmokesprite; // pointer to the first free smokepuff in the SmokeSprites pool +static smokesprite_t *lastusedsmokesprite; // pointer to the last used smokepuff + +void InitSmokeSprites( void ) { + int i; + + memset( &SmokeSprites, 0, sizeof( SmokeSprites ) ); + for ( i = 0; i < MAX_SMOKESPRITES - 1; i++ ) { + SmokeSprites[i].next = &SmokeSprites[i + 1]; + } + + firstfreesmokesprite = &SmokeSprites[0]; + lastusedsmokesprite = NULL; + SmokeSpriteCount = 0; +} + +static smokesprite_t *AllocSmokeSprite( void ) { + smokesprite_t *alloc; + + if ( SmokeSpriteCount >= MAX_SMOKESPRITES ) { + return( NULL ); + } + + alloc = firstfreesmokesprite; + + firstfreesmokesprite = alloc->next; + + if ( lastusedsmokesprite ) { + lastusedsmokesprite->next = alloc; + } + + alloc->next = NULL; + alloc->prev = lastusedsmokesprite; + lastusedsmokesprite = alloc; + + SmokeSpriteCount++; + return( alloc ); +} + +// Returns previous alloced smokesprite in list (or NULL when there are no more alloced smokesprites left) +static smokesprite_t *DeAllocSmokeSprite( smokesprite_t *dealloc ) { + smokesprite_t *ret_smokesprite; + + if ( dealloc->prev ) { + dealloc->prev->next = dealloc->next; + } + + if ( dealloc->next ) { + dealloc->next->prev = dealloc->prev; + } else { // no next particle, so this particle was 'lastusedsmokesprite' + lastusedsmokesprite = dealloc->prev; + if ( lastusedsmokesprite ) { // incase that there was no previous particle (happens when there is only one particle and this one gets dealloced) + lastusedsmokesprite->next = NULL; + } + } + + ret_smokesprite = dealloc->prev; + + memset( dealloc, 0, sizeof( smokesprite_t ) ); + dealloc->next = firstfreesmokesprite; + firstfreesmokesprite = dealloc; + + SmokeSpriteCount--; + return( ret_smokesprite ); +} + +static qboolean CG_SmokeSpritePhysics( smokesprite_t *smokesprite, const float dist ) { + trace_t tr; + vec3_t oldpos; + //vec3_t mins, maxs; + + VectorCopy( smokesprite->pos, oldpos ); + VectorMA( oldpos, dist, smokesprite->dir, smokesprite->pos ); + + smokesprite->dist += dist; + + smokesprite->size += 1.25f * dist; + + // see if we hit a solid + // FIXME: use mins and max with smoke sprite minimum radius and then expand to max possible distance or real current sprite size? + // would definately look nice I think + //VectorSet( maxs, .3f * smokesprite->size, .3f * smokesprite->size, .3f * smokesprite->size ); + //VectorNegate( maxs, mins ); + //CG_Trace( &tr, oldpos, mins, maxs, smokesprite->pos, -1, CONTENTS_SOLID ); + CG_Trace( &tr, oldpos, NULL, NULL, smokesprite->pos, -1, CONTENTS_SOLID ); + + if ( tr.fraction != 1.f ) { + //float dot; + + if ( smokesprite->dist < 24.f ) { + return( qfalse ); + } + VectorCopy( tr.endpos, smokesprite->pos ); + + // bounce off + //dot = DotProduct( smokesprite->dir, tr.plane.normal ); + //VectorMA( smokesprite->dir, -2*dot, tr.plane.normal, smokesprite->dir ); + //VectorScale( smokesprite->dir, .25f, smokesprite->dir ); + } // else { + // smokesprite->size += 1.25f * dist; + //} + + return( qtrue ); +} + +qboolean CG_SpawnSmokeSprite( centity_t *cent, float dist ) { + smokesprite_t *smokesprite = AllocSmokeSprite(); + + if ( smokesprite ) { + smokesprite->smokebomb = cent; + //VectorCopy( cent->lerpOrigin, smokesprite->pos ); + //smokesprite->pos[2] += 32; + VectorCopy( cent->origin2, smokesprite->pos ); + VectorCopy( bytedirs[rand() % NUMVERTEXNORMALS], smokesprite->dir ); + smokesprite->dir[2] *= .5f; + smokesprite->size = 16.f; + smokesprite->colour[0] = .35f; // + crandom() * .1f; + smokesprite->colour[1] = smokesprite->colour[0]; + smokesprite->colour[2] = smokesprite->colour[0]; + smokesprite->colour[3] = .8f; + + // Advance sprite + if ( !CG_SmokeSpritePhysics( smokesprite, dist ) ) { + DeAllocSmokeSprite( smokesprite ); + return( qfalse ); + } else { + cent->miscTime++; + } + } + + return( qtrue ); +} + +void CG_RenderSmokeGrenadeSmoke( centity_t *cent, const weaponInfo_t *weapon ) { + //int numSpritesForRadius, numNewSpritesNeeded = 0; + int spritesNeeded = 0; + smokesprite_t *smokesprite; + float spawnrate = ( 1.f / SMOKEBOMB_SPAWNRATE ) * 1000.f; + + if ( cent->currentState.effect1Time == 16 ) { + cent->miscTime = 0; + cent->lastFuseSparkTime = 0; // last spawn time + cent->muzzleFlashTime = 0; // delta time + cent->dl_atten = 0; + return; + } + + if ( cent->currentState.effect1Time > 16 ) { + int volume = 16 + ( ( cent->currentState.effect1Time / 640.f ) * ( 100 - 16 ) ); + + if ( !cent->dl_atten || + cent->currentState.pos.trType != TR_STATIONARY || + ( cent->currentState.groundEntityNum != ENTITYNUM_WORLD && !VectorCompare( cent->lastLerpOrigin, cent->lerpOrigin ) ) ) { + trace_t tr; + + VectorCopy( cent->lerpOrigin, cent->origin2 ); + cent->origin2[2] += 32; + CG_Trace( &tr, cent->currentState.pos.trBase, NULL, NULL, cent->origin2, -1, CONTENTS_SOLID ); + + if ( tr.startsolid ) { + cent->dl_atten = 2; + } else { + VectorCopy( tr.endpos, cent->origin2 ); + cent->dl_atten = 1; + } + } + + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, weapon->overheatSound, volume, 0 ); + + // emitter is stuck in solid + if ( cent->dl_atten == 2 ) { + return; + } + + // Number of sprites for radius calculation: + // lifetime of a sprite : (.5f * radius) / velocity + // number of sprites in a row: radius / SMOKEBOMB_DISTANCEBETWEENSPRITES +// numSpritesForRadius = cent->currentState.effect1Time / SMOKEBOMB_DISTANCEBETWEENSPRITES; + +// numSpritesForRadius = cent->currentState.effect1Time / ((((640.f - 16.f)/16)/1000.f) * cg.frametime); +// numNewSpritesNeeded = numSpritesForRadius - cent->miscTime; + +// CG_Printf( "numSpritesForRadius: %i / numNewSpritesNeeded: %i / cent->miscTime: %i\n", numSpritesForRadius, numNewSpritesNeeded, cent->miscTime ); + + if ( cg.oldTime && cent->lastFuseSparkTime != cg.time ) { + cent->muzzleFlashTime += cg.frametime; + spritesNeeded = cent->muzzleFlashTime / spawnrate; + cent->muzzleFlashTime -= ( spawnrate * spritesNeeded ); + cent->lastFuseSparkTime = cg.time; + } + +// if( spritesNeeded + cent->miscTime < 40 ) +// spritesNeeded = 40 - cent->miscTime; + + if ( !spritesNeeded ) { + return; + } else if ( spritesNeeded == 1 ) { + // this is theoretically fine, till the smokegrenade ends up in a solid + //while( !CG_SpawnSmokeSprite( cent, 0.f ) ); + + // this is better + if ( !CG_SpawnSmokeSprite( cent, 0.f ) ) { + // try again, just in case, so we don't get lots of gaps and remain quite constant + CG_SpawnSmokeSprite( cent, 0.f ); + } + } else { +// float lerpfrac = 1.0f / (float)spritesNeeded; + float lerp = 1.0f; + float dtime; + + for ( dtime = spritesNeeded * spawnrate; dtime > 0; dtime -= spawnrate ) { + // this is theoretically fine, till the smokegrenade ends up in a solid + //while( !CG_SpawnSmokeSprite( cent, lerp * cg.frametime * SMOKEBOMB_SMOKEVELOCITY ) ); + + // this is better + if ( !CG_SpawnSmokeSprite( cent, lerp * cg.frametime * SMOKEBOMB_SMOKEVELOCITY ) ) { + // try again, just in case, so we don't get lots of gaps and remain quite constant + CG_SpawnSmokeSprite( cent, lerp * cg.frametime * SMOKEBOMB_SMOKEVELOCITY ); + } + } + } + } else if ( cent->currentState.effect1Time == -1 ) { + // unlink smokesprites from smokebomb + if ( cent->miscTime > 0 ) { + smokesprite = lastusedsmokesprite; + while ( smokesprite ) { + if ( smokesprite->smokebomb == cent ) { + smokesprite->smokebomb = NULL; + cent->miscTime--; + } + + smokesprite = smokesprite->prev; + } + } + } +} + +void CG_AddSmokeSprites( void ) { + smokesprite_t *smokesprite; + qhandle_t shader; + byte color[4]; + polyVert_t verts[4]; + vec3_t top, bottom; + vec3_t right, up, tmp; + float radius; + float halfSmokeSpriteWidth, halfSmokeSpriteHeight; + float dist = SMOKEBOMB_SMOKEVELOCITY * cg.frametime; + + smokesprite = lastusedsmokesprite; + while ( smokesprite ) { + if ( smokesprite->smokebomb && !smokesprite->smokebomb->currentValid ) { + smokesprite = smokesprite->prev; + continue; + } + + // Do physics + if ( !CG_SmokeSpritePhysics( smokesprite, dist ) ) { + if ( smokesprite->smokebomb ) { + smokesprite->smokebomb->miscTime--; + } + smokesprite = DeAllocSmokeSprite( smokesprite ); + continue; + } + + if ( smokesprite->smokebomb ) { + radius = smokesprite->smokebomb->currentState.effect1Time; + } else { + radius = -1.f; + } + + if ( radius < 0 ) { + radius = 640.f; // max radius + + } + // Expire sprites + if ( smokesprite->dist > radius * .5f ) { + if ( smokesprite->smokebomb ) { + smokesprite->smokebomb->miscTime--; + } + + smokesprite = DeAllocSmokeSprite( smokesprite ); + continue; + } + + // Now render it + halfSmokeSpriteWidth = 0.5f * smokesprite->size; + halfSmokeSpriteHeight = 0.5f * smokesprite->size; + + VectorCopy( cg.refdef_current->viewaxis[1], tmp ); + RotatePointAroundVector( right, cg.refdef_current->viewaxis[0], tmp, 0 ); + CrossProduct( cg.refdef_current->viewaxis[0], right, up ); + + VectorMA( smokesprite->pos, halfSmokeSpriteHeight, up, top ); + VectorMA( smokesprite->pos, -halfSmokeSpriteHeight, up, bottom ); + + color[0] = smokesprite->colour[0] * 0xff; + color[1] = smokesprite->colour[1] * 0xff; + color[2] = smokesprite->colour[2] * 0xff; + color[3] = smokesprite->colour[3] * 0xff; + + // fadeout + if ( smokesprite->dist > ( radius * .5f * .8f ) ) { + color[3] = ( smokesprite->colour[3] - smokesprite->colour[3] * ( ( smokesprite->dist - ( radius * .5f * .8f ) ) / ( ( radius * .5f ) - ( radius * .5f * .8f ) ) ) ) * 0xff; + } else { + color[3] = smokesprite->colour[3] * 0xff; + } + + VectorMA( top, halfSmokeSpriteWidth, right, verts[0].xyz ); + verts[0].st[0] = 1; + verts[0].st[1] = 0; + memcpy( verts[0].modulate, color, 4 ); + + VectorMA( top, -halfSmokeSpriteWidth, right, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 0; + memcpy( verts[1].modulate, color, 4 ); + + VectorMA( bottom, -halfSmokeSpriteWidth, right, verts[2].xyz ); + verts[2].st[0] = 0; + verts[2].st[1] = 1; + memcpy( verts[2].modulate, color, 4 ); + + VectorMA( bottom, halfSmokeSpriteWidth, right, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 1; + memcpy( verts[3].modulate, color, 4 ); + + shader = cgs.media.smokePuffShader; + + trap_R_AddPolyToScene( shader, 4, verts ); + + smokesprite = smokesprite->prev; + } +} diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c new file mode 100644 index 0000000..e559841 --- /dev/null +++ b/src/cgame/cg_ents.c @@ -0,0 +1,2828 @@ +/* +=========================================================================== + +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: cg_ents.c + * + * desc: present snapshot entities, happens every single frame + * +*/ + + +#include "cg_local.h" + +/* +====================== +CG_PositionEntityOnTag + +Modifies the entities position and axis by the given +tag location +====================== +*/ +void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, const char *tagName, int startIndex, vec3_t *offset ) { + int i; + orientation_t lerped; + + // lerp the tag + trap_R_LerpTag( &lerped, parent, tagName, startIndex ); + + // allow origin offsets along tag + VectorCopy( parent->origin, entity->origin ); + + if ( offset ) { + VectorAdd( lerped.origin, *offset, lerped.origin ); + } + + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // had to cast away the const to avoid compiler problems... + MatrixMultiply( lerped.axis, ( (refEntity_t *)parent )->axis, entity->axis ); +} + + +/* +====================== +CG_PositionRotatedEntityOnTag + +Modifies the entities position and axis by the given +tag location +====================== +*/ +void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, const char *tagName ) { + int i; + orientation_t lerped; + vec3_t tempAxis[3]; + +//AxisClear( entity->axis ); + // lerp the tag + trap_R_LerpTag( &lerped, parent, tagName, 0 ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // had to cast away the const to avoid compiler problems... + MatrixMultiply( entity->axis, lerped.axis, tempAxis ); + MatrixMultiply( tempAxis, ( (refEntity_t *)parent )->axis, entity->axis ); +} + + +/* +========================================================================== + +FUNCTIONS CALLED EACH FRAME + +========================================================================== +*/ + +/* +====================== +CG_SetEntitySoundPosition + +Also called by event processing code +====================== +*/ +void CG_SetEntitySoundPosition( centity_t *cent ) { + if ( cent->currentState.solid == SOLID_BMODEL ) { + vec3_t origin; + float *v; + + v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; + VectorAdd( cent->lerpOrigin, v, origin ); + trap_S_UpdateEntityPosition( cent->currentState.number, origin ); + } else { + trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin ); + } +} + + + + +#define LS_FRAMETIME 100 // (ms) cycle through lightstyle characters at 10fps + + +/* +============== +CG_SetDlightIntensity + +============== +*/ +void CG_AddLightstyle( centity_t *cent ) { + float lightval; + int cl; + int r, g, b; + int stringlength; + float offset; + int offsetwhole; + int otime; + int lastch, nextch; + + if ( !cent->dl_stylestring ) { + return; + } + + otime = cg.time - cent->dl_time; + stringlength = strlen( cent->dl_stylestring ); + + // it's been a long time since you were updated, lets assume a reset + if ( otime > 2 * LS_FRAMETIME ) { + otime = 0; + cent->dl_frame = cent->dl_oldframe = 0; + cent->dl_backlerp = 0; + } + + cent->dl_time = cg.time; + + offset = ( (float)otime ) / LS_FRAMETIME; + offsetwhole = (int)offset; + + cent->dl_backlerp += offset; + + + if ( cent->dl_backlerp > 1 ) { // we're moving on to the next frame + cent->dl_oldframe = cent->dl_oldframe + (int)cent->dl_backlerp; + cent->dl_frame = cent->dl_oldframe + 1; + if ( cent->dl_oldframe >= stringlength ) { + cent->dl_oldframe = ( cent->dl_oldframe ) % stringlength; + if ( cent->dl_oldframe < 3 && cent->dl_sound ) { // < 3 so if an alarm comes back into the pvs it will only start a sound if it's going to be closely synced with the light, otherwise wait till the next cycle + trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.gameSounds[cent->dl_sound] ); + } + } + + if ( cent->dl_frame >= stringlength ) { + cent->dl_frame = ( cent->dl_frame ) % stringlength; + } + + cent->dl_backlerp = cent->dl_backlerp - (int)cent->dl_backlerp; + } + + + lastch = cent->dl_stylestring[cent->dl_oldframe] - 'a'; + nextch = cent->dl_stylestring[cent->dl_frame] - 'a'; + + lightval = ( lastch * ( 1.0 - cent->dl_backlerp ) ) + ( nextch * cent->dl_backlerp ); + + // ydnar: dlight values go from 0-1.5ish + #if 0 + lightval = ( lightval * ( 1000.0f / 24.0f ) ) - 200.0f; // they want 'm' as the "middle" value as 300 + lightval = max( 0.0f, lightval ); + lightval = min( 1000.0f, lightval ); + #else + lightval *= 0.071429; + lightval = max( 0.0f, lightval ); + lightval = min( 20.0f, lightval ); + #endif + + cl = cent->currentState.constantLight; + r = cl & 255; + g = ( cl >> 8 ) & 255; + b = ( cl >> 16 ) & 255; + + //% trap_R_AddLightToScene( cent->lerpOrigin, lightval, 1.0, (float)r/255.0f, (float)g/255.0f, (float)b/255.0f, 0, 0 ); // overdraw forced to 0 for now + + // ydnar: if the dlight has angles, then it is a directional global dlight + if ( cent->currentState.angles[ 0 ] || cent->currentState.angles[ 1 ] || cent->currentState.angles[ 2 ] ) { + vec3_t normal; + + + AngleVectors( cent->currentState.angles, normal, NULL, NULL ); + trap_R_AddLightToScene( normal, 256, lightval, + (float) r / 255.0f, (float) r / 255.0f, (float) r / 255.0f, 0, REF_DIRECTED_DLIGHT ); + } + // normal global dlight + else + { + trap_R_AddLightToScene( cent->lerpOrigin, 256, lightval, + (float) r / 255.0f, (float) g / 255.0f, (float) b / 255.0f, 0, 0 ); + } +} + + +void CG_GetWindVector( vec3_t dir ); // JPW NERVE + +/* +================== +CG_EntityEffects + +Add continuous entity effects, like local entity emission and lighting +================== +*/ +static void CG_EntityEffects( centity_t *cent ) { + static vec3_t dir; + // update sound origins + CG_SetEntitySoundPosition( cent ); + + // add loop sound + if ( cent->currentState.loopSound ) { + // ydnar: allow looped sounds to start at trigger time + if ( cent->soundTime == 0 ) { + cent->soundTime = trap_S_GetCurrentSoundTime(); + } + + if ( cent->currentState.eType == ET_SPEAKER ) { + int volume = cent->currentState.onFireStart; + + if ( cent->currentState.dmgFlags ) { // range is set + trap_S_AddRealLoopingSound( cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], cent->currentState.dmgFlags, volume, cent->soundTime ); + } else { + trap_S_AddRealLoopingSound( cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 1250, volume, cent->soundTime ); + } + } else if ( cent->currentState.eType == ET_MOVER ) { + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], cent->currentState.onFireStart, cent->soundTime ); + } else if ( cent->currentState.solid == SOLID_BMODEL ) { + vec3_t origin; + float *v; + + v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; + VectorAdd( cent->lerpOrigin, v, origin ); + trap_S_AddLoopingSound( origin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], cent->currentState.onFireStart, cent->soundTime ); + } else { + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255, cent->soundTime ); + } + } else if ( cent->soundTime ) { + cent->soundTime = 0; + } + + // constant light glow + if ( cent->currentState.constantLight ) { + int cl; + int i, r, g, b; + + + if ( cent->dl_stylestring[0] != 0 ) { // it's probably a dlight + CG_AddLightstyle( cent ); + } else + { + cl = cent->currentState.constantLight; + r = cl & 255; + g = ( cl >> 8 ) & 255; + b = ( cl >> 16 ) & 255; + i = ( ( cl >> 24 ) & 255 ) * 4; + + trap_R_AddLightToScene( cent->lerpOrigin, i, 1.0, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 0, 0 ); + } + } + + // Ridah, flaming sounds + if ( CG_EntOnFire( cent ) ) { + // play a flame blow sound when moving + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.media.flameBlowSound, (int)( 255.0 * ( 1.0 - fabs( cent->fireRiseDir[2] ) ) ), 0 ); + // play a burning sound when not moving + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.media.flameSound, (int)( 0.3 * 255.0 * ( pow( cent->fireRiseDir[2],2 ) ) ), 0 ); + } + + // ydnar: overheating is a special effect + if ( ( cent->currentState.eFlags & EF_OVERHEATING ) == EF_OVERHEATING ) { + if ( cent->overheatTime < ( cg.time - 3000 ) ) { + cent->overheatTime = cg.time; + } + if ( !( rand() % 3 ) ) { + float alpha; + vec3_t muzzle; + + if ( CG_CalcMuzzlePoint( ( cent - cg_entities ), muzzle ) ) { + muzzle[ 2 ] -= DEFAULT_VIEWHEIGHT; + } else { + VectorCopy( cent->lerpOrigin, muzzle ); + } + alpha = 1.0f - ( (float) ( cg.time - cent->overheatTime ) / 3000.0f ); + alpha *= 0.25f; + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, muzzle, + 1000, 8, 20, 30, alpha, 8.f ); + } + } + // DHM - Nerve :: If EF_SMOKING is set, emit smoke + else if ( cent->currentState.eFlags & EF_SMOKING ) { + float rnd = random(); + + if ( cent->lastTrailTime < cg.time ) { + cent->lastTrailTime = cg.time + 100; + +// JPW NERVE -- use wind vector for smoke + CG_GetWindVector( dir ); + VectorScale( dir,20,dir ); // was 75, before that 55 + if ( dir[2] < 10 ) { + dir[2] += 10; + } +// dir[0] = crandom() * 10; +// dir[1] = crandom() * 10; +// dir[2] = 10 + rnd * 30; +// jpw + CG_SmokePuff( cent->lerpOrigin, dir, 15 + ( random() * 10 ), + 0.3 + rnd, 0.3 + rnd, 0.3 + rnd, 0.4, 1500 + ( rand() % 500 ), + cg.time, cg.time + 500, 0, cgs.media.smokePuffShader ); + } + } + // dhm - end + // JPW NERVE same thing but for smoking barrels instead of nasty server-side effect from single player + else if ( cent->currentState.eFlags & EF_SMOKINGBLACK ) { + float rnd = random(); + + if ( cent->lastTrailTime < cg.time ) { + cent->lastTrailTime = cg.time + 75; + + CG_GetWindVector( dir ); + VectorScale( dir,50,dir ); // was 75, before that 55 + if ( dir[2] < 50 ) { + dir[2] += 50; + } + + CG_SmokePuff( cent->lerpOrigin, dir, 40 + random() * 70, //40+(rnd*40), + rnd * 0.1, rnd * 0.1, rnd * 0.1, 1, 2800 + ( rand() % 4000 ), //2500+(random()*1500), + cg.time, 0, 0, cgs.media.smokePuffShader ); + } + } +// jpw + + + +} + +void CG_RailTrail2( clientInfo_t *ci, vec3_t start, vec3_t end ); + +/* +================== +CG_General +================== +*/ +static void CG_General( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // if set to invisible, skip + if ( !s1->modelindex ) { + return; + } + + memset( &ent, 0, sizeof( ent ) ); + + // set frame + + ent.frame = s1->frame; + ent.oldframe = ent.frame; + ent.backlerp = 0; + + if ( ent.frame ) { + + ent.oldframe -= 1; + ent.backlerp = 1 - cg.frameInterpolation; + + if ( cent->currentState.time ) { + ent.fadeStartTime = cent->currentState.time; + ent.fadeEndTime = cent->currentState.time2; + } + + } + + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + ent.hModel = cgs.gameModels[s1->modelindex]; + + // player model + if ( s1->number == cg.snap->ps.clientNum ) { + ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors + } + + if ( cent->currentState.eType == ET_MG42_BARREL ) { + // grab angles from first person user or self if not + // ATVI Wolfenstein Misc #469 - don't track until viewlocked + if ( cent->currentState.otherEntityNum == cg.snap->ps.clientNum && cg.snap->ps.viewlocked ) { + AnglesToAxis( cg.predictedPlayerState.viewangles, ent.axis ); + } else { + AnglesToAxis( cent->lerpAngles, ent.axis ); + } + } else if ( cent->currentState.eType == ET_AAGUN ) { + // grab angles from first person user or self if not + if ( cent->currentState.otherEntityNum == cg.snap->ps.clientNum && cg.snap->ps.viewlocked ) { + AnglesToAxis( cg.predictedPlayerState.viewangles, ent.axis ); + } else { + //AnglesToAxis( cg_entities[cent->currentState.otherEntityNum].lerpAngles, ent.axis ); + AnglesToAxis( cent->lerpAngles, ent.axis ); + } +/* { + vec3_t v; + VectorCopy( cent->lerpOrigin, v ); + VectorMA( cent->lerpOrigin, 10, ent.axis[0], v ); + CG_RailTrail2( NULL, cent->lerpOrigin, v ); + VectorCopy( cent->lerpOrigin, v ); + VectorMA( cent->lerpOrigin, 10, ent.axis[1], v ); + CG_RailTrail2( NULL, cent->lerpOrigin, v ); + VectorCopy( cent->lerpOrigin, v ); + VectorMA( cent->lerpOrigin, 10, ent.axis[2], v ); + CG_RailTrail2( NULL, cent->lerpOrigin, v ); + return; + }*/ + } else { + // convert angles to axis + AnglesToAxis( cent->lerpAngles, ent.axis ); + } + + // scale gamemodels + if ( cent->currentState.eType == ET_GAMEMODEL ) { + VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] ); + VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] ); + VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + + if ( cent->currentState.apos.trType ) { + ent.reFlags |= REFLAG_ORIENT_LOD; + } + + if ( s1->torsoAnim ) { + if ( cg.time >= cent->lerpFrame.frameTime ) { + cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime; + cent->lerpFrame.oldFrame = cent->lerpFrame.frame; + + while ( cg.time >= cent->lerpFrame.frameTime && + // Mad Doc xkan, 11/18/2002 - if teamNum == 1, then we are supposed to stop + // the animation when we reach the end of this loop + // Gordon: 27/11/02: clientNum already does this. + // xkan, 1/8/2003 - No, it does something a little different. + !( s1->teamNum == 1 && + cent->lerpFrame.frame + s1->frame == s1->legsAnim + s1->torsoAnim ) ) { + cent->lerpFrame.frameTime += s1->weapon; + cent->lerpFrame.frame++; + + if ( cent->lerpFrame.frame >= s1->legsAnim + s1->torsoAnim ) { + if ( s1->clientNum ) { + cent->lerpFrame.frame = s1->legsAnim + s1->torsoAnim - 1; + cent->lerpFrame.oldFrame = s1->legsAnim + s1->torsoAnim - 1; + } else { + cent->lerpFrame.frame = s1->legsAnim; + } + } + } + } + + if ( cent->lerpFrame.frameTime == cent->lerpFrame.oldFrameTime ) { + cent->lerpFrame.backlerp = 0; + } else { + cent->lerpFrame.backlerp = 1.0 - (float)( cg.time - cent->lerpFrame.oldFrameTime ) / ( cent->lerpFrame.frameTime - cent->lerpFrame.oldFrameTime ); + } + + ent.frame = cent->lerpFrame.frame + s1->frame; // offset + if ( ent.frame >= s1->legsAnim + s1->torsoAnim ) { + ent.frame -= s1->torsoAnim; + } + + ent.oldframe = cent->lerpFrame.oldFrame + s1->frame; // offset + if ( ent.oldframe >= s1->legsAnim + s1->torsoAnim ) { + ent.oldframe -= s1->torsoAnim; + } + + ent.backlerp = cent->lerpFrame.backlerp; + +// CG_Printf( "Gamemodel: oldframe: %i frame: %i lerp: %f\n", ent.oldframe, ent.frame, ent.backlerp ); + } + + // xkan, 11/27/2002 - only advance/change frame if the game model has not + // been stopped (teamNum != 1) + if ( cent->trailTime && s1->teamNum != 1 ) { + cent->lerpFrame.oldFrame = cent->lerpFrame.frame; + cent->lerpFrame.frame = s1->legsAnim; + + cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime; + cent->lerpFrame.frameTime = cg.time; + + ent.oldframe = ent.frame; + ent.frame = s1->legsAnim; + ent.backlerp = 0; + } + + if ( cent->nextState.animMovetype != s1->animMovetype ) { + cent->trailTime = 1; + } else { + cent->trailTime = 0; + } + + if ( s1->modelindex2 ) { + ent.customSkin = cgs.gameModelSkins[s1->modelindex2]; + } + } + + // special shader if under construction + if ( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) { + /*if( cent->currentState.solid == SOLID_BMODEL ) { + ent.customShader = cgs.media.genericConstructionShaderBrush; + } else { + ent.customShader = cgs.media.genericConstructionShaderModel; + }*/ + ent.customShader = cgs.media.genericConstructionShader; + } + + // add to refresh list + trap_R_AddRefEntityToScene( &ent ); + + memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); +} + +/* +================== +CG_Speaker + +Speaker entities can automatically play sounds +================== +*/ +static void CG_Speaker( centity_t *cent ) { + if ( !cent->currentState.clientNum ) { // FIXME: use something other than clientNum... + return; // not auto triggering + } + + if ( cg.time < cent->miscTime ) { + return; + } + + trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] ); + + // ent->s.frame = ent->wait * 10; + // ent->s.clientNum = ent->random * 10; + cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom(); +} + +qboolean CG_PlayerSeesItem( playerState_t *ps, entityState_t *item, int atTime, int itemType ) { + vec3_t vorigin, eorigin, viewa, dir; + float dot, dist, foo; + trace_t tr; + + BG_EvaluateTrajectory( &item->pos, atTime, eorigin, qfalse, item->effect2Time ); + + VectorCopy( ps->origin, vorigin ); + vorigin[2] += ps->viewheight; // get the view loc up to the viewheight +// eorigin[2] += 8; // and subtract the item's offset (that is used to place it on the ground) + + + VectorSubtract( vorigin, eorigin, dir ); + + dist = VectorNormalize( dir ); // dir is now the direction from the item to the player + + if ( dist > 255 ) { + return qfalse; // only run the remaining stuff on items that are close enough + + } + // (SA) FIXME: do this without AngleVectors. + // It'd be nice if the angle vectors for the player + // have already been figured at this point and I can + // just pick them up. (if anybody is storing this somewhere, + // for the current frame please let me know so I don't + // have to do redundant calcs) + AngleVectors( ps->viewangles, viewa, 0, 0 ); + dot = DotProduct( viewa, dir ); + + // give more range based on distance (the hit area is wider when closer) + +// foo = -0.94f - (dist/255.0f) * 0.057f; // (ranging from -0.94 to -0.997) (it happened to be a pretty good range) + foo = -0.94f - ( dist * ( 1.0f / 255.0f ) ) * 0.057f; // (ranging from -0.94 to -0.997) (it happened to be a pretty good range) + +/// Com_Printf("test: if(%f > %f) return qfalse (dot > foo)\n", dot, foo); + if ( dot > foo ) { + return qfalse; + } + + // (SA) okay, everything else is okay, so do a bloody trace. (so coronas on treasure doesn't show through walls) + if ( itemType == IT_TREASURE ) { + CG_Trace( &tr, vorigin, NULL, NULL, eorigin, -1, MASK_SOLID ); + + if ( tr.fraction != 1 ) { + return qfalse; + } + } + + return qtrue; + +} + + +/* +================== +CG_Item +================== +*/ +static void CG_Item( centity_t *cent ) { + refEntity_t ent; + entityState_t *es; + gitem_t *item; +// float scale; + qboolean hasStand, highlight; + float highlightFadeScale = 1.0f; + + es = ¢->currentState; + + hasStand = qfalse; + highlight = qfalse; + + // (item index is stored in es->modelindex for item) + + if ( es->modelindex >= bg_numItems ) { + CG_Error( "Bad item index %i on entity", es->modelindex ); + } + + // if set to invisible, skip + if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) { + return; + } + + item = &bg_itemlist[ es->modelindex ]; + +// scale = 0.005 + cent->currentState.number * 0.00001; + + memset( &ent, 0, sizeof( ent ) ); + + ent.nonNormalizedAxes = qfalse; + + if ( item->giType == IT_WEAPON ) { + weaponInfo_t *weaponInfo = &cg_weapons[item->giTag]; + + if ( weaponInfo->standModel ) { + hasStand = qtrue; + } + + if ( hasStand ) { // first try to put the weapon on it's 'stand' + refEntity_t stand; + + memset( &stand, 0, sizeof( stand ) ); + stand.hModel = weaponInfo->standModel; + + if ( es->eFlags & EF_SPINNING ) { + if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air) + VectorCopy( cg.autoAnglesSlow, cent->lerpAngles ); + VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles ); + } else { + VectorCopy( cent->lastLerpAngles, cent->lerpAngles ); // make a tossed weapon sit on the ground in a position that matches how it was yawed + } + } + + AnglesToAxis( cent->lerpAngles, stand.axis ); + VectorCopy( cent->lerpOrigin, stand.origin ); + + // scale the stand to match the weapon scale ( the weapon will also be scaled inside CG_PositionEntityOnTag() ) + VectorScale( stand.axis[0], 1.5, stand.axis[0] ); + VectorScale( stand.axis[1], 1.5, stand.axis[1] ); + VectorScale( stand.axis[2], 1.5, stand.axis[2] ); + +//----(SA) modified + if ( cent->currentState.frame ) { + CG_PositionEntityOnTag( &ent, &stand, va( "tag_stand%d", cent->currentState.frame ), 0, NULL ); + } else { + CG_PositionEntityOnTag( &ent, &stand, "tag_stand", 0, NULL ); + } +//----(SA) end + + VectorCopy( ent.origin, ent.oldorigin ); + ent.nonNormalizedAxes = qtrue; + + } else { // then default to laying it on it's side + if ( weaponInfo->droppedAnglesHack ) { + cent->lerpAngles[2] += 90; + } + + AnglesToAxis( cent->lerpAngles, ent.axis ); + + // increase the size of the weapons when they are presented as items + VectorScale( ent.axis[0], 1.5, ent.axis[0] ); + VectorScale( ent.axis[1], 1.5, ent.axis[1] ); + VectorScale( ent.axis[2], 1.5, ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + if ( es->eFlags & EF_SPINNING ) { // spinning will override the angles set by a stand + if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air) + VectorCopy( cg.autoAnglesSlow, cent->lerpAngles ); + VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles ); + } else { + VectorCopy( cent->lastLerpAngles, cent->lerpAngles ); // make a tossed weapon sit on the ground in a position that matches how it was yawed + } + } + } + + } else { + AnglesToAxis( cent->lerpAngles, ent.axis ); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + if ( es->eFlags & EF_SPINNING ) { // spinning will override the angles set by a stand + VectorCopy( cg.autoAnglesSlow, cent->lerpAngles ); + AxisCopy( cg.autoAxisSlow, ent.axis ); + } + } + + + if ( es->modelindex2 ) { // modelindex2 was specified for the ent, meaning it probably has an alternate model (as opposed to the one in the itemlist) + // try to load it first, and if it fails, default to the itemlist model + ent.hModel = cgs.gameModels[ es->modelindex2 ]; + } else { + //if( item->giType == IT_WEAPON && cg_items[es->modelindex].models[2]) // check if there's a specific model for weapon pickup placement + // ent.hModel = cg_items[es->modelindex].models[2]; + if ( item->giType == IT_WEAPON ) { + ent.hModel = cg_weapons[item->giTag].weaponModel[W_PU_MODEL].model; + + if ( item->giTag == WP_AMMO ) { + if ( cent->currentState.density == 2 ) { + ent.customShader = cg_weapons[item->giTag].modModels[0]; + } + } + } else { + ent.hModel = cg_items[es->modelindex].models[0]; + } + } + + //----(SA) find midpoint for highlight corona. + // Can't do it when item is registered since it wouldn't know about replacement model + if ( !( cent->usehighlightOrigin ) ) { + vec3_t mins, maxs, offset; + int i; + + trap_R_ModelBounds( ent.hModel, mins, maxs ); // get bounds + + for ( i = 0 ; i < 3 ; i++ ) { + offset[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] ); // find object-space center + } + + VectorCopy( cent->lerpOrigin, cent->highlightOrigin ); // set 'midpoint' to origin + + for ( i = 0 ; i < 3 ; i++ ) { // adjust midpoint by offset and orientation + cent->highlightOrigin[i] += offset[0] * ent.axis[0][i] + + offset[1] * ent.axis[1][i] + + offset[2] * ent.axis[2][i]; + } + + cent->usehighlightOrigin = qtrue; + } + + // items without glow textures need to keep a minimum light value so they are always visible +// if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) { + ent.renderfx |= RF_MINLIGHT; +// } + + // highlighting items the player looks at + if ( cg_drawCrosshairPickups.integer ) { + + + if ( cg_drawCrosshairPickups.integer == 2 ) { // '2' is 'force highlights' + highlight = qtrue; + } + + if ( CG_PlayerSeesItem( &cg.predictedPlayerState, es, cg.time, item->giType ) ) { + highlight = qtrue; + + if ( item->giType == IT_TREASURE ) { + trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qtrue ); //----(SA) add corona to treasure + } + } else { + if ( item->giType == IT_TREASURE ) { + trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qfalse ); //----(SA) "empty corona" for proper fades + } + } + +//----(SA) added fixed item highlight fading + + if ( highlight ) { + if ( !cent->highlighted ) { + cent->highlighted = qtrue; + cent->highlightTime = cg.time; + } + ent.hilightIntensity = ( ( cg.time - cent->highlightTime ) / 250.0f ) * highlightFadeScale; // .25 sec to brighten up + } else { + if ( cent->highlighted ) { + cent->highlighted = qfalse; + cent->highlightTime = cg.time; + } + ent.hilightIntensity = 1.0f - ( ( cg.time - cent->highlightTime ) / 1000.0f ) * highlightFadeScale; // 1 sec to dim down (diff in time causes problems if you quickly flip to/away from looking at the item) + } + + if ( ent.hilightIntensity < 0.25f ) { // leave a minlight + ent.hilightIntensity = 0.25f; + } + if ( ent.hilightIntensity > 1 ) { + ent.hilightIntensity = 1.0; + } + } +//----(SA) end + + + // add to refresh list + trap_R_AddRefEntityToScene( &ent ); +} + +//============================================================================ + +/* +=============== +CG_Bomb +=============== +*/ + +static void CG_Bomb( centity_t *cent ) { + refEntity_t ent, beam; + entityState_t *s1; + const weaponInfo_t *weapon; + vec3_t end; + trace_t trace; + + memset( &ent, 0, sizeof( ent ) ); + + s1 = ¢->currentState; + + weapon = &cg_weapons[WP_TRIPMINE]; + + VectorCopy( s1->origin2, ent.axis[0] ); + PerpendicularVector( ent.axis[1], ent.axis[0] ); + CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); + + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + ent.hModel = weapon->missileModel; + ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; + + CG_AddRefEntityWithPowerups( &ent, s1->powerups, TEAM_FREE, s1, vec3_origin ); + + + memset( &beam, 0, sizeof( beam ) ); + + VectorCopy( cent->lerpOrigin, beam.origin ); + VectorMA( cent->lerpOrigin, 4096, s1->origin2, end ); + + + trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, NULL, NULL, 0, MASK_SHOT ); + + VectorCopy( trace.endpos, beam.oldorigin ); + + beam.reType = RT_RAIL_CORE; + beam.renderfx = RF_NOSHADOW; + beam.customShader = cgs.media.railCoreShader; + beam.shaderRGBA[0] = 255; + beam.shaderRGBA[1] = 0; + beam.shaderRGBA[2] = 0; + beam.shaderRGBA[3] = 255; + + + AxisClear( beam.axis ); + + trap_R_AddRefEntityToScene( &beam ); +} + +/* +=============== +CG_Smoker +=============== +*/ +static void CG_Smoker( centity_t *cent ) { + // this ent has some special setting up + // time = speed + // time2 = duration + // angles2[0] = start_size + // angles2[1] = end_size + // angles2[2] = wait + // dl_intensity = health + // constantLight = delay + // origin2 = normal to emit particles along + + if ( cg.time - cent->highlightTime > cent->currentState.constantLight ) { + // FIXME: make this framerate independant? + cent->highlightTime = cg.time; // fire a particle this frame + + if ( cent->currentState.modelindex2 ) { + CG_ParticleSmoke( cgs.gameShaders[cent->currentState.modelindex2], cent ); + } else if ( cent->currentState.density == 3 ) { // cannon + CG_ParticleSmoke( cgs.media.smokePuffShaderdirty, cent ); + } else if ( !( cent->currentState.density ) ) { + CG_ParticleSmoke( cgs.media.smokePuffShader, cent ); + } else { + CG_ParticleSmoke( cgs.media.smokePuffShader, cent ); + } + } + + cent->lastTrailTime = cg.time; // time we were last received at the client +} + +/* +=============== +CG_Missile +=============== +*/ +static void CG_DrawMineMarkerFlag( centity_t *cent, refEntity_t *ent, const weaponInfo_t *weapon ) { + entityState_t *s1; + + s1 = ¢->currentState; + + ent->hModel = cent->currentState.otherEntityNum2 ? weapon->modModels[1] : weapon->modModels[0]; + + ent->origin[2] += 8; + ent->oldorigin[2] += 8; + + // 20 frames + if ( cg.time >= cent->lerpFrame.frameTime ) { + cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime; + cent->lerpFrame.oldFrame = cent->lerpFrame.frame; + + while ( cg.time >= cent->lerpFrame.frameTime ) { + cent->lerpFrame.frameTime += 50; // 1000 / fps (which is 20) + cent->lerpFrame.frame++; + + if ( cent->lerpFrame.frame >= 20 ) { + cent->lerpFrame.frame = 0; + } + } + } + + if ( cent->lerpFrame.frameTime == cent->lerpFrame.oldFrameTime ) { + cent->lerpFrame.backlerp = 0; + } else { + cent->lerpFrame.backlerp = 1.0 - (float)( cg.time - cent->lerpFrame.oldFrameTime ) / ( cent->lerpFrame.frameTime - cent->lerpFrame.oldFrameTime ); + } + + ent->frame = cent->lerpFrame.frame + s1->frame; // offset + if ( ent->frame >= 20 ) { + ent->frame -= 20; + } + + ent->oldframe = cent->lerpFrame.oldFrame + s1->frame; // offset + if ( ent->oldframe >= 20 ) { + ent->oldframe -= 20; + } + + ent->backlerp = cent->lerpFrame.backlerp; +} + +extern void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ); + +static void CG_Missile( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + const weaponInfo_t *weapon; + + s1 = ¢->currentState; + if ( s1->weapon > WP_NUM_WEAPONS ) { + s1->weapon = 0; + } + weapon = &cg_weapons[s1->weapon]; + + // calculate the axis + VectorCopy( s1->angles, cent->lerpAngles ); + + if ( s1->weapon == WP_SMOKE_BOMB ) { + // Arnout: the smoke effect + CG_RenderSmokeGrenadeSmoke( cent, weapon ); + } else if ( s1->weapon == WP_SATCHEL && s1->clientNum == cg.snap->ps.clientNum ) { + // rain - use snap client number so that the detonator works + // right when spectating (#218) + + cg.satchelCharge = cent; + } else if ( s1->weapon == WP_ARTY && s1->otherEntityNum2 && s1->teamNum == cgs.clientinfo[ cg.clientNum ].team ) { + VectorCopy( cent->lerpOrigin, cg.artilleryRequestPos[s1->clientNum] ); + cg.artilleryRequestTime[s1->clientNum] = cg.time; + } + + // add trails + if ( cent->currentState.eType == ET_FP_PARTS + || cent->currentState.eType == ET_FIRE_COLUMN + || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE + || cent->currentState.eType == ET_RAMJET ) { + CG_RocketTrail( cent, NULL ); + } else if ( weapon->missileTrailFunc ) { + weapon->missileTrailFunc( cent, weapon ); + } + + // add dynamic light + if ( weapon->missileDlight ) { + //% trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, + //% weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2], 0 ); + trap_R_AddLightToScene( cent->lerpOrigin, weapon->missileDlight, 1.0, + weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2], 0, 0 ); + } + +//----(SA) whoops, didn't mean to check it in with the missile flare + + // add missile sound + if ( weapon->missileSound ) { + if ( cent->currentState.weapon == WP_GPG40 || cent->currentState.weapon == WP_M7 ) { + if ( !cent->currentState.effect1Time ) { + int flytime = cg.time - cent->currentState.pos.trTime; + + if ( flytime > 300 ) { + // have a quick fade in so we don't have a pop + vec3_t velocity; + int volume = flytime > 375 ? 255 : ( 75.f / ( (float)flytime - 300.f ) ) * 255; + + BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity, qfalse, -1 ); + trap_S_AddLoopingSound( cent->lerpOrigin, velocity, weapon->missileSound, volume, 0 ); + } + } + } else { + vec3_t velocity; + + BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity, qfalse, -1 ); + trap_S_AddLoopingSound( cent->lerpOrigin, velocity, weapon->missileSound, 255, 0 ); + } + } + + // DHM - Nerve :: Don't tick until armed + if ( cent->currentState.weapon == WP_DYNAMITE ) { + if ( cent->currentState.teamNum < 4 ) { + vec3_t velocity; + + BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity, qfalse, -1 ); + trap_S_AddLoopingSound( cent->lerpOrigin, velocity, weapon->spindownSound, 255, 0 ); + } + } + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + // flicker between two skins + ent.skinNum = cg.clientFrame & 1; + + if ( cent->currentState.eType == ET_FP_PARTS ) { + ent.hModel = cgs.gameModels[cent->currentState.modelindex]; + } else if ( cent->currentState.eType == ET_EXPLO_PART ) { + ent.hModel = cgs.gameModels[cent->currentState.modelindex]; + } else if ( cent->currentState.eType == ET_FLAMEBARREL ) { + ent.hModel = cgs.media.flamebarrel; + } else if ( cent->currentState.eType == ET_FIRE_COLUMN || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE ) { + // it may have a model sometime in the future + ent.hModel = 0; + } else if ( cent->currentState.eType == ET_RAMJET ) { + ent.hModel = 0; + // ent.hModel = cgs.gameModels[cent->currentState.modelindex]; + } else { + team_t missileTeam = cent->currentState.weapon == WP_LANDMINE ? cent->currentState.teamNum % 4 : cent->currentState.teamNum; + + ent.hModel = weapon->missileModel; + + if ( missileTeam == TEAM_ALLIES ) { + ent.customSkin = weapon->missileAlliedSkin; + } else if ( missileTeam == TEAM_AXIS ) { + ent.customSkin = weapon->missileAxisSkin; + } + } + ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW; + + if ( cent->currentState.weapon == WP_LANDMINE ) { + if ( cgs.clientinfo[ cg.clientNum ].team == TEAM_SPECTATOR ) { + return; + } + + VectorCopy( ent.origin, ent.lightingOrigin ); + ent.renderfx |= RF_LIGHTING_ORIGIN; + + if ( cent->currentState.teamNum < 4 ) { + ent.origin[2] -= 8; + ent.oldorigin[2] -= 8; + + if ( ( cgs.clientinfo[cg.snap->ps.clientNum].team != ( !cent->currentState.otherEntityNum2 ? TEAM_ALLIES : TEAM_AXIS ) ) ) { + if ( cent->currentState.density - 1 == cg.snap->ps.clientNum ) { + //ent.customShader = cgs.media.genericConstructionShaderModel; + ent.customShader = cgs.media.genericConstructionShader; + } else if ( !cent->currentState.modelindex2 ) { + // see if we have the skill to see them and are close enough + if ( cgs.clientinfo[cg.snap->ps.clientNum].skill[SK_BATTLE_SENSE] >= 4 ) { + vec_t distSquared = DistanceSquared( cent->lerpOrigin, cg.predictedPlayerEntity.lerpOrigin ); + + if ( distSquared > Square( 256 ) ) { + return; + } else { + //ent.customShader = cgs.media.genericConstructionShaderModel; + ent.customShader = cgs.media.genericConstructionShader; + } + } else { + return; + } + } else { + CG_DrawMineMarkerFlag( cent, &ent, weapon ); + } + } else { + CG_DrawMineMarkerFlag( cent, &ent, weapon ); + /*if ( !cent->highlighted ) { + cent->highlighted = qtrue; + cent->highlightTime = cg.time; + } + + ent.hilightIntensity = 0.5f * sin((cg.time-cent->highlightTime)/1000.f) + 1.f;*/ + } + } + + if ( cent->currentState.teamNum >= 8 ) { + ent.origin[2] -= 8; + ent.oldorigin[2] -= 8; + } + } + + // convert direction of travel into axis + if ( cent->currentState.weapon == WP_MORTAR_SET ) { + vec3_t delta; + + if ( VectorCompare( cent->rawOrigin, vec3_origin ) ) { + VectorSubtract( cent->lerpOrigin, s1->pos.trBase, delta ); + VectorCopy( cent->lerpOrigin, cent->rawOrigin ); + } else { + VectorSubtract( cent->lerpOrigin, cent->rawOrigin, delta ); + if ( !VectorCompare( cent->lerpOrigin, cent->rawOrigin ) ) { + VectorCopy( cent->lerpOrigin, cent->rawOrigin ); + } + } + if ( VectorNormalize2( delta, ent.axis[0] ) == 0 ) { + ent.axis[0][2] = 1; + } + } else if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { + ent.axis[0][2] = 1; + } + + // spin as it moves + if ( s1->pos.trType != TR_STATIONARY ) { + RotateAroundDirection( ent.axis, cg.time / 4 ); + } else { + RotateAroundDirection( ent.axis, s1->time ); + } + + // Rafael + // Added this since it may be a propExlosion + if ( ent.hModel ) { + // add to refresh list, possibly with quad glow + CG_AddRefEntityWithPowerups( &ent, s1->powerups, TEAM_FREE, s1, vec3_origin ); + } + +} + +// DHM - Nerve :: capture and hold flag +static animation_t multi_flagpoleAnims[] = { + { 0, "", 0, 1, 0, 1000 / 15, 1000 / 15 }, // (no flags, idle) + { 0, "", 0, 15, 0, 1000 / 15, 1000 / 15 }, // (axis flag rising) + { 0, "", 490, 15, 0, 1000 / 15, 1000 / 15 }, // (american flag rising) + { 0, "", 20, 211, 211, 1000 / 15, 1000 / 15 }, // (axis flag raised) + { 0, "", 255, 211, 211, 1000 / 15, 1000 / 15 }, // (american flag raised) + { 0, "", 235, 15, 0, 1000 / 15, 1000 / 15 }, // (axis switching to american) + { 0, "", 470, 15, 0, 1000 / 15, 1000 / 15 }, // (american switching to axis) + { 0, "", 510, 15, 0, 1000 / 15, 1000 / 15 }, // (axis flag falling) + { 0, "", 530, 15, 0, 1000 / 15, 1000 / 15 } // (american flag falling) +}; + +// dhm - end + +extern void CG_RunLerpFrame( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ); + + +/* +============== +CG_TrapSetAnim +============== +*/ +static void CG_TrapSetAnim( centity_t *cent, lerpFrame_t *lf, int newAnim ) { + // transition animation + lf->animationNumber = cent->currentState.frame; + + // DHM - Nerve :: teamNum specifies which set of animations to use (only 1 exists right now) + switch ( cent->currentState.teamNum ) { + case 1: + lf->animation = &multi_flagpoleAnims[ cent->currentState.frame ]; + break; + default: + return; + } + + lf->animationTime = lf->frameTime + lf->animation->initialLerp; +} + +/* +============== +CG_Trap + // TODO: change from 'trap' to something else. 'trap' is a misnomer. it's actually used for other stuff too +============== +*/ +static void CG_Trap( centity_t *cent ) { + refEntity_t ent; + entityState_t *cs; + lerpFrame_t *traplf; + + memset( &ent, 0, sizeof( ent ) ); + + cs = ¢->currentState; + + traplf = ¢->lerpFrame; + + // initial setup + if ( !traplf->oldFrameTime ) { + traplf->frameTime = + traplf->oldFrameTime = cg.time; + + CG_TrapSetAnim( cent, traplf, cs->frame ); + + traplf->frame = + traplf->oldFrame = traplf->animation->firstFrame; + } + + // transition to new anim if requested + if ( ( traplf->animationNumber != cs->frame ) || !traplf->animation ) { + CG_TrapSetAnim( cent, traplf, cs->frame ); + } + + CG_RunLerpFrame( cent, NULL, traplf, 0, 1 ); // use existing lerp code rather than re-writing + + ent.frame = traplf->frame; + ent.oldframe = traplf->oldFrame; + ent.backlerp = traplf->backlerp; + + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + ent.hModel = cgs.gameModels[cs->modelindex]; + + AnglesToAxis( cent->lerpAngles, ent.axis ); + + trap_R_AddRefEntityToScene( &ent ); + + memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); +} +//----(SA) end + + +/* +============== +CG_Corona +============== +*/ +static void CG_Corona( centity_t *cent ) { + trace_t tr; + int r, g, b; + int dli; + qboolean visible = qfalse, + behind = qfalse, + toofar = qfalse; + + float dot, dist; + vec3_t dir; + + if ( cg_coronas.integer == 0 ) { // if set to '0' no coronas + return; + } + + dli = cent->currentState.dl_intensity; + r = dli & 255; + g = ( dli >> 8 ) & 255; + b = ( dli >> 16 ) & 255; + + // only coronas that are in your PVS are being added + + VectorSubtract( cg.refdef_current->vieworg, cent->lerpOrigin, dir ); + + dist = VectorNormalize2( dir, dir ); + if ( dist > cg_coronafardist.integer ) { // performance variable cg_coronafardist will keep down super long traces + toofar = qtrue; + } + + dot = DotProduct( dir, cg.refdef_current->viewaxis[0] ); + if ( dot >= -0.6 ) { // assumes ~90 deg fov (SA) changed value to 0.6 (screen corner at 90 fov) + behind = qtrue; // use the dot to at least do trivial removal of those behind you. + } + // yeah, I could calc side planes to clip against, but would that be worth it? (much better than dumb dot>= thing?) + +// CG_Printf("dot: %f\n", dot); + + if ( cg_coronas.integer == 2 ) { // if set to '2' trace everything + behind = qfalse; + toofar = qfalse; + } + + + if ( !behind && !toofar ) { + CG_Trace( &tr, cg.refdef_current->vieworg, NULL, NULL, cent->lerpOrigin, -1, MASK_SOLID | CONTENTS_BODY ); // added blockage by players. not sure how this is going to be since this is their bb, not their model (too much blockage) + + if ( tr.fraction == 1 ) { + visible = qtrue; + } + + trap_R_AddCoronaToScene( cent->lerpOrigin, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)cent->currentState.density / 255.0f, cent->currentState.number, visible ); + } +} + + +/* +============== +CG_Efx +============== +*/ +static void CG_SpotlightEfx( centity_t *cent ) { + vec3_t targetpos, normalized_direction, direction; + float dist, fov = 90; + vec4_t color = {1, 1, 1, .1}; + int splinetarget = 0; + char *cs; + + + VectorCopy( cent->currentState.origin2, targetpos ); + + splinetarget = cent->overheatTime; + + if ( !splinetarget ) { + cs = (char *)CG_ConfigString( CS_SPLINES + cent->currentState.density ); + cent->overheatTime = splinetarget = CG_LoadCamera( va( "cameras/%s.camera", cs ) ); + if ( splinetarget != -1 ) { + trap_startCamera( splinetarget, cg.time ); + } + } else { + vec3_t angles; + if ( splinetarget != -1 ) { + if ( trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov ) ) { + + } else { // loop + trap_startCamera( splinetarget, cg.time ); + trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov ); + } + } + } + + + normalized_direction[0] = direction[0] = targetpos[0] - cent->currentState.origin[0]; + normalized_direction[1] = direction[1] = targetpos[1] - cent->currentState.origin[1]; + normalized_direction[2] = direction[2] = targetpos[2] - cent->currentState.origin[2]; + + dist = VectorNormalize( normalized_direction ); + + if ( dist == 0 ) { + return; + } + + CG_Spotlight( cent, color, cent->currentState.origin, normalized_direction, 999, 2048, 10, fov, 0 ); +} + + +//----(SA) adding func_explosive + +/* +=============== +CG_Explosive + This is currently almost exactly the same as CG_Mover + It's split out so that any changes or experiments are + unattached to anything else. +=============== +*/ +static void CG_Explosive( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); +// VectorCopy( cent->lerpOrigin, ent.oldorigin); +// VectorCopy( ent.origin, cent->lerpOrigin); + AnglesToAxis( cent->lerpAngles, ent.axis ); + + ent.renderfx = RF_NOSHADOW; + + // get the model, either as a bmodel or a modelindex + if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex]; + } else { + ent.hModel = cgs.gameModels[s1->modelindex]; + } + + // add to refresh list + // trap_R_AddRefEntityToScene(&ent); + + // add the secondary model + if ( s1->modelindex2 ) { + ent.skinNum = 0; + ent.hModel = cgs.gameModels[s1->modelindex2]; + trap_R_AddRefEntityToScene( &ent ); + } else { + trap_R_AddRefEntityToScene( &ent ); + } + +} + +/* +=============== +CG_Constructible + This is currently almost exactly the same as CG_Mover + It's split out so that any changes or experiments are + unattached to anything else. +=============== +*/ +static void CG_Constructible( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); +// VectorCopy( ent.origin, cent->lerpOrigin); + AnglesToAxis( cent->lerpAngles, ent.axis ); + + ent.renderfx = RF_NOSHADOW; + +// CG_Printf( "Adding constructible: %s\n", CG_ConfigString( CS_OID_NAMES + cent->currentState.otherEntityNum2 ) ); + + if ( s1->modelindex ) { + // get the model, either as a bmodel or a modelindex + //if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex]; + //} else { + // ent.hModel = cgs.gameModels[s1->modelindex]; + //} + + // add to refresh list + // trap_R_AddRefEntityToScene(&ent); + + // ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0xff; + // ent.shaderRGBA[3] = s1->density; + + //if( s1->angles2[0] < 255 ) + //if( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) + // ent.customShader = cgs.media.genericConstructionShaderBrush; + + trap_R_AddRefEntityToScene( &ent ); + } + + // add the secondary model + if ( s1->modelindex2 ) { + if ( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) { + //ent.customShader = cgs.media.genericConstructionShaderBrush; + ent.customShader = cgs.media.genericConstructionShader; + + /*switch( cent->currentState.frame ) { + case 1: trap_S_AddLoopingSound( cent->currentState.origin2, vec3_origin, cgs.media.buildSound[0], 255, 0 ); break; + case 2: trap_S_AddLoopingSound( cent->currentState.origin2, vec3_origin, cgs.media.buildSound[1], 255, 0 ); break; + case 3: trap_S_AddLoopingSound( cent->currentState.origin2, vec3_origin, cgs.media.buildSound[2], 255, 0 ); break; + case 4: trap_S_AddLoopingSound( cent->currentState.origin2, vec3_origin, cgs.media.buildSound[3], 255, 0 ); break; + }*/ + } + + //if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex2]; + //} else { + // ent.hModel = cgs.gameModels[s1->modelindex2]; + //} + trap_R_AddRefEntityToScene( &ent ); + } + //else + // trap_R_AddRefEntityToScene(&ent); +} + +/* +=============== +CG_Waypoint +=============== +*/ +/* +static void CG_Waypoint( centity_t *cent ) { + refEntity_t ent; + + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_SPRITE; + VectorCopy( cent->lerpOrigin, ent.origin ); + ent.origin[2] += 24; + VectorCopy( ent.origin, ent.oldorigin ); + ent.radius = 14; + + switch( cent->currentState.frame ) { + case WAYP_ATTACK: ent.customShader = cgs.media.waypointAttackShader; break; + case WAYP_DEFEND: ent.customShader = cgs.media.waypointDefendShader; break; + case WAYP_REGROUP: ent.customShader = cgs.media.waypointRegroupShader; break; + // TAT 8/29/2002 - Use the bot shader for bots + case WAYP_BOT: + ent.customShader = cgs.media.waypointBotShader; + break; + // TAT 1/13/2003 - and the queued shader for queued commands + case WAYP_BOTQUEUED: + ent.customShader = cgs.media.waypointBotQueuedShader; + break; + } + + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 255; + trap_R_AddRefEntityToScene(&ent); +} +*/ +/* +=============== +CG_ConstructibleMarker +=============== +*/ +/*static void CG_ConstructibleMarker( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset (&ent, 0, sizeof(ent)); + + VectorCopy( cent->lerpOrigin, ent.origin); + VectorCopy( cent->lerpOrigin, ent.oldorigin); + + // special shader if under construction + if( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) + ent.customShader = cgs.media.genericConstructionShader; + + // add the secondary model + if ( s1->modelindex2 ) { + AnglesToAxis( cent->lerpAngles, ent.axis ); + ent.skinNum = 0; + ent.hModel = cgs.gameModels[s1->modelindex2]; + ent.frame = s1->frame; + if( s1->effect1Time ) + ent.customSkin = cgs.gameModelSkins[s1->effect1Time]; + trap_R_AddRefEntityToScene(&ent); + memcpy( ¢->refEnt, &ent, sizeof(refEntity_t) ); + } else { + AnglesToAxis( vec3_origin, ent.axis ); + trap_R_AddRefEntityToScene(&ent); + } +}*/ + +//----(SA) done + +// declaration for add bullet particles (might as well stick this one in a .h file I think) +extern void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ); + +/* +=============== +CG_Mover +=============== +*/ +static void CG_Mover( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + AnglesToAxis( cent->lerpAngles, ent.axis ); + + ent.renderfx = 0; + ent.skinNum = 0; + + // get the model, either as a bmodel or a modelindex + if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex]; + } else { + ent.hModel = cgs.gameModels[s1->modelindex]; + } + + // Rafael + // testing for mike to get movers to scale + if ( cent->currentState.density & 1 ) { + VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] ); + VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] ); + VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] ); + ent.nonNormalizedAxes = qtrue; + } + + + if ( cent->currentState.eType == ET_ALARMBOX ) { + ent.renderfx |= RF_MINLIGHT; + } + + + // special shader if under construction + if ( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) { + /*if( cent->currentState.solid == SOLID_BMODEL ) { + ent.customShader = cgs.media.genericConstructionShaderBrush; + } else { + ent.customShader = cgs.media.genericConstructionShaderModel; + }*/ + ent.customShader = cgs.media.genericConstructionShader; + } + + // add the secondary model + if ( s1->modelindex2 && !( cent->currentState.density & 2 ) ) { + ent.hModel = cgs.gameModels[s1->modelindex2]; + ent.frame = s1->frame; + + if ( s1->torsoAnim ) { + if ( cg.time >= cent->lerpFrame.frameTime ) { + cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime; + cent->lerpFrame.oldFrame = cent->lerpFrame.frame; + + while ( cg.time >= cent->lerpFrame.frameTime ) { + cent->lerpFrame.frameTime += s1->weapon; + cent->lerpFrame.frame++; + + if ( cent->lerpFrame.frame >= s1->legsAnim + s1->torsoAnim ) { + cent->lerpFrame.frame = s1->legsAnim; + } + } + } + + if ( cent->lerpFrame.frameTime == cent->lerpFrame.oldFrameTime ) { + cent->lerpFrame.backlerp = 0; + } else { + cent->lerpFrame.backlerp = 1.0 - (float)( cg.time - cent->lerpFrame.oldFrameTime ) / ( cent->lerpFrame.frameTime - cent->lerpFrame.oldFrameTime ); + } + + ent.frame = cent->lerpFrame.frame + s1->frame; // offset + if ( ent.frame >= s1->legsAnim + s1->torsoAnim ) { + ent.frame -= s1->torsoAnim; + } + + ent.oldframe = cent->lerpFrame.oldFrame + s1->frame; // offset + if ( ent.oldframe >= s1->legsAnim + s1->torsoAnim ) { + ent.oldframe -= s1->torsoAnim; + } + + ent.backlerp = cent->lerpFrame.backlerp; + } + + if ( cent->trailTime ) { + cent->lerpFrame.oldFrame = cent->lerpFrame.frame; + cent->lerpFrame.frame = s1->legsAnim; + + cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime; + cent->lerpFrame.frameTime = cg.time; + + ent.oldframe = ent.frame; + ent.frame = s1->legsAnim; + ent.backlerp = 0; + } + + if ( cent->nextState.animMovetype != s1->animMovetype ) { + cent->trailTime = 1; + } else { + cent->trailTime = 0; + } + + trap_R_AddRefEntityToScene( &ent ); + memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); + } else { + trap_R_AddRefEntityToScene( &ent ); + } + + // alarm box spark effects +/* if( cent->currentState.eType == ET_ALARMBOX) { + if(cent->currentState.frame == 2 ) { // i'm dead + if(rand()%50 == 1) { + vec3_t angNorm; // normalized angles + VectorNormalize2(cent->lerpAngles, angNorm); + // (origin, dir, speed, duration, count, 'randscale') + CG_AddBulletParticles( cent->lerpOrigin, angNorm, 2, 800, 4, 16.0f ); + trap_S_StartSound (NULL, cent->currentState.number, CHAN_AUTO, cgs.media.sparkSounds ); + } + } + }*/ +} + +void CG_Mover_PostProcess( centity_t* cent ) { + refEntity_t mg42base; + refEntity_t mg42upper; + refEntity_t mg42gun; + refEntity_t player; + refEntity_t flash; + vec_t* angles; + int i; + + if ( !( cent->currentState.density & 4 ) ) { // mounted gun + return; + } + + + if ( cg.snap->ps.eFlags & EF_MOUNTEDTANK && cg_entities[cg.snap->ps.clientNum].tagParent == cent->currentState.effect3Time ) { + i = cg.snap->ps.clientNum; + } else { + for ( i = 0; i < MAX_CLIENTS; i++ ) { + // Gordon: is this entity mounted on a tank, and attached to _OUR_ turret entity (which could be us) + if ( cg_entities[i].currentValid && ( cg_entities[i].currentState.eFlags & EF_MOUNTEDTANK ) ) { + if ( cg_entities[i].tagParent == cent->currentState.effect3Time ) { + break; + } + } + } + } + + if ( i != MAX_CLIENTS ) { + if ( i != cg.snap->ps.clientNum ) { + angles = cg_entities[i].lerpAngles; + } else { + angles = cg.predictedPlayerState.viewangles; + } + } else { + angles = vec3_origin; + } + + cg_entities[cent->currentState.effect3Time].tankparent = cent - cg_entities; + CG_AttachBitsToTank( &cg_entities[cent->currentState.effect3Time], &mg42base, &mg42upper, &mg42gun, &player, &flash, angles, "tag_player", cent->currentState.density & 8 ? qtrue : qfalse ); + + // Gordon: if we (or someone we're spectating) is on this tank, recalc our view values + if ( cg.snap->ps.eFlags & EF_MOUNTEDTANK ) { + centity_t* tank = &cg_entities[cg_entities[cg.snap->ps.clientNum].tagParent]; + if ( tank == &cg_entities[cent->currentState.effect3Time] ) { + CG_CalcViewValues(); + } + } + + VectorCopy( mg42base.origin, mg42base.lightingOrigin ); + VectorCopy( mg42base.origin, mg42base.oldorigin ); + + VectorCopy( mg42upper.origin, mg42upper.lightingOrigin ); + VectorCopy( mg42upper.origin, mg42upper.oldorigin ); + + VectorCopy( mg42gun.origin, mg42gun.lightingOrigin ); + VectorCopy( mg42gun.origin, mg42gun.oldorigin ); + + trap_R_AddRefEntityToScene( &mg42base ); + + if ( i != cg.snap->ps.clientNum || cg.renderingThirdPerson ) { + trap_R_AddRefEntityToScene( &mg42upper ); + trap_R_AddRefEntityToScene( &mg42gun ); + } +} + + + +/* +=============== +CG_Beam_2 + +Gordon: new beam entity, for rope like stuff... +=============== +*/ +void CG_Beam_2( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + vec3_t origin, origin2; + + s1 = ¢->currentState; + + BG_EvaluateTrajectory( &s1->pos, cg.time, origin, qfalse, s1->effect1Time ); + BG_EvaluateTrajectory( &s1->apos, cg.time, origin2, qfalse, s1->effect2Time ); + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + + VectorCopy( origin, ent.origin ); + VectorCopy( origin2, ent.oldorigin ); + +// CG_Printf( "O: %i %i %i OO: %i %i %i\n", (int)origin[0], (int)origin[1], (int)origin[2], (int)origin2[0], (int)origin2[1], (int)origin2[2] ); + AxisClear( ent.axis ); + ent.reType = RT_RAIL_CORE; + ent.customShader = cgs.gameShaders[s1->modelindex2]; + ent.radius = 8; + ent.frame = 2; + + VectorScale( cent->currentState.angles2, 255, ent.shaderRGBA ); + ent.shaderRGBA[3] = 255; + + // add to refresh list + trap_R_AddRefEntityToScene( &ent ); +} + +/* +=============== +CG_Beam + +Also called as an event +=============== +*/ +void CG_Beam( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( s1->pos.trBase, ent.origin ); + VectorCopy( s1->origin2, ent.oldorigin ); + + AxisClear( ent.axis ); + ent.reType = RT_RAIL_CORE; + + switch ( s1->legsAnim ) { + case 1: + ent.customShader = cgs.media.ropeShader; + break; + default: + ent.customShader = cgs.media.railCoreShader; + break; + } + + ent.shaderRGBA[0] = s1->angles2[0] * 255; + ent.shaderRGBA[1] = s1->angles2[1] * 255; + ent.shaderRGBA[2] = s1->angles2[2] * 255; + ent.shaderRGBA[3] = 255; + + ent.renderfx = RF_NOSHADOW; + + // add to refresh list + trap_R_AddRefEntityToScene( &ent ); +} + + +/* +=============== +CG_Portal +=============== +*/ +static void CG_Portal( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + + s1 = ¢->currentState; + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( s1->origin2, ent.oldorigin ); + ByteToDir( s1->eventParm, ent.axis[0] ); + PerpendicularVector( ent.axis[1], ent.axis[0] ); + + // negating this tends to get the directions like they want + // we really should have a camera roll value + VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] ); + + CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); + ent.reType = RT_PORTALSURFACE; + ent.frame = s1->frame; // rotation speed + ent.skinNum = s1->clientNum / 256.0 * 360; // roll offset + + // add to refresh list + trap_R_AddRefEntityToScene( &ent ); +} + +/* +=============== +CG_Prop +=============== +*/ +static void CG_Prop( centity_t *cent ) { + refEntity_t ent; + entityState_t *s1; + vec3_t angles; + float scale; + + s1 = ¢->currentState; + + // create the render entity + memset( &ent, 0, sizeof( ent ) ); + + if ( cg.renderingThirdPerson ) { + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + + ent.frame = s1->frame; + ent.oldframe = ent.frame; + ent.backlerp = 0; + } else + { + VectorCopy( cg.refdef_current->vieworg, ent.origin ); + VectorCopy( cg.refdefViewAngles, angles ); + + if ( cg.bobcycle & 1 ) { + scale = -cg.xyspeed; + } else { + scale = cg.xyspeed; + } + + // modify angles from bobbing + angles[ROLL] += scale * cg.bobfracsin * 0.005; + angles[YAW] += scale * cg.bobfracsin * 0.01; + angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005; + + VectorCopy( angles, cent->lerpAngles ); + + ent.frame = s1->frame; + ent.oldframe = ent.frame; + ent.backlerp = 0; + + if ( cent->currentState.density ) { + ent.frame = s1->frame + cent->currentState.density; + ent.oldframe = ent.frame - 1; + ent.backlerp = 1 - cg.frameInterpolation; + ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON; + + //CG_Printf ("frame %d oldframe %d\n", ent.frame, ent.oldframe); + } else if ( ent.frame ) { + ent.oldframe -= 1; + ent.backlerp = 1 - cg.frameInterpolation; + } else + { + ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON; + } + } + + AnglesToAxis( cent->lerpAngles, ent.axis ); + + ent.renderfx |= RF_NOSHADOW; + + // flicker between two skins (FIXME?) + ent.skinNum = ( cg.time >> 6 ) & 1; + + // get the model, either as a bmodel or a modelindex + if ( s1->solid == SOLID_BMODEL ) { + ent.hModel = cgs.inlineDrawModel[s1->modelindex]; + } else { + ent.hModel = cgs.gameModels[s1->modelindex]; + } + + // special shader if under construction + if ( cent->currentState.powerups == STATE_UNDERCONSTRUCTION ) { + /*if( cent->currentState.solid == SOLID_BMODEL ) { + ent.customShader = cgs.media.genericConstructionShaderBrush; + } else { + ent.customShader = cgs.media.genericConstructionShaderModel; + }*/ + ent.customShader = cgs.media.genericConstructionShader; + } + + // add the secondary model + if ( s1->modelindex2 ) { + ent.skinNum = 0; + ent.hModel = cgs.gameModels[s1->modelindex2]; + ent.frame = s1->frame; + trap_R_AddRefEntityToScene( &ent ); + memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); + } else { + trap_R_AddRefEntityToScene( &ent ); + } + +} + +typedef enum cabinetType_e { + CT_AMMO, + CT_HEALTH, + CT_MAX, +} cabinetType_t; + +#define MAX_CABINET_TAGS 6 +typedef struct cabinetTag_s { + const char* tagsnames[MAX_CABINET_TAGS]; + + const char* itemnames[MAX_CABINET_TAGS]; + qhandle_t itemmodels[MAX_CABINET_TAGS]; + + const char* modelName; + qhandle_t model; +} cabinetTag_t; + +cabinetTag_t cabinetInfo[CT_MAX] = { + { + { + "tag_ammo01", + "tag_ammo02", + "tag_ammo03", + "tag_ammo04", + "tag_ammo05", + "tag_ammo06", +/* "tag_obj1", + "tag_obj1", + "tag_obj1", + "tag_obj1", + "tag_obj1", + "tag_obj1",*/ + }, + { + "models/multiplayer/supplies/ammobox_wm.md3", + "models/multiplayer/supplies/ammobox_wm.md3", + "models/multiplayer/supplies/ammobox_wm.md3", + "models/multiplayer/supplies/ammobox_wm.md3", + "models/multiplayer/supplies/ammobox_wm.md3", + "models/multiplayer/supplies/ammobox_wm.md3", + }, + { + 0, 0, 0, 0, 0, 0 + }, + "models/mapobjects/supplystands/stand_ammo.md3", +// "models/mapobjects/blitz_sd/blitzbody.md3", + 0, + }, + { + { + "tag_Medikit_01", + "tag_Medikit_02", + "tag_Medikit_03", + "tag_Medikit_04", + "tag_Medikit_05", + "tag_Medikit_06", + }, + { + "models/multiplayer/supplies/healthbox_wm.md3", + "models/multiplayer/supplies/healthbox_wm.md3", + "models/multiplayer/supplies/healthbox_wm.md3", + "models/multiplayer/supplies/healthbox_wm.md3", + "models/multiplayer/supplies/healthbox_wm.md3", + "models/multiplayer/supplies/healthbox_wm.md3", + }, + { + 0, 0, 0, 0, 0, 0 + }, + "models/mapobjects/supplystands/stand_health.md3", + 0, + }, +}; + +/* +========================= +CG_Cabinet +========================= +*/ +void CG_Cabinet( centity_t* cent, cabinetType_t type ) { + refEntity_t cabinet; + refEntity_t mini_me; + int i, cnt; +// int k; + + if ( type < 0 || type >= CT_MAX ) { + return; + } + + memset( &cabinet, 0, sizeof( cabinet ) ); + memset( &mini_me, 0, sizeof( mini_me ) ); + + cabinet.hModel = cabinetInfo[type].model; +// cabinet.hModel = cabinetInfo[type].itemmodels[0]; + cabinet.frame = 0; + cabinet.oldframe = 0; + cabinet.backlerp = 0.f; + + VectorCopy( cent->lerpOrigin, cabinet.origin ); + VectorCopy( cabinet.origin, cabinet.oldorigin ); + VectorCopy( cabinet.origin, cabinet.lightingOrigin ); + cabinet.lightingOrigin[2] += 16; + cabinet.renderfx |= RF_MINLIGHT; + AnglesToAxis( cent->lerpAngles, cabinet.axis ); + + if ( cent->currentState.onFireStart == -9999 ) { + cnt = MAX_CABINET_TAGS; + } else { + cnt = MAX_CABINET_TAGS * ( cent->currentState.onFireStart / (float)cent->currentState.onFireEnd ); + if ( cnt == 0 && cent->currentState.onFireStart ) { + cnt = 1; + } + } + + for ( i = 0; i < cnt; i++ ) { + mini_me.hModel = cabinetInfo[type].itemmodels[i]; + + CG_PositionEntityOnTag( &mini_me, &cabinet, cabinetInfo[type].tagsnames[i], 0, NULL ); + + VectorCopy( mini_me.origin, mini_me.oldorigin ); + VectorCopy( mini_me.origin, mini_me.lightingOrigin ); + mini_me.renderfx |= RF_MINLIGHT; + + trap_R_AddRefEntityToScene( &mini_me ); + } + +/* for( k = 0; k < 3; k++ ) { + VectorScale( cabinet.axis[k], 2.f, cabinet.axis[k] ); + } + cabinet.nonNormalizedAxes = qtrue;*/ + + trap_R_AddRefEntityToScene( &cabinet ); +} + +void CG_SetupCabinets( void ) { + int i, j; + for ( i = 0; i < CT_MAX; i++ ) { + cabinetInfo[i].model = trap_R_RegisterModel( cabinetInfo[i].modelName ); + for ( j = 0; j < MAX_CABINET_TAGS; j++ ) { + cabinetInfo[i].itemmodels[j] = trap_R_RegisterModel( cabinetInfo[i].itemnames[j] ); + } + } +} + +/* +========================= +CG_AdjustPositionForMover + +Also called by client movement prediction code +========================= +*/ +void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t outDeltaAngles ) { + centity_t *cent; + vec3_t oldOrigin, origin, deltaOrigin; + vec3_t oldAngles, angles, deltaAngles; + vec3_t transpose[3]; + vec3_t matrix[3]; + vec3_t move, org, org2; + + if ( outDeltaAngles ) { + VectorClear( outDeltaAngles ); + } + + if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) { + VectorCopy( in, out ); + return; + } + + cent = &cg_entities[ moverNum ]; + + if ( cent->currentState.eType != ET_MOVER ) { + VectorCopy( in, out ); + return; + } + + if ( !( cent->currentState.eFlags & EF_PATH_LINK ) ) { + BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles, qtrue, cent->currentState.effect2Time ); + + BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles, qtrue, cent->currentState.effect2Time ); + + VectorSubtract( origin, oldOrigin, deltaOrigin ); + VectorSubtract( angles, oldAngles, deltaAngles ); + } else { + CG_AddLinkedEntity( cent, qtrue, fromTime ); + + VectorCopy( cent->lerpOrigin, oldOrigin ); + VectorCopy( cent->lerpAngles, oldAngles ); + + CG_AddLinkedEntity( cent, qtrue, toTime ); + + VectorSubtract( cent->lerpOrigin, oldOrigin, deltaOrigin ); + VectorSubtract( cent->lerpAngles, oldAngles, deltaAngles ); + + CG_AddLinkedEntity( cent, qtrue, cg.time ); + } + + BG_CreateRotationMatrix( deltaAngles, transpose ); + BG_TransposeMatrix( (const vec3_t *)transpose, matrix ); + + VectorSubtract( cg.snap->ps.origin, cent->lerpOrigin, org ); + + VectorCopy( org, org2 ); + BG_RotatePoint( org2, (const vec3_t *)matrix ); + VectorSubtract( org2, org, move ); + VectorAdd( deltaOrigin, move, deltaOrigin ); + + VectorAdd( in, deltaOrigin, out ); + if ( outDeltaAngles ) { + VectorCopy( deltaAngles, outDeltaAngles ); + } + + // F I X M E: origin change when on a rotating object + // Gordon: Added +} + + +/* +============================= +CG_InterpolateEntityPosition +============================= +*/ +static void CG_InterpolateEntityPosition( centity_t *cent ) { + vec3_t current, next; + float f; + + // it would be an internal error to find an entity that interpolates without + // a snapshot ahead of the current one + if ( cg.nextSnap == NULL ) { + // DHM - Nerve :: FIXME? There are some cases when in Limbo mode during a map restart + // that were tripping this error. + //CG_Error( "CG_InterpolateEntityPosition: cg.nextSnap == NULL" ); + //CG_Printf("CG_InterpolateEntityPosition: cg.nextSnap == NULL"); + return; + } + + f = cg.frameInterpolation; + + // this will linearize a sine or parabolic curve, but it is important + // to not extrapolate player positions if more recent data is available + BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next, qfalse, cent->currentState.effect2Time ); + + cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); + cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); + cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); + + BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current, qtrue, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next, qtrue, cent->currentState.effect2Time ); + + cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); + cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); + cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); + +} + +/* +=============== +CG_CalcEntityLerpPositions + +=============== +*/ +void CG_CalcEntityLerpPositions( centity_t *cent ) { + if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) { + CG_InterpolateEntityPosition( cent ); + return; + } + + // NERVE - SMF - fix for jittery clients in multiplayer + // first see if we can interpolate between two snaps for + // linear extrapolated clients + if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP && cent->currentState.number < MAX_CLIENTS ) { + CG_InterpolateEntityPosition( cent ); + return; + } + // -NERVE - SMF + + // backup + VectorCopy( cent->lerpAngles, cent->lastLerpAngles ); + VectorCopy( cent->lerpOrigin, cent->lastLerpOrigin ); + + // just use the current frame and evaluate as best we can + BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles, qtrue, cent->currentState.effect2Time ); + + // adjust for riding a mover if it wasn't rolled into the predicted + // player state + if ( cent != &cg.predictedPlayerEntity && !cg.showGameView ) { + CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, cg.snap->serverTime, cg.time, cent->lerpOrigin, NULL ); + } +} + +/* +=============== +CG_ProcessEntity +=============== +*/ +static void CG_ProcessEntity( centity_t *cent ) { + switch ( cent->currentState.eType ) { + default: + // ydnar: test for actual bad entity type + if ( cent->currentState.eType < 0 || cent->currentState.eType >= ET_EVENTS ) { + CG_Error( "Bad entity type: %i\n", cent->currentState.eType ); + } + break; + case ET_CONCUSSIVE_TRIGGER: + case ET_CAMERA: + case ET_INVISIBLE: + case ET_PUSH_TRIGGER: + case ET_TELEPORT_TRIGGER: + case ET_OID_TRIGGER: + case ET_AI_EFFECT: + case ET_EXPLOSIVE_INDICATOR: // NERVE - SMF + case ET_CONSTRUCTIBLE_INDICATOR: + case ET_TANK_INDICATOR: + case ET_TANK_INDICATOR_DEAD: + case ET_COMMANDMAP_MARKER: // this one should _never_ reach the client +#ifdef VISIBLE_TRIGGERS + case ET_TRIGGER_MULTIPLE: + case ET_TRIGGER_FLAGONLY: + case ET_TRIGGER_FLAGONLY_MULTIPLE: +#endif // VISIBLE_TRIGGERS + break; + case ET_SPEAKER: + CG_Speaker( cent ); + break; + case ET_GAMEMODEL: + case ET_MG42_BARREL: + case ET_FOOTLOCKER: + case ET_GENERAL: + case ET_AAGUN: + CG_General( cent ); + break; + case ET_CABINET_H: + CG_Cabinet( cent, CT_HEALTH ); + break; + case ET_CABINET_A: + CG_Cabinet( cent, CT_AMMO ); + break; + case ET_CORPSE: + case ET_PLAYER: + if ( cg.showGameView && cg.filtercams ) { + break; + } + CG_Player( cent ); + break; + case ET_ITEM: + CG_Item( cent ); + break; + case ET_MISSILE: + case ET_FLAMEBARREL: + case ET_FP_PARTS: + case ET_FIRE_COLUMN: + case ET_FIRE_COLUMN_SMOKE: + case ET_EXPLO_PART: + case ET_RAMJET: + CG_Missile( cent ); + break; + case ET_EF_SPOTLIGHT: + CG_SpotlightEfx( cent ); + break; + case ET_EXPLOSIVE: + CG_Explosive( cent ); + break; + case ET_CONSTRUCTIBLE: + CG_Constructible( cent ); + break; +/* case ET_WAYPOINT: + // TAT - 8/29/2002 - draw the botgoal indicator the same way you draw the waypoint flag + case ET_BOTGOAL_INDICATOR: + CG_Waypoint( cent ); + break;*/ +/* case ET_CONSTRUCTIBLE_MARKER: + CG_ConstructibleMarker( cent ); + break;*/ + case ET_TRAP: + CG_Trap( cent ); + break; + case ET_CONSTRUCTIBLE_MARKER: + case ET_ALARMBOX: + case ET_MOVER: + CG_Mover( cent ); + break; + case ET_PROP: + CG_Prop( cent ); + break; + case ET_BEAM: + CG_Beam( cent ); + break; + case ET_PORTAL: + CG_Portal( cent ); + break; + case ET_CORONA: + CG_Corona( cent ); + break; + case ET_BOMB: + CG_Bomb( cent ); + break; + case ET_BEAM_2: + CG_Beam_2( cent ); + break; + case ET_GAMEMANAGER: + cgs.gameManager = cent; + break; + case ET_SMOKER: + CG_Smoker( cent ); + break; + } +} + +/* +=============== +CG_AddCEntity + +=============== +*/ +void CG_AddCEntity( centity_t *cent ) { + // event-only entities will have been dealt with already + if ( cent->currentState.eType >= ET_EVENTS ) { + return; + } + + cent->processedFrame = cg.clientFrame; + + // calculate the current origin + CG_CalcEntityLerpPositions( cent ); + + // add automatic effects + CG_EntityEffects( cent ); + + // call the appropriate function which will add this entity to the view accordingly + CG_ProcessEntity( cent ); +} + +qboolean CG_AddLinkedEntity( centity_t *cent, qboolean ignoreframe, int atTime ) { + entityState_t *s1; + centity_t *centParent; + entityState_t *sParent; + vec3_t v; + + // event-only entities will have been dealt with already + if ( cent->currentState.eType >= ET_EVENTS ) { + return qtrue; + } + + if ( !ignoreframe && ( cent->processedFrame == cg.clientFrame ) && cg.mvTotalClients < 2 ) { + // already processed this frame + return qtrue; + } + + s1 = ¢->currentState; + centParent = &cg_entities[s1->torsoAnim]; + sParent = &( centParent->currentState ); + + // if parent isn't visible, then don't draw us + if ( !centParent->currentValid ) { + return qfalse; + } + + // make sure all parents are added first + if ( ( centParent->processedFrame != cg.clientFrame ) || ignoreframe ) { + if ( sParent->eFlags & EF_PATH_LINK ) { + if ( !CG_AddLinkedEntity( centParent, ignoreframe, atTime ) ) { + return qfalse; + } + } + } + + if ( !ignoreframe ) { + cent->processedFrame = cg.clientFrame; + } + + // Arnout: removed from here + //VectorCopy( cent->lerpAngles, cent->lastLerpAngles ); + //VectorCopy( cent->lerpOrigin, cent->lastLerpOrigin ); + + if ( !( sParent->eFlags & EF_PATH_LINK ) ) { + if ( sParent->pos.trType == TR_LINEAR_PATH ) { + int pos; + float frac; + + if ( !( cent->backspline = BG_GetSplineData( sParent->effect2Time, ¢->back ) ) ) { + return qfalse; + } + + cent->backdelta = sParent->pos.trDuration ? ( atTime - sParent->pos.trTime ) / ( (float)sParent->pos.trDuration ) : 0; + + if ( cent->backdelta < 0.f ) { + cent->backdelta = 0.f; + } else if ( cent->backdelta > 1.f ) { + cent->backdelta = 1.f; + } + + if ( cent->back ) { + cent->backdelta = 1 - cent->backdelta; + } + + pos = floor( cent->backdelta * ( MAX_SPLINE_SEGMENTS ) ); + if ( pos >= MAX_SPLINE_SEGMENTS ) { + pos = MAX_SPLINE_SEGMENTS - 1; + frac = cent->backspline->segments[pos].length; + } else { + frac = ( ( cent->backdelta * ( MAX_SPLINE_SEGMENTS ) ) - pos ) * cent->backspline->segments[pos].length; + } + + VectorMA( cent->backspline->segments[pos].start, frac, cent->backspline->segments[pos].v_norm, v ); + if ( sParent->apos.trBase[0] ) { + BG_LinearPathOrigin2( sParent->apos.trBase[0], ¢->backspline, ¢->backdelta, v, cent->back ); + } + + VectorCopy( v, cent->lerpOrigin ); + + if ( s1->angles2[0] ) { + BG_LinearPathOrigin2( s1->angles2[0], ¢->backspline, ¢->backdelta, v, cent->back ); + } + + VectorCopy( v, cent->origin2 ); + + if ( s1->angles2[0] < 0 ) { + VectorSubtract( v, cent->lerpOrigin, v ); + vectoangles( v, cent->lerpAngles ); + } else if ( s1->angles2[0] > 0 ) { + VectorSubtract( cent->lerpOrigin, v, v ); + vectoangles( v, cent->lerpAngles ); + } else { + VectorClear( cent->lerpAngles ); + } + + cent->moving = qtrue; + } else { + cent->moving = qfalse; + VectorCopy( s1->pos.trBase, cent->lerpOrigin ); + VectorCopy( s1->apos.trBase, cent->lerpAngles ); + } + } else { + if ( centParent->moving ) { + VectorCopy( centParent->origin2, v ); + + cent->back = centParent->back; + cent->backdelta = centParent->backdelta; + cent->backspline = centParent->backspline; + + VectorCopy( v, cent->lerpOrigin ); + + if ( s1->angles2[0] && cent->backspline ) { + BG_LinearPathOrigin2( s1->angles2[0], ¢->backspline, ¢->backdelta, v, cent->back ); + } + + VectorCopy( v, cent->origin2 ); + + if ( s1->angles2[0] < 0 ) { + VectorSubtract( v, cent->lerpOrigin, v ); + vectoangles( v, cent->lerpAngles ); + } else if ( s1->angles2[0] > 0 ) { + VectorSubtract( cent->lerpOrigin, v, v ); + vectoangles( v, cent->lerpAngles ); + } else { + VectorClear( cent->lerpAngles ); + } + cent->moving = qtrue; + + } else { + cent->moving = qfalse; + VectorCopy( s1->pos.trBase, cent->lerpOrigin ); + VectorCopy( s1->apos.trBase, cent->lerpAngles ); + } + } + + if ( !ignoreframe ) { + // add automatic effects + CG_EntityEffects( cent ); + + // call the appropriate function which will add this entity to the view accordingly + CG_ProcessEntity( cent ); + } + + return qtrue; +} + +void CG_RailTrail2( clientInfo_t *ci, vec3_t start, vec3_t end ); + +/* +================== +CG_AddEntityToTag +================== +*/ +qboolean CG_AddEntityToTag( centity_t *cent ) { + centity_t *centParent; + entityState_t *sParent; + refEntity_t ent; + + // event-only entities will have been dealt with already + if ( cent->currentState.eType >= ET_EVENTS ) { + return qfalse; + } + + if ( cent->processedFrame == cg.clientFrame && cg.mvTotalClients < 2 ) { + // already processed this frame + return qtrue; + } + + // calculate the current origin + CG_CalcEntityLerpPositions( cent ); + + if ( cent->tagParent < MAX_CLIENTS ) { + return qfalse; + } + + centParent = &cg_entities[cent->tagParent]; + sParent = ¢Parent->currentState; + + // if parent isn't visible, then don't draw us + if ( !centParent->currentValid ) { + return qfalse; + } + + // make sure all parents are added first + if ( centParent->processedFrame != cg.clientFrame ) { + if ( !CG_AddCEntity_Filter( centParent ) ) { + return qfalse; + } + } + + cent->processedFrame = cg.clientFrame; + + // start with default axis + AnglesToAxis( vec3_origin, ent.axis ); + + // get the tag position from parent + CG_PositionEntityOnTag( &ent, ¢Parent->refEnt, cent->tagName, 0, NULL ); + + VectorCopy( ent.origin, cent->lerpOrigin ); + // we need to add the child's angles to the tag angles + if ( cent->currentState.eType != ET_PLAYER ) { + if ( !cent->currentState.density ) { // this entity should rotate with it's parent, but can turn around using it's own angles + // Gordon: fixed to rotate about the object's axis, not the world + vec3_t mat[3], mat2[3]; + memcpy( mat2, ent.axis, sizeof( mat2 ) ); + BG_CreateRotationMatrix( cent->lerpAngles, mat ); + MatrixMultiply( mat, mat2, ent.axis ); + AxisToAngles( ent.axis, cent->lerpAngles ); + } else { // face our angles exactly + BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles, qtrue, cent->currentState.effect2Time ); + } + } + + // add automatic effects + CG_EntityEffects( cent ); + + // call the appropriate function which will add this entity to the view accordingly + CG_ProcessEntity( cent ); + + return qtrue; +} + +extern int cg_numSolidEntities; +extern centity_t *cg_solidEntities[]; +/* +=============== +CG_AddPacketEntities + +=============== +*/ + +qboolean CG_AddCEntity_Filter( centity_t* cent ) { + if ( cent->processedFrame == cg.clientFrame && cg.mvTotalClients < 2 ) { + return qtrue; + } + + if ( cent->currentState.eFlags & EF_PATH_LINK ) { + return CG_AddLinkedEntity( cent, qfalse, cg.time ); + } + + if ( cent->currentState.eFlags & EF_TAGCONNECT ) { + return CG_AddEntityToTag( cent ); + } + + CG_AddCEntity( cent ); + return qtrue; +} + +void CG_AddPacketEntities( void ) { + int num; + playerState_t *ps; + //int clcount; + + // set cg.frameInterpolation + if ( cg.nextSnap ) { + int delta; + + delta = ( cg.nextSnap->serverTime - cg.snap->serverTime ); + if ( delta == 0 ) { + cg.frameInterpolation = 0; + } else { + cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; + } + } else { + cg.frameInterpolation = 0; // actually, it should never be used, because + // no entities should be marked as interpolating + } + + // the auto-rotating items will all have the same axis + cg.autoAnglesSlow[0] = 0; + cg.autoAnglesSlow[1] = ( cg.time & 4095 ) * 360 / 4095.0; + cg.autoAnglesSlow[2] = 0; + + cg.autoAngles[0] = 0; + cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0; + cg.autoAngles[2] = 0; + + cg.autoAnglesFast[0] = 0; + cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; + cg.autoAnglesFast[2] = 0; + + AnglesToAxis( cg.autoAnglesSlow, cg.autoAxisSlow ); + AnglesToAxis( cg.autoAngles, cg.autoAxis ); + AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); + + // generate and add the entity from the playerstate + ps = &cg.predictedPlayerState; + BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse ); + CG_AddCEntity( &cg.predictedPlayerEntity ); + + // lerp the non-predicted value for lightning gun origins + CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] ); + + cg.satchelCharge = NULL; + + // Gordon: changing to a single loop, child will request that their parents are added first anyway + for ( num = 0; num < cg.snap->numEntities ; num++ ) { + CG_AddCEntity_Filter( &cg_entities[ cg.snap->entities[ num ].number ] ); + } + + // Gordon: Add tank bits as a second loop, to stop recursion problems with tank bits not on base entity + for ( num = 0; num < cg.snap->numEntities ; num++ ) { + if ( cg_entities[ cg.snap->entities[ num ].number ].currentState.eType == ET_MOVER ) { + CG_Mover_PostProcess( &cg_entities[ cg.snap->entities[ num ].number ] ); + } + } + + // Ridah, add the flamethrower sounds + CG_UpdateFlamethrowerSounds(); +} + +void CGRefEntityToTag( refEntity_t* ent, tag_t* tag ) { + int i; + VectorCopy( ent->origin, tag->origin ); + for ( i = 0; i < 3; i++ ) { + VectorCopy( ent->axis[i], tag->axis[i] ); + } +} + +void CGTagToRefEntity( refEntity_t* ent, tag_t* tag ) { + int i; + VectorCopy( tag->origin, ent->origin ); + for ( i = 0; i < 3; i++ ) { + VectorCopy( tag->axis[i], ent->axis[i] ); + } +} + +void CG_AttachBitsToTank( centity_t* tank, refEntity_t* mg42base, refEntity_t* mg42upper, refEntity_t* mg42gun, refEntity_t* player, refEntity_t* flash, vec_t* playerangles, const char* tagName, qboolean browning ) { + refEntity_t ent; + vec3_t angles; + int i; + + memset( mg42base, 0, sizeof( refEntity_t ) ); + memset( mg42gun, 0, sizeof( refEntity_t ) ); + memset( mg42upper, 0, sizeof( refEntity_t ) ); + memset( player, 0, sizeof( refEntity_t ) ); + memset( flash, 0, sizeof( refEntity_t ) ); + + mg42base->hModel = cgs.media.hMountedMG42Base; + mg42upper->hModel = cgs.media.hMountedMG42Nest; + if ( browning ) { + mg42gun->hModel = cgs.media.hMountedBrowning; + } else { + mg42gun->hModel = cgs.media.hMountedMG42; + } + + if ( !CG_AddCEntity_Filter( tank ) ) { + return; + } + + if ( tank->tankframe != cg.clientFrame ) { + tank->tankframe = cg.clientFrame; + + memset( &ent, 0, sizeof( refEntity_t ) ); + + if ( tank->currentState.solid == SOLID_BMODEL ) { + ent.hModel = cgs.gameModels[tank->currentState.modelindex2]; + } else { + ent.hModel = cgs.gameModels[tank->currentState.modelindex]; + } + + ent.frame = tank->lerpFrame.frame; + ent.oldframe = tank->lerpFrame.oldFrame; + ent.backlerp = tank->lerpFrame.backlerp; + + AnglesToAxis( tank->lerpAngles, ent.axis ); + VectorCopy( tank->lerpOrigin, ent.origin ); + + AxisClear( mg42base->axis ); + CG_PositionEntityOnTag( mg42base, &ent, tagName, 0, NULL ); + + VectorCopy( playerangles, angles ); + angles[PITCH] = 0; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + // Gordon: is this entity mounted on a tank, and attached to _OUR_ turret entity (which could be us) + if ( cg_entities[i].currentValid && cg_entities[i].currentState.eFlags & EF_MOUNTEDTANK && cg_entities[i].tagParent == tank - cg_entities ) { + angles[YAW] -= tank->lerpAngles[YAW]; + angles[PITCH] -= tank->lerpAngles[PITCH]; + break; + } + } + + AnglesToAxis( angles, mg42upper->axis ); + CG_PositionRotatedEntityOnTag( mg42upper, mg42base, "tag_mg42nest" ); + + VectorCopy( playerangles, angles ); + angles[YAW] = 0; + angles[ROLL] = 0; + + AnglesToAxis( angles, mg42gun->axis ); + CG_PositionRotatedEntityOnTag( mg42gun, mg42upper, "tag_mg42" ); + + CG_PositionEntityOnTag( player, mg42upper, "tag_playerpo", 0, NULL ); + CG_PositionEntityOnTag( flash, mg42gun, "tag_flash", 0, NULL ); + + CGRefEntityToTag( mg42base, &tank->mountedMG42Base ); + CGRefEntityToTag( mg42upper, &tank->mountedMG42Nest ); + CGRefEntityToTag( mg42gun, &tank->mountedMG42 ); + CGRefEntityToTag( player, &tank->mountedMG42Player ); + CGRefEntityToTag( flash, &tank->mountedMG42Flash ); + } + + CGTagToRefEntity( mg42base, &tank->mountedMG42Base ); + CGTagToRefEntity( mg42upper, &tank->mountedMG42Nest ); + CGTagToRefEntity( mg42gun, &tank->mountedMG42 ); + CGTagToRefEntity( player, &tank->mountedMG42Player ); + CGTagToRefEntity( flash, &tank->mountedMG42Flash ); +} diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c new file mode 100644 index 0000000..36f1c74 --- /dev/null +++ b/src/cgame/cg_event.c @@ -0,0 +1,2968 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_event.c -- handle entity events at snapshot or playerstate transitions + +#include "cg_local.h" + +extern void CG_StartShakeCamera( float param ); +extern void CG_Tracer( vec3_t source, vec3_t dest, int sparks ); +//========================================================================== + +/* +============= +CG_Obituary +============= +*/ +static void CG_Obituary( entityState_t *ent ) { + int mod; + int target, attacker; + char *message; + char *message2; + char targetName[32]; + char attackerName[32]; + clientInfo_t *ci, *ca; // JPW NERVE ca = attacker + qhandle_t deathShader = cgs.media.pmImages[PM_DEATH]; + + target = ent->otherEntityNum; + attacker = ent->otherEntityNum2; + mod = ent->eventParm; + + if ( target < 0 || target >= MAX_CLIENTS ) { + CG_Error( "CG_Obituary: target out of range" ); + } + ci = &cgs.clientinfo[target]; + + if ( attacker < 0 || attacker >= MAX_CLIENTS ) { + attacker = ENTITYNUM_WORLD; + ca = NULL; + } else { + ca = &cgs.clientinfo[attacker]; + } + + Q_strncpyz( targetName, ci->name, sizeof( targetName ) - 2 ); + strcat( targetName, S_COLOR_WHITE ); + + message2 = ""; + + // check for single client messages + switch ( mod ) { + case MOD_SUICIDE: + message = "committed suicide"; + break; + case MOD_FALLING: + message = "fell to his death"; + break; + case MOD_CRUSH: + message = "was crushed"; + break; + case MOD_WATER: + message = "drowned"; + break; + case MOD_SLIME: + message = "died by toxic materials"; + break; + case MOD_TRIGGER_HURT: + case MOD_TELEFRAG: // rain - added TELEFRAG and TARGET_LASER, just in case + case MOD_TARGET_LASER: + message = "was killed"; + break; + case MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER: + message = "got buried under a pile of rubble"; + break; + case MOD_LAVA: // rain + message = "was incinerated"; + break; + default: + message = NULL; + break; + } + + if ( attacker == target ) { + switch ( mod ) { + case MOD_DYNAMITE: + message = "dynamited himself to pieces"; + break; + case MOD_GRENADE_LAUNCHER: + case MOD_GRENADE_PINEAPPLE: // rain - added PINEAPPLE + message = "dove on his own grenade"; + break; + case MOD_PANZERFAUST: + message = "vaporized himself"; + break; + case MOD_FLAMETHROWER: // rain + message = "played with fire"; + break; + case MOD_AIRSTRIKE: + message = "obliterated himself"; + break; + case MOD_ARTY: + message = "fired-for-effect on himself"; + break; + case MOD_EXPLOSIVE: + message = "died in his own explosion"; + break; + // rain - everything from this point on is sorted by MOD, didn't + // resort existing messages to avoid differences between pre + // and post-patch code (for source patching) + case MOD_GPG40: + case MOD_M7: // rain + //bani - more amusing, less wordy + message = "ate his own rifle grenade"; + break; + case MOD_LANDMINE: // rain + //bani - slightly more amusing + message = "failed to spot his own landmine"; + break; + case MOD_SATCHEL: // rain + message = "embraced his own satchel explosion"; + break; + case MOD_TRIPMINE: // rain - dormant code + message = "forgot where his tripmine was"; + break; + case MOD_CRUSH_CONSTRUCTION: // rain + message = "engineered himself into oblivion"; + break; + case MOD_CRUSH_CONSTRUCTIONDEATH: // rain + message = "buried himself alive"; + break; + case MOD_MORTAR: // rain + message = "never saw his own mortar round coming"; + break; + case MOD_SMOKEGRENADE: // rain + // bani - more amusing + message = "danced on his airstrike marker"; + break; + // no obituary message if changing teams + case MOD_SWITCHTEAM: + return; + default: + message = "killed himself"; + break; + } + } + + if ( message ) { + message = CG_TranslateString( message ); + CG_AddPMItem( PM_DEATH, va( "%s %s.", targetName, message ), deathShader ); + return; + } + + // check for kill messages from the current clientNum + if ( attacker == cg.snap->ps.clientNum ) { + char *s; + + if ( ci->team == ca->team ) { + if ( mod == MOD_SWAP_PLACES ) { + s = va( "%s %s", CG_TranslateString( "You swapped places with" ), targetName ); + } else { + s = va( "%s %s", CG_TranslateString( "You killed ^1TEAMMATE^7" ), targetName ); + } + } else { + s = va( "%s %s", CG_TranslateString( "You killed" ), targetName ); + } + CG_PriorityCenterPrint( s, SCREEN_HEIGHT * 0.75, BIGCHAR_WIDTH * 0.6, 1 ); + // print the text message as well + } + + // check for double client messages + if ( !ca ) { + strcpy( attackerName, "noname" ); + } else { + Q_strncpyz( attackerName, ca->name, sizeof( attackerName ) - 2 ); + strcat( attackerName, S_COLOR_WHITE ); + + // check for kill messages about the current clientNum + if ( target == cg.snap->ps.clientNum ) { + Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); + } + } + + if ( ca ) { + switch ( mod ) { + case MOD_KNIFE: + message = "was stabbed by"; + message2 = "'s knife"; + // OSP - goat luvin +// if( attacker == cg.snap->ps.clientNum || target == cg.snap->ps.clientNum ) { +// trap_S_StartSound( cg.snap->ps.origin, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.goatAxis ); +// } + break; + + case MOD_AKIMBO_COLT: + case MOD_AKIMBO_SILENCEDCOLT: + message = "was killed by"; + message2 = "'s Akimbo .45ACP 1911s"; + break; + + case MOD_AKIMBO_LUGER: + case MOD_AKIMBO_SILENCEDLUGER: + message = "was killed by"; + message2 = "'s Akimbo Luger 9mms"; + break; + + case MOD_SILENCER: + case MOD_LUGER: + message = "was killed by"; + message2 = "'s Luger 9mm"; + break; + + case MOD_SILENCED_COLT: + case MOD_COLT: + message = "was killed by"; + message2 = "'s .45ACP 1911"; + break; + + case MOD_MP40: + message = "was killed by"; + message2 = "'s MP40"; + break; + + case MOD_THOMPSON: + message = "was killed by"; + message2 = "'s Thompson"; + break; + + case MOD_STEN: + message = "was killed by"; + message2 = "'s Sten"; + break; + + case MOD_DYNAMITE: + message = "was blasted by"; + message2 = "'s dynamite"; + break; + + case MOD_PANZERFAUST: + message = "was blasted by"; + message2 = "'s Panzerfaust"; + break; + + case MOD_GRENADE_LAUNCHER: + case MOD_GRENADE_PINEAPPLE: + message = "was exploded by"; + message2 = "'s grenade"; + break; + + case MOD_FLAMETHROWER: + message = "was cooked by"; + message2 = "'s flamethrower"; + break; + + case MOD_MORTAR: + message = "never saw"; + message2 = "'s mortar round coming"; + break; + + case MOD_MACHINEGUN: + message = "was perforated by"; + message2 = "'s crew-served MG"; + break; + + case MOD_BROWNING: + message = "was perforated by"; + message2 = "'s tank-mounted browning 30cal"; + break; + + case MOD_MG42: + message = "was perforated by"; + message2 = "'s tank-mounted MG42"; + break; + + case MOD_AIRSTRIKE: + message = "was blasted by"; + message2 = "'s support fire"; + break; + + case MOD_ARTY: + message = "was shelled by"; + message2 = "'s artillery support"; + break; + + case MOD_SWAP_PLACES: + message = "^2swapped places with^7"; + message2 = ""; + break; + + case MOD_KAR98: // same weapon really + case MOD_K43: + message = "was killed by"; + message2 = "'s K43"; + break; + + case MOD_CARBINE: // same weapon really + case MOD_GARAND: + message = "was killed by"; + message2 = "'s Garand"; + break; + + case MOD_GPG40: + case MOD_M7: + message = "was killed by"; + message2 = "'s rifle grenade"; + break; + + case MOD_LANDMINE: + message = "failed to spot"; + message2 = "'s Landmine"; + break; + + case MOD_CRUSH_CONSTRUCTION: + message = "got caught in"; + message2 = "'s construction madness"; + break; + + case MOD_CRUSH_CONSTRUCTIONDEATH: + message = "got burried under"; + message2 = "'s rubble"; + break; + + case MOD_MOBILE_MG42: + message = "was mown down by"; + message2 = "'s Mobile MG42"; + break; + + case MOD_GARAND_SCOPE: + message = "was silenced by"; + message2 = "'s Garand"; + break; + + case MOD_K43_SCOPE: + message = "was silenced by"; + message2 = "'s K43"; + break; + + case MOD_FG42: + message = "was killed by"; + message2 = "'s FG42"; + break; + + case MOD_FG42SCOPE: + message = "was sniped by"; + message2 = "'s FG42"; + break; + + case MOD_SATCHEL: + message = "was blasted by"; + message2 = "'s Satchel Charge"; + break; + + case MOD_TRIPMINE: // rain - dormant code + message = "was detonated by"; + message2 = "'s trip mine"; + break; + + case MOD_SMOKEGRENADE: // rain + message = "stood on"; + message2 = "'s airstrike marker"; + break; + + default: + message = "was killed by"; + break; + } + + if ( ci->team == ca->team ) { + message = "^1WAS KILLED BY TEAMMATE^7"; + message2 = ""; + } + + if ( message ) { + message = CG_TranslateString( message ); + if ( message2 ) { + message2 = CG_TranslateString( message2 ); + CG_AddPMItem( PM_DEATH, va( "%s %s %s%s", targetName, message, attackerName, message2 ), deathShader ); +// CG_Printf( "[cgnotify]%s %s %s%s\n", targetName, message, attackerName, message2 ); + } + return; + } + } + + // we don't know what it was + switch ( mod ) { + default: + CG_AddPMItem( PM_DEATH, va( "%s died.", targetName ), deathShader ); + break; + } +} + +//========================================================================== + +// from cg_weapons.c +extern int CG_WeaponIndex( int weapnum, int *bank, int *cycle ); + + +/* +================ +CG_ItemPickup + +A new item was picked up this frame +================ +*/ +static void CG_ItemPickup( int itemNum ) { + int itemid; + int wpbank_cur, wpbank_pickup; + + itemid = bg_itemlist[itemNum].giTag; + + CG_AddPMItem( PM_MESSAGE, va( "Picked up %s", CG_PickupItemText( itemNum ) ), cgs.media.pmImages[PM_MESSAGE] ); + +// cg.itemPickup = itemNum; +// cg.itemPickupTime = cg.time; +// cg.itemPickupBlendTime = cg.time; + + // see if it should be the grabbed weapon + if ( bg_itemlist[itemNum].giType == IT_WEAPON ) { + + if ( cg_autoswitch.integer && cg.predictedPlayerState.weaponstate != WEAPON_RELOADING ) { + + // 0 - "Off" + // 1 - "Always Switch" + // 2 - "If New" + // 3 - "If Better" + // 4 - "New or Better" + + // don't ever autoswitch to secondary fire weapons + // Gordon: Leave autoswitch to secondary kar/carbine as they use alt ammo and arent zoomed: Note, not that it would do this anyway as it isnt in a bank.... + if ( itemid != WP_FG42SCOPE && itemid != WP_GARAND_SCOPE && itemid != WP_K43_SCOPE && itemid != WP_AMMO ) { //----(SA) modified + // no weap currently selected, always just select the new one + if ( !cg.weaponSelect ) { + cg.weaponSelectTime = cg.time; + cg.weaponSelect = itemid; + } + // 1 - always switch to new weap (Q3A default) + else if ( cg_autoswitch.integer == 1 ) { + cg.weaponSelectTime = cg.time; + cg.weaponSelect = itemid; + } else { + + // 2 - switch to weap if it's not already in the player's inventory (Wolf default) + // 4 - both 2 and 3 + + // FIXME: this works fine for predicted pickups (when you walk over the weapon), but not for + // manual pickups (activate item) + if ( cg_autoswitch.integer == 2 || cg_autoswitch.integer == 4 ) { + if ( !COM_BitCheck( cg.snap->ps.weapons, itemid ) ) { + cg.weaponSelectTime = cg.time; + cg.weaponSelect = itemid; + } + } // end 2 + + // 3 - switch to weap if it's in a bank greater than the current weap + // 4 - both 2 and 3 + if ( cg_autoswitch.integer == 3 || cg_autoswitch.integer == 4 ) { + // switch away only if a primary weapon is selected (read: don't switch away if current weap is a secondary mode) + if ( CG_WeaponIndex( cg.weaponSelect, &wpbank_cur, NULL ) ) { + if ( CG_WeaponIndex( itemid, &wpbank_pickup, NULL ) ) { + if ( wpbank_pickup > wpbank_cur ) { + cg.weaponSelectTime = cg.time; + cg.weaponSelect = itemid; + } + } + } + } // end 3 + + } // end cg_autoswitch.integer != 1 + + } + + } // end cg_autoswitch.integer + + } // end bg_itemlist[itemNum].giType == IT_WEAPON +} + + +/* +================ +CG_PainEvent + +Also called by playerstate transition +================ +*/ +typedef struct { + char *tag; + int refEntOfs; + int anim; +} painAnimForTag_t; + +#define PEFOFS( x ) ( (int)&( ( (playerEntity_t *)0 )->x ) ) + +void CG_PainEvent( centity_t *cent, int health, qboolean crouching ) { + char *snd; + + // don't do more than two pain sounds a second + if ( cg.time - cent->pe.painTime < 500 ) { + return; + } + + if ( health < 25 ) { + snd = "*pain25_1.wav"; + } else if ( health < 50 ) { + snd = "*pain50_1.wav"; + } else if ( health < 75 ) { + snd = "*pain75_1.wav"; + } else { + snd = "*pain100_1.wav"; + } + trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, + CG_CustomSound( cent->currentState.number, snd ) ); + + // save pain time for programitic twitch animation + cent->pe.painTime = cg.time; + cent->pe.painDirection ^= 1; +} + + + + + +/* +============== +CG_Explode + + + if (cent->currentState.angles2[0] || cent->currentState.angles2[1] || cent->currentState.angles2[2]) + +============== +*/ + +#define POSSIBLE_PIECES 6 + +typedef struct fxSound_s { + int max; + qhandle_t sound[3]; + const char *soundfile[3]; +} fxSound_t; + +static fxSound_t fxSounds[POSSIBLE_PIECES] = { + // wood + { 1, { -1, -1, -1 }, { "sound/world/boardbreak.wav", NULL, NULL } }, + // glass + { 3, { -1, -1, -1 }, { "sound/world/glassbreak1.wav", "sound/world/glassbreak2.wav", "sound/world/glassbreak3.wav" } }, + // metal + { 1, { -1, -1, -1 }, { "sound/world/metalbreak.wav", NULL, NULL } }, + // gibs + { 1, { -1, -1, -1 }, { "sound/world/gibsplit1.wav", NULL, NULL } }, + // brick + { 1, { -1, -1, -1 }, { "sound/world/debris1.wav", NULL, NULL } }, + // stone + { 1, { -1, -1, -1 }, { "sound/world/stonefall.wav", NULL, NULL } } +}; + +void CG_PrecacheFXSounds( void ) { + int i, j; + + for ( i = 0; i < POSSIBLE_PIECES; i++ ) { + for ( j = 0; j < fxSounds[i].max; j++ ) { + fxSounds[i].sound[j] = trap_S_RegisterSound( fxSounds[i].soundfile[j], qfalse ); + } + } +} + +void CG_Explodef( vec3_t origin, vec3_t dir, int mass, int type, qhandle_t sound, int forceLowGrav, qhandle_t shader ); +void CG_RubbleFx( vec3_t origin, vec3_t dir, int mass, int type, qhandle_t sound, int forceLowGrav, qhandle_t shader, float speedscale, float sizescale ); + +/* +============== +CG_Explode + the old cent-based explode calls will still work with this pass-through +============== +*/ +void CG_Explode( centity_t *cent, vec3_t origin, vec3_t dir, qhandle_t shader ) { + + qhandle_t inheritmodel = 0; + + // inherit shader + // (SA) FIXME: do this at spawn time rather than explode time so any new necessary shaders are created earlier + if ( cent->currentState.eFlags & EF_INHERITSHADER ) { + if ( !shader ) { +// inheritmodel = cent->currentState.modelindex; + inheritmodel = cgs.inlineDrawModel[cent->currentState.modelindex]; // okay, this should be better. + if ( inheritmodel ) { + shader = trap_R_GetShaderFromModel( inheritmodel, 0, 0 ); + } + } + } + + if ( !cent->currentState.dl_intensity ) { + sfxHandle_t sound; + + sound = random() * fxSounds[cent->currentState.frame].max; + + if ( fxSounds[cent->currentState.frame].sound[sound] == -1 ) { + fxSounds[cent->currentState.frame].sound[sound] = trap_S_RegisterSound( fxSounds[cent->currentState.frame].soundfile[sound], qfalse ); + } + + sound = fxSounds[cent->currentState.frame].sound[sound]; + + CG_Explodef( origin, + dir, + cent->currentState.density, // mass + cent->currentState.frame, // type + sound, // sound + cent->currentState.weapon, // forceLowGrav + shader + ); + } else { + sfxHandle_t sound; + + if ( cent->currentState.dl_intensity == -1 ) { + sound = 0; + } else { + sound = cgs.gameSounds[cent->currentState.dl_intensity]; + } + + CG_Explodef( origin, + dir, + cent->currentState.density, // mass + cent->currentState.frame, // type + sound, // sound + cent->currentState.weapon, // forceLowGrav + shader + ); + } + +} + +/* +============== +CG_Explode + the old cent-based explode calls will still work with this pass-through +============== +*/ +void CG_Rubble( centity_t *cent, vec3_t origin, vec3_t dir, qhandle_t shader ) { + + qhandle_t inheritmodel = 0; + + // inherit shader + // (SA) FIXME: do this at spawn time rather than explode time so any new necessary shaders are created earlier + if ( cent->currentState.eFlags & EF_INHERITSHADER ) { + if ( !shader ) { +// inheritmodel = cent->currentState.modelindex; + inheritmodel = cgs.inlineDrawModel[cent->currentState.modelindex]; // okay, this should be better. + if ( inheritmodel ) { + shader = trap_R_GetShaderFromModel( inheritmodel, 0, 0 ); + } + } + } + + if ( !cent->currentState.dl_intensity ) { + sfxHandle_t sound; + + sound = random() * fxSounds[cent->currentState.frame].max; + + if ( fxSounds[cent->currentState.frame].sound[sound] == -1 ) { + fxSounds[cent->currentState.frame].sound[sound] = trap_S_RegisterSound( fxSounds[cent->currentState.frame].soundfile[sound], qfalse ); + } + + sound = fxSounds[cent->currentState.frame].sound[sound]; + + CG_RubbleFx( origin, + dir, + cent->currentState.density, // mass + cent->currentState.frame, // type + sound, // sound + cent->currentState.weapon, // forceLowGrav + shader, + cent->currentState.angles2[0], + cent->currentState.angles2[1] + ); + } else { + sfxHandle_t sound; + + if ( cent->currentState.dl_intensity == -1 ) { + sound = 0; + } else { + sound = cgs.gameSounds[cent->currentState.dl_intensity]; + } + + CG_RubbleFx( origin, + dir, + cent->currentState.density, // mass + cent->currentState.frame, // type + sound, // sound + cent->currentState.weapon, // forceLowGrav + shader, + cent->currentState.angles2[0], + cent->currentState.angles2[1] + ); + } +} + + +/* +============== +CG_RubbleFx +============== +*/ +void CG_RubbleFx( vec3_t origin, vec3_t dir, int mass, int type, sfxHandle_t sound, int forceLowGrav, qhandle_t shader, float speedscale, float sizescale ) { + int i; + localEntity_t *le; + refEntity_t *re; + int howmany, total, totalsounds; + int pieces[6]; // how many of each piece + qhandle_t modelshader = 0; + float materialmul = 1; // multiplier for different types + + memset( &pieces, 0, sizeof( pieces ) ); + + pieces[5] = (int)( mass / 250.0f ); + pieces[4] = (int)( mass / 76.0f ); + pieces[3] = (int)( mass / 37.0f ); // so 2 per 75 + pieces[2] = (int)( mass / 15.0f ); + pieces[1] = (int)( mass / 10.0f ); + pieces[0] = (int)( mass / 5.0f ); + + if ( pieces[0] > 20 ) { + pieces[0] = 20; // cap some of the smaller bits so they don't get out of control + } + if ( pieces[1] > 15 ) { + pieces[1] = 15; + } + if ( pieces[2] > 10 ) { + pieces[2] = 10; + } + + if ( type == 0 ) { // cap wood even more since it's often grouped, and the small splinters can add up + if ( pieces[0] > 10 ) { + pieces[0] = 10; + } + if ( pieces[1] > 10 ) { + pieces[1] = 10; + } + if ( pieces[2] > 10 ) { + pieces[2] = 10; + } + } + + totalsounds = 0; + total = pieces[5] + pieces[4] + pieces[3] + pieces[2] + pieces[1] + pieces[0]; + + if ( sound ) { + trap_S_StartSound( origin, -1, CHAN_AUTO, sound ); + } + + if ( shader ) { // shader passed in to use + modelshader = shader; + } + + for ( i = 0; i < POSSIBLE_PIECES; i++ ) { + leBounceSoundType_t snd = LEBS_NONE; + int hmodel = 0; + float scale; + int endtime; + for ( howmany = 0; howmany < pieces[i]; howmany++ ) { + + scale = 1.0f; + endtime = 0; // set endtime offset for faster/slower fadeouts + + switch ( type ) { + case 0: // "wood" + snd = LEBS_WOOD; + hmodel = cgs.media.debWood[i]; + + if ( i == 0 ) { + scale = 0.5f; + } else if ( i == 1 ) { + scale = 0.6f; + } else if ( i == 2 ) { + scale = 0.7f; + } else if ( i == 3 ) { + scale = 0.5f; + } + // else goto pass; + + if ( i < 3 ) { + endtime = -3000; // small bits live 3 sec shorter than normal + } + break; + + case 1: // "glass" + snd = LEBS_NONE; + if ( i == 5 ) { + hmodel = cgs.media.shardGlass1; + } else if ( i == 4 ) { + hmodel = cgs.media.shardGlass2; + } else if ( i == 2 ) { + hmodel = cgs.media.shardGlass2; + } else if ( i == 1 ) { + hmodel = cgs.media.shardGlass2; + scale = 0.5f; + } else {goto pass;} + break; + + case 2: // "metal" + snd = LEBS_METAL; + if ( i == 5 ) { + hmodel = cgs.media.shardMetal1; + } else if ( i == 4 ) { + hmodel = cgs.media.shardMetal2; + } else if ( i == 2 ) { + hmodel = cgs.media.shardMetal2; + } else if ( i == 1 ) { + hmodel = cgs.media.shardMetal2; + scale = 0.5f; + } else {goto pass;} + break; + + case 3: // "gibs" + snd = LEBS_BLOOD; + if ( i == 5 ) { + hmodel = cgs.media.gibIntestine; + } else if ( i == 4 ) { + hmodel = cgs.media.gibLeg; + } else if ( i == 2 ) { + hmodel = cgs.media.gibChest; + } else { goto pass;} + break; + + case 4: // "brick" + snd = LEBS_ROCK; + hmodel = cgs.media.debBlock[i]; + break; + + case 5: // "rock" + snd = LEBS_ROCK; + if ( i == 5 ) { + hmodel = cgs.media.debRock[2]; // temporarily use the next smallest rock piece + } else if ( i == 4 ) { + hmodel = cgs.media.debRock[2]; + } else if ( i == 3 ) { + hmodel = cgs.media.debRock[1]; + } else if ( i == 2 ) { + hmodel = cgs.media.debRock[0]; + } else if ( i == 1 ) { + hmodel = cgs.media.debBlock[1]; // temporarily use the small block pieces + } else { hmodel = cgs.media.debBlock[0]; // temporarily use the small block pieces + } + if ( i <= 2 ) { + endtime = -2000; // small bits live 2 sec shorter than normal + } + break; + + case 6: // "fabric" + if ( i == 5 ) { + hmodel = cgs.media.debFabric[0]; + } else if ( i == 4 ) { + hmodel = cgs.media.debFabric[1]; + } else if ( i == 2 ) { + hmodel = cgs.media.debFabric[2]; + } else if ( i == 1 ) { + hmodel = cgs.media.debFabric[2]; + scale = 0.5; + } else {goto pass; // (only do 5, 4, 2 and 1) + } + break; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + + le->endTime = ( le->startTime + 5000 + random() * 5000 ) + endtime; + + // as it turns out, i'm not sure if setting the re->axis here will actually do anything + // AxisClear(re->axis); + // re->axis[0][0] = + // re->axis[1][1] = + // re->axis[2][2] = scale; + // + // if(scale != 1.0) + // re->nonNormalizedAxes = qtrue; + + le->sizeScale = scale * sizescale; + + if ( type == 1 ) { // glass + // Rafael added this because glass looks funky when it fades out + // TBD: need to look into this so that they fade out correctly + re->fadeStartTime = le->endTime; + re->fadeEndTime = le->endTime; + } else { + re->fadeStartTime = le->endTime - 4000; + re->fadeEndTime = le->endTime; + } + + if ( total > 5 ) { + if ( totalsounds > 5 || ( howmany % 8 ) != 0 ) { + snd = LEBS_NONE; + } else { + totalsounds++; + } + } + + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + le->leFlags = LEF_TUMBLE; + le->leMarkType = 0; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->leBounceSoundType = snd; + re->hModel = hmodel; + + // inherit shader + if ( modelshader ) { + re->customShader = modelshader; + } + + re->radius = 1000; + + // trying to make this a little more interesting + if ( type == 6 ) { // "fabric" + le->pos.trType = TR_GRAVITY_FLOAT; // the fabric stuff will change to use something that looks better + } else { + if ( !forceLowGrav && rand() & 1 ) { // if low gravity is not forced and die roll goes our way use regular grav + le->pos.trType = TR_GRAVITY; + } else { + le->pos.trType = TR_GRAVITY_LOW; + } + } + + switch ( type ) { + case 6: // fabric + le->bounceFactor = 0.0; + materialmul = 0.3; // rotation speed + break; + default: + le->bounceFactor = 0.4; + break; + } + + + // rotation + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand() & 31; + le->angles.trBase[1] = rand() & 31; + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + le->angles.trDelta[1] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + le->angles.trDelta[2] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + + + // if(type == 6) // fabric + // materialmul = 1; // translation speed + + + VectorCopy( origin, le->pos.trBase ); + VectorNormalize( dir ); + le->pos.trTime = cg.time; + + // (SA) hoping that was just intended to represent randomness + // if (cent->currentState.angles2[0] || cent->currentState.angles2[1] || cent->currentState.angles2[2]) + if ( le->angles.trBase[0] == 1 || le->angles.trBase[1] == 1 || le->angles.trBase[2] == 1 ) { + le->pos.trType = TR_GRAVITY; + VectorScale( dir, 10 * 8, le->pos.trDelta ); + le->pos.trDelta[0] += ( ( random() * 400 ) - 200 ) * speedscale; + le->pos.trDelta[1] += ( ( random() * 400 ) - 200 ) * speedscale; + le->pos.trDelta[2] = ( ( random() * 400 ) + 400 ) * speedscale; + + } else { + // location + VectorScale( dir, 200 + mass, le->pos.trDelta ); + le->pos.trDelta[0] += ( ( random() * 200 ) - 100 ); + le->pos.trDelta[1] += ( ( random() * 200 ) - 100 ); + + if ( dir[2] ) { + le->pos.trDelta[2] = random() * 200 * materialmul; // randomize sort of a lot so they don't all land together + } else { + le->pos.trDelta[2] = random() * 20; + } + } + } +pass: + continue; + } + +} + +/* +============== +CG_Explodef + made this more generic for spawning hits and breaks without needing a *cent +============== +*/ +void CG_Explodef( vec3_t origin, vec3_t dir, int mass, int type, qhandle_t sound, int forceLowGrav, qhandle_t shader ) { + int i; + localEntity_t *le; + refEntity_t *re; + int howmany, total, totalsounds; + int pieces[6]; // how many of each piece + qhandle_t modelshader = 0; + float materialmul = 1; // multiplier for different types + + memset( &pieces, 0, sizeof( pieces ) ); + + pieces[5] = (int)( mass / 250.0f ); + pieces[4] = (int)( mass / 76.0f ); + pieces[3] = (int)( mass / 37.0f ); // so 2 per 75 + pieces[2] = (int)( mass / 15.0f ); + pieces[1] = (int)( mass / 10.0f ); + pieces[0] = (int)( mass / 5.0f ); + + if ( pieces[0] > 20 ) { + pieces[0] = 20; // cap some of the smaller bits so they don't get out of control + } + if ( pieces[1] > 15 ) { + pieces[1] = 15; + } + if ( pieces[2] > 10 ) { + pieces[2] = 10; + } + + if ( type == 0 ) { // cap wood even more since it's often grouped, and the small splinters can add up + if ( pieces[0] > 10 ) { + pieces[0] = 10; + } + if ( pieces[1] > 10 ) { + pieces[1] = 10; + } + if ( pieces[2] > 10 ) { + pieces[2] = 10; + } + } + + total = pieces[5] + pieces[4] + pieces[3] + pieces[2] + pieces[1] + pieces[0]; + totalsounds = 0; + + if ( sound ) { + trap_S_StartSound( origin, -1, CHAN_AUTO, sound ); + } + + if ( shader ) { // shader passed in to use + modelshader = shader; + } + + for ( i = 0; i < POSSIBLE_PIECES; i++ ) { + leBounceSoundType_t snd = LEBS_NONE; + int hmodel = 0; + float scale; + int endtime; + for ( howmany = 0; howmany < pieces[i]; howmany++ ) { + + scale = 1.0f; + endtime = 0; // set endtime offset for faster/slower fadeouts + + switch ( type ) { + case 0: // "wood" + snd = LEBS_WOOD; + hmodel = cgs.media.debWood[i]; + + if ( i == 0 ) { + scale = 0.5f; + } else if ( i == 1 ) { + scale = 0.6f; + } else if ( i == 2 ) { + scale = 0.7f; + } else if ( i == 3 ) { + scale = 0.5f; + } + // else goto pass; + + if ( i < 3 ) { + endtime = -3000; // small bits live 3 sec shorter than normal + } + break; + + case 1: // "glass" + snd = LEBS_NONE; + if ( i == 5 ) { + hmodel = cgs.media.shardGlass1; + } else if ( i == 4 ) { + hmodel = cgs.media.shardGlass2; + } else if ( i == 2 ) { + hmodel = cgs.media.shardGlass2; + } else if ( i == 1 ) { + hmodel = cgs.media.shardGlass2; + scale = 0.5f; + } else {goto pass;} + break; + + case 2: // "metal" + snd = LEBS_BRASS; + if ( i == 5 ) { + hmodel = cgs.media.shardMetal1; + } else if ( i == 4 ) { + hmodel = cgs.media.shardMetal2; + } else if ( i == 2 ) { + hmodel = cgs.media.shardMetal2; + } else if ( i == 1 ) { + hmodel = cgs.media.shardMetal2; + scale = 0.5f; + } else {goto pass;} + break; + + case 3: // "gibs" + snd = LEBS_BLOOD; + if ( i == 5 ) { + hmodel = cgs.media.gibIntestine; + } else if ( i == 4 ) { + hmodel = cgs.media.gibLeg; + } else if ( i == 2 ) { + hmodel = cgs.media.gibChest; + } else { goto pass;} + break; + + case 4: // "brick" + snd = LEBS_ROCK; + hmodel = cgs.media.debBlock[i]; + break; + + case 5: // "rock" + snd = LEBS_ROCK; + if ( i == 5 ) { + hmodel = cgs.media.debRock[2]; // temporarily use the next smallest rock piece + } else if ( i == 4 ) { + hmodel = cgs.media.debRock[2]; + } else if ( i == 3 ) { + hmodel = cgs.media.debRock[1]; + } else if ( i == 2 ) { + hmodel = cgs.media.debRock[0]; + } else if ( i == 1 ) { + hmodel = cgs.media.debBlock[1]; // temporarily use the small block pieces + } else { hmodel = cgs.media.debBlock[0]; // temporarily use the small block pieces + } + if ( i <= 2 ) { + endtime = -2000; // small bits live 2 sec shorter than normal + } + break; + + case 6: // "fabric" + if ( i == 5 ) { + hmodel = cgs.media.debFabric[0]; + } else if ( i == 4 ) { + hmodel = cgs.media.debFabric[1]; + } else if ( i == 2 ) { + hmodel = cgs.media.debFabric[2]; + } else if ( i == 1 ) { + hmodel = cgs.media.debFabric[2]; + scale = 0.5; + } else {goto pass; // (only do 5, 4, 2 and 1) + } + break; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + + le->endTime = ( le->startTime + 5000 + random() * 5000 ) + endtime; + + // as it turns out, i'm not sure if setting the re->axis here will actually do anything + // AxisClear(re->axis); + // re->axis[0][0] = + // re->axis[1][1] = + // re->axis[2][2] = scale; + // + // if(scale != 1.0) + // re->nonNormalizedAxes = qtrue; + + le->sizeScale = scale; + + if ( type == 1 ) { // glass + // Rafael added this because glass looks funky when it fades out + // TBD: need to look into this so that they fade out correctly + re->fadeStartTime = le->endTime; + re->fadeEndTime = le->endTime; + } else { + re->fadeStartTime = le->endTime - 4000; + re->fadeEndTime = le->endTime; + } + + if ( total > 5 ) { + if ( totalsounds > 5 || ( howmany % 8 ) != 0 ) { + snd = LEBS_NONE; + } else { + totalsounds++; + } + } + + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + le->leFlags = LEF_TUMBLE; + le->leMarkType = 0; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->leBounceSoundType = snd; + re->hModel = hmodel; + + // inherit shader + if ( modelshader ) { + re->customShader = modelshader; + } + + re->radius = 1000; + + // trying to make this a little more interesting + if ( type == 6 ) { // "fabric" + le->pos.trType = TR_GRAVITY_FLOAT; // the fabric stuff will change to use something that looks better + } else { + if ( !forceLowGrav && rand() & 1 ) { // if low gravity is not forced and die roll goes our way use regular grav + le->pos.trType = TR_GRAVITY; + } else { + le->pos.trType = TR_GRAVITY_LOW; + } + } + + switch ( type ) { + case 6: // fabric + le->bounceFactor = 0.0; + materialmul = 0.3; // rotation speed + break; + default: + le->bounceFactor = 0.4; + break; + } + + + // rotation + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand() & 31; + le->angles.trBase[1] = rand() & 31; + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + le->angles.trDelta[1] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + le->angles.trDelta[2] = ( ( 100 + ( rand() & 500 ) ) - 300 ) * materialmul; + + + // if(type == 6) // fabric + // materialmul = 1; // translation speed + + + VectorCopy( origin, le->pos.trBase ); + VectorNormalize( dir ); + le->pos.trTime = cg.time; + + // (SA) hoping that was just intended to represent randomness + // if (cent->currentState.angles2[0] || cent->currentState.angles2[1] || cent->currentState.angles2[2]) + if ( le->angles.trBase[0] == 1 || le->angles.trBase[1] == 1 || le->angles.trBase[2] == 1 ) { + le->pos.trType = TR_GRAVITY; + VectorScale( dir, 10 * 8, le->pos.trDelta ); + le->pos.trDelta[0] += ( ( random() * 100 ) - 50 ); + le->pos.trDelta[1] += ( ( random() * 100 ) - 50 ); + le->pos.trDelta[2] = ( random() * 200 ) + 200; + + } else { + // location + VectorScale( dir, 200 + mass, le->pos.trDelta ); + le->pos.trDelta[0] += ( ( random() * 100 ) - 50 ); + le->pos.trDelta[1] += ( ( random() * 100 ) - 50 ); + + if ( dir[2] ) { + le->pos.trDelta[2] = random() * 200 * materialmul; // randomize sort of a lot so they don't all land together + } else { + le->pos.trDelta[2] = random() * 20; + } + } + } +pass: + continue; + } + +} + + +/* +============== +CG_Effect + Quake ed -> target_effect (0 .5 .8) (-6 -6 -6) (6 6 6) fire explode smoke debris gore lowgrav +============== +*/ +void CG_Effect( centity_t *cent, vec3_t origin, vec3_t dir ) { + localEntity_t *le; + refEntity_t *re; +// int howmany; + int mass; +// int large, small; + vec4_t projection, color; + + + VectorSet( dir, 0, 0, 1 ); // straight up. + + mass = cent->currentState.density; + +// 1 large per 100, 1 small per 24 +// large = (int)(mass / 100); +// small = (int)(mass / 24) + 1; + + if ( cent->currentState.eventParm & 1 ) { // fire + CG_MissileHitWall( WP_DYNAMITE, 0, origin, dir, 0 ); + return; + } + + // (SA) right now force smoke on any explosions +// if(cent->currentState.eventParm & 4) // smoke + if ( cent->currentState.eventParm & 7 ) { + int i, j; + vec3_t sprVel, sprOrg; + // explosion sprite animation + VectorScale( dir, 16, sprVel ); + for ( i = 0; i < 5; i++ ) { + for ( j = 0; j < 3; j++ ) + sprOrg[j] = origin[j] + 64 * dir[j] + 24 * crandom(); + sprVel[2] += rand() % 50; +// CG_ParticleExplosion( 2, sprOrg, sprVel, 1000+rand()%250, 20, 40+rand()%60 ); + CG_ParticleExplosion( "blacksmokeanim", sprOrg, sprVel, 3500 + rand() % 250, 10, 250 + rand() % 60, qfalse ); // JPW NERVE was smokeanimb + } + } + + + if ( cent->currentState.eventParm & 2 ) { // explode + vec3_t sprVel, sprOrg; + trap_S_StartSound( origin, -1, CHAN_AUTO, cgs.media.sfx_rockexp ); + + // new explode (from rl) + VectorMA( origin, 16, dir, sprOrg ); + VectorScale( dir, 100, sprVel ); + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 500, 20, 160, qtrue ); + //CG_ParticleExplosion( "blueexp", sprOrg, sprVel, 1200, 9, 300 ); + + // (SA) this is done only if the level designer has it marked in the entity. + // (see "cent->currentState.eventParm & 64" below) + + // RF, throw some debris +// CG_AddDebris( origin, dir, +// 280, // speed +// 1400, // duration +// // 15 + rand()%5 ); // count +// 7 + rand()%2 ); // count + + //% CG_ImpactMark( cgs.media.burnMarkShader, origin, dir, random()*360, 1,1,1,1, qfalse, 64, qfalse, 0xffffffff ); + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = 64.0f; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( cgs.media.burnMarkShader, 1, (vec3_t*) origin, projection, color, cg_markTime.integer, ( cg_markTime.integer >> 4 ) ); + } + + + if ( cent->currentState.eventParm & 8 ) { // rubble + // share the cg_explode code with func_explosives + const char *s; + qhandle_t sh = 0; // shader handle + + vec3_t newdir = {0, 0, 0}; + + if ( cent->currentState.angles2[0] || cent->currentState.angles2[1] || cent->currentState.angles2[2] ) { + VectorCopy( cent->currentState.angles2, newdir ); + } + + s = CG_ConfigString( CS_TARGETEFFECT ); // see if ent has a shader specified + if ( s && strlen( s ) > 0 ) { + sh = trap_R_RegisterShader( va( "textures/%s", s ) ); // FIXME: don't do this here. only for testing + + } + cent->currentState.eFlags &= ~EF_INHERITSHADER; // don't try to inherit shader + cent->currentState.dl_intensity = 0; // no sound + CG_Explode( cent, origin, newdir, sh ); + } + + + if ( cent->currentState.eventParm & 16 ) { // gore + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 5000 + random() * 3000; +//----(SA) fading out + re->fadeStartTime = le->endTime - 4000; + re->fadeEndTime = le->endTime; +//----(SA) end + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + // re->hModel = hModel; + re->hModel = cgs.media.gibIntestine; + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + + // VectorCopy( velocity, le->pos.trDelta ); + VectorNormalize( dir ); + VectorMA( dir, 200, dir, le->pos.trDelta ); + + le->pos.trTime = cg.time; + + le->bounceFactor = 0.3; + + le->leBounceSoundType = LEBS_BLOOD; + le->leMarkType = LEMT_BLOOD; + } + + + if ( cent->currentState.eventParm & 64 ) { // debris trails (the black strip that Ryan did) + CG_AddDebris( origin, dir, + 280, // speed + 1400, // duration + // 15 + rand()%5 ); // count + 7 + rand() % 2 ); // count + } +} + + + + + + +/* +CG_Shard + + We should keep this separate since there will be considerable differences + in the physical properties of shard vrs debris. not to mention the fact + there is no way we can quantify what type of effects the designers will + potentially desire. If it is still possible to merge the functionality of + cg_shard into cg_explode at a latter time I would have no problem with that + but for now I want to keep it separate +*/ +void CG_Shard( centity_t *cent, vec3_t origin, vec3_t dir ) { + localEntity_t *le; + refEntity_t *re; + int type; + int howmany; + int i; + int rval; + + qboolean isflyingdebris = qfalse; + + type = cent->currentState.density; + howmany = cent->currentState.frame; + + for ( i = 0; i < howmany; i++ ) + { + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 5000 + random() * 5000; + +//----(SA) fading out + re->fadeStartTime = le->endTime - 1000; + re->fadeEndTime = le->endTime; +//----(SA) end + + if ( type == 999 ) { + le->startTime = cg.time; + le->endTime = le->startTime + 100; + re->fadeStartTime = le->endTime - 100; + re->fadeEndTime = le->endTime; + type = 1; + + isflyingdebris = qtrue; + } + + + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + le->leFlags = LEF_TUMBLE; + le->bounceFactor = 0.4; + // le->leBounceSoundType = LEBS_WOOD; + le->leMarkType = 0; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + if ( type == FXTYPE_GLASS ) { // glass + rval = rand() % 2; + + if ( rval ) { + re->hModel = cgs.media.shardGlass1; + } else { + re->hModel = cgs.media.shardGlass2; + } + } else if ( type == FXTYPE_WOOD ) { // wood + rval = rand() % 2; + + if ( rval ) { + re->hModel = cgs.media.shardWood1; + } else { + re->hModel = cgs.media.shardWood2; + } + } else if ( type == FXTYPE_METAL ) { // metal + rval = rand() % 2; + + if ( rval ) { + re->hModel = cgs.media.shardMetal1; + } else { + re->hModel = cgs.media.shardMetal2; + } + } + /*else if (type == 3) // ceramic + { + rval = rand()%2; + + if (rval) + re->hModel = cgs.media.shardCeramic1; + else + re->hModel = cgs.media.shardCeramic2; + }*/ + else if ( type == FXTYPE_BRICK || type == FXTYPE_STONE ) { // rubble + rval = rand() % 3; + + if ( rval == 1 ) { + re->hModel = cgs.media.shardRubble1; + } else if ( rval == 2 ) { + re->hModel = cgs.media.shardRubble2; + } else { + re->hModel = cgs.media.shardRubble3; + } + + } else { + CG_Printf( "CG_Debris has an unknown type\n" ); + } + + // location + if ( isflyingdebris ) { + le->pos.trType = TR_GRAVITY_LOW; + } else { + le->pos.trType = TR_GRAVITY; + } + + VectorCopy( origin, le->pos.trBase ); + VectorNormalize( dir ); + VectorScale( dir, 10 * howmany, le->pos.trDelta ); + le->pos.trTime = cg.time; + le->pos.trDelta[0] += ( ( random() * 100 ) - 50 ); + le->pos.trDelta[1] += ( ( random() * 100 ) - 50 ); + if ( type ) { + le->pos.trDelta[2] = ( random() * 200 ) + 100; // randomize sort of a lot so they don't all land together + } else { // glass + le->pos.trDelta[2] = ( random() * 100 ) + 50; // randomize sort of a lot so they don't all land together + + } + // rotation + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = rand() & 31; + le->angles.trBase[1] = rand() & 31; + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = ( 100 + ( rand() & 500 ) ) - 300; + le->angles.trDelta[1] = ( 100 + ( rand() & 500 ) ) - 300; + le->angles.trDelta[2] = ( 100 + ( rand() & 500 ) ) - 300; + + } + +} + + +void CG_ShardJunk( centity_t *cent, vec3_t origin, vec3_t dir ) { + localEntity_t *le; + refEntity_t *re; + int type; + + type = cent->currentState.density; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 5000 + random() * 5000; + + re->fadeStartTime = le->endTime - 1000; + re->fadeEndTime = le->endTime; + + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + le->leFlags = LEF_TUMBLE; + le->bounceFactor = 0.4; + le->leMarkType = 0; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + re->hModel = cgs.media.shardJunk[rand() % MAX_LOCKER_DEBRIS]; + + le->pos.trType = TR_GRAVITY; + + VectorCopy( origin, le->pos.trBase ); + VectorNormalize( dir ); + VectorScale( dir, 10 * 8, le->pos.trDelta ); + le->pos.trTime = cg.time; + le->pos.trDelta[0] += ( ( random() * 100 ) - 50 ); + le->pos.trDelta[1] += ( ( random() * 100 ) - 50 ); + + le->pos.trDelta[2] = ( random() * 100 ) + 50; // randomize sort of a lot so they don't all land together + + // rotation + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + //le->angles.trBase[0] = rand()&31; + //le->angles.trBase[1] = rand()&31; + le->angles.trBase[2] = rand() & 31; + + //le->angles.trDelta[0] = (100 + (rand()&500)) - 300; + //le->angles.trDelta[1] = (100 + (rand()&500)) - 300; + le->angles.trDelta[2] = ( 100 + ( rand() & 500 ) ) - 300; + +} + +// Gordon: debris test +void CG_Debris( centity_t *cent, vec3_t origin, vec3_t dir ) { + localEntity_t *le; + refEntity_t *re; + int type; + + type = cent->currentState.density; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + 5000 + random() * 5000; + + re->fadeStartTime = le->endTime - 1000; + re->fadeEndTime = le->endTime; + + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + le->leFlags = LEF_TUMBLE | LEF_TUMBLE_SLOW; + le->bounceFactor = 0.4; + le->leMarkType = 0; + le->breakCount = 1; + le->sizeScale = 0.5; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + re->hModel = cgs.inlineDrawModel[cent->currentState.modelindex]; + + le->pos.trType = TR_GRAVITY; + + VectorCopy( origin, le->pos.trBase ); + VectorCopy( dir, le->pos.trDelta ); + le->pos.trTime = cg.time; + + // rotation + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[2] = rand() & 31; + + le->angles.trDelta[2] = ( 100 + ( rand() & 500 ) ) - 300; + le->angles.trDelta[2] = ( 50 + ( rand() & 400 ) ) - 100; + le->angles.trDelta[2] = ( 50 + ( rand() & 400 ) ) - 100; +} +// =================== + +//void CG_BatDeath( centity_t *cent ) +//{ +// CG_ParticleExplosion( "blood", cent->lerpOrigin, vec3_origin, 400, 20, 30, qfalse ); +//} + +void CG_MortarImpact( centity_t *cent, vec3_t origin, int sfx, qboolean dist ) { + if ( sfx >= 0 ) { + trap_S_StartSound( origin, -1, CHAN_AUTO, cgs.media.sfx_mortarexp[sfx] ); + } + + if ( dist ) { + vec3_t gorg, norm; + float gdist; + + VectorSubtract( origin, cg.refdef_current->vieworg, norm ); + gdist = VectorNormalize( norm ); + if ( gdist > 1200 && gdist < 8000 ) { // 1200 is max cam shakey dist (2*600) use gorg as the new sound origin + VectorMA( cg.refdef_current->vieworg, 800, norm, gorg ); // non-distance falloff makes more sense; sfx2range was gdist*0.2 + // sfx2range is variable to give us minimum volume control different explosion sizes (see mortar, panzerfaust, and grenade) + trap_S_StartSoundEx( gorg, -1, CHAN_WEAPON, cgs.media.sfx_mortarexpDist, SND_NOCUT ); + } + + if ( cent->currentState.clientNum == cg.snap->ps.clientNum && cg.mortarImpactTime != -2 ) { + VectorCopy( origin, cg.mortarImpactPos ); + cg.mortarImpactTime = cg.time; + cg.mortarImpactOutOfMap = qfalse; + } + } +} + +void CG_MortarMiss( centity_t *cent, vec3_t origin ) { + if ( cent->currentState.clientNum == cg.snap->ps.clientNum && cg.mortarImpactTime != -2 ) { + VectorCopy( origin, cg.mortarImpactPos ); + cg.mortarImpactTime = cg.time; + if ( cent->currentState.density ) { + cg.mortarImpactOutOfMap = qtrue; + } else { + cg.mortarImpactOutOfMap = qfalse; + } + } +} + +// a convenience function for all footstep sound playing +static void CG_StartFootStepSound( bg_playerclass_t* classInfo, entityState_t *es, sfxHandle_t sfx ) { + if ( cg_footsteps.integer ) { + trap_S_StartSound( NULL, es->number, CHAN_BODY, sfx ); + } +} + +/* +============== +CG_EntityEvent + +An entity has an event value +also called by CG_CheckPlayerstateEvents +============== +*/ +extern void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ); +// JPW NERVE +void CG_MachineGunEjectBrass( centity_t *cent ); +void CG_MachineGunEjectBrassNew( centity_t *cent ); +// jpw +#define DEBUGNAME( x ) if ( cg_debugEvents.integer ) {CG_Printf( x "\n" );} +void CG_EntityEvent( centity_t *cent, vec3_t position ) { + entityState_t *es; + int event; + vec3_t dir; + const char *s; + int clientNum; + clientInfo_t *ci; + char tempStr[MAX_QPATH]; + bg_playerclass_t *classInfo; + bg_character_t *character; + +// JPW NERVE copied here for mg42 SFX event + vec3_t porg, gorg, norm; // player/gun origin + float gdist; +// jpw + + static int footstepcnt = 0; + static int splashfootstepcnt = 0; + + es = ¢->currentState; + event = es->event & ~EV_EVENT_BITS; + + if ( cg_debugEvents.integer ) { + CG_Printf( "time:%i ent:%3i event:%3i ", cg.time, es->number, event ); + } + + if ( !event ) { + DEBUGNAME( "ZEROEVENT" ); + return; + } + + clientNum = es->clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + classInfo = CG_PlayerClassForClientinfo( ci, cent ); + character = CG_CharacterForClientinfo( ci, cent ); + + switch ( event ) { + // + // movement generated events + // + case EV_FOOTSTEP: + DEBUGNAME( "EV_FOOTSTEP" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + CG_StartFootStepSound( classInfo, es, cgs.media.footsteps[ es->eventParm ][footstepcnt] ); + } else { + CG_StartFootStepSound( classInfo, es, cgs.media.footsteps[ character->animModelInfo->footsteps ][footstepcnt] ); + } + } + break; + case EV_FOOTSPLASH: + DEBUGNAME( "EV_FOOTSPLASH" ); + CG_StartFootStepSound( classInfo, es, cgs.media.footsteps[ FOOTSTEP_SPLASH ][splashfootstepcnt] ); + break; + case EV_FOOTWADE: + DEBUGNAME( "EV_FOOTWADE" ); + CG_StartFootStepSound( classInfo, es, cgs.media.footsteps[ FOOTSTEP_SPLASH ][splashfootstepcnt] ); + break; + case EV_SWIM: + DEBUGNAME( "EV_SWIM" ); + CG_StartFootStepSound( classInfo, es, cgs.media.footsteps[ FOOTSTEP_SPLASH ][footstepcnt] ); + break; + + case EV_FALL_SHORT: + DEBUGNAME( "EV_FALL_SHORT" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -8; + cg.landTime = cg.time; + } + break; + + case EV_FALL_DMG_10: + DEBUGNAME( "EV_FALL_DMG_10" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landHurt ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -16; + cg.landTime = cg.time; + } + break; + case EV_FALL_DMG_15: + DEBUGNAME( "EV_FALL_DMG_15" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landHurt ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -16; + cg.landTime = cg.time; + } + break; + case EV_FALL_DMG_25: + DEBUGNAME( "EV_FALL_DMG_25" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landHurt ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -24; + cg.landTime = cg.time; + } + break; + case EV_FALL_DMG_50: + DEBUGNAME( "EV_FALL_DMG_50" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landHurt ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + if ( clientNum == cg.predictedPlayerState.clientNum ) { + // smooth landing z changes + cg.landChange = -24; + cg.landTime = cg.time; + } + break; + case EV_FALL_NDIE: + DEBUGNAME( "EV_FALL_NDIE" ); + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ es->eventParm ] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound[ character->animModelInfo->footsteps ] ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landHurt ); + cent->pe.painTime = cg.time; // don't play a pain sound right after this + // splat + break; + + case EV_EXERT1: + DEBUGNAME( "EV_EXERT1" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*exert1.wav" ) ); + break; + case EV_EXERT2: + DEBUGNAME( "EV_EXERT2" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*exert2.wav" ) ); + break; + case EV_EXERT3: + DEBUGNAME( "EV_EXERT3" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*exert3.wav" ) ); + break; + + case EV_STEP_4: + case EV_STEP_8: + case EV_STEP_12: + case EV_STEP_16: // smooth out step up transitions + DEBUGNAME( "EV_STEP" ); + { + float oldStep; + int delta; + int step; + + if ( clientNum != cg.predictedPlayerState.clientNum ) { + break; + } + // if we are interpolating, we don't need to smooth steps + if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || + cg_nopredict.integer +#ifdef ALLOW_GSYNC + || cg_synchronousClients.integer +#endif // ALLOW_GSYNC + ) { + break; + } + // check for stepping up before a previous step is completed + delta = cg.time - cg.stepTime; + if ( delta < STEP_TIME ) { + oldStep = cg.stepChange * ( STEP_TIME - delta ) / STEP_TIME; + } else { + oldStep = 0; + } + + // add this amount + step = 4 * ( event - EV_STEP_4 + 1 ); + cg.stepChange = oldStep + step; + if ( cg.stepChange > MAX_STEP_CHANGE ) { + cg.stepChange = MAX_STEP_CHANGE; + } + cg.stepTime = cg.time; + break; + } + + case EV_JUMP: + DEBUGNAME( "EV_JUMP" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) ); + break; + case EV_TAUNT: + DEBUGNAME( "EV_TAUNT" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) ); + break; + case EV_WATER_TOUCH: + DEBUGNAME( "EV_WATER_TOUCH" ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrInSound ); + break; + case EV_WATER_LEAVE: + DEBUGNAME( "EV_WATER_LEAVE" ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); + break; + case EV_WATER_UNDER: + DEBUGNAME( "EV_WATER_UNDER" ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound ); + if ( cg.clientNum == es->number ) { + cg.waterundertime = cg.time + HOLDBREATHTIME; + } + +//----(SA) this fog stuff for underwater is really just a test for feasibility of creating the under-water effect that way. +//----(SA) the related issues of load/savegames, death underwater, etc. are not handled at all. +//----(SA) the actual problem, of course, is doing underwater stuff when the water is very turbulant and you can't simply +//----(SA) do things based on the players head being above/below the water brushes top surface. (since the waves can potentially be /way/ above/below that) + + // DHM - Nerve :: causes problems in multiplayer... + break; + case EV_WATER_CLEAR: + DEBUGNAME( "EV_WATER_CLEAR" ); + //trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound ); + if ( es->eventParm ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrGaspSound ); + } + break; + + case EV_ITEM_PICKUP: + case EV_ITEM_PICKUP_QUIET: + DEBUGNAME( "EV_ITEM_PICKUP" ); + { + gitem_t *item; + int index; + + index = es->eventParm; // player predicted + + if ( index < 1 || index >= bg_numItems ) { + break; + } + item = &bg_itemlist[ index ]; + + if ( event == EV_ITEM_PICKUP ) { // not quiet + // powerups and team items will have a separate global sound, this one + // will be played at prediction time + if ( item->giType == IT_TEAM ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( "sound/misc/w_pkup.wav", qfalse ) ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); + } + } + + // show icon and name on status bar + if ( es->number == cg.snap->ps.clientNum ) { + CG_ItemPickup( index ); + } + +//----(SA) draw the HUD items for a sec since this is a special item +/* if ( item->giType == IT_KEY) + cg.itemFadeTime = cg.time + 1000;*/ + + } + break; + + case EV_GLOBAL_ITEM_PICKUP: + DEBUGNAME( "EV_GLOBAL_ITEM_PICKUP" ); + { + gitem_t *item; + int index; + + index = es->eventParm; // player predicted + + if ( index < 1 || index >= bg_numItems ) { + break; + } + item = &bg_itemlist[ index ]; + if ( *item->pickup_sound ) { + // powerup pickups are global + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) ); // FIXME: precache + } + + // show icon and name on status bar + if ( es->number == cg.snap->ps.clientNum ) { + CG_ItemPickup( index ); + } + } + break; + + // + // weapon events + // + case EV_VENOM: + DEBUGNAME( "EV_VENOM" ); +// CG_VenomFire( es, qfalse ); + break; + + case EV_WEAP_OVERHEAT: + DEBUGNAME( "EV_WEAP_OVERHEAT" ); + + // start weapon idle animation + if ( es->number == cg.snap->ps.clientNum ) { + cg.predictedPlayerState.weapAnim = ( ( cg.predictedPlayerState.weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | PM_IdleAnimForWeapon( cg.snap->ps.weapon ); + cent->overheatTime = cg.time; // used to make the barrels smoke when overheated + } + + if ( BG_PlayerMounted( es->eFlags ) ) { + trap_S_StartSoundVControl( NULL, es->number, CHAN_AUTO, cgs.media.hWeaponHeatSnd, 255 ); + } else if ( es->eFlags & EF_MOUNTEDTANK ) { + if ( cg_entities[cg_entities[cg_entities[ es->number ].tagParent].tankparent].currentState.density & 8 ) { + trap_S_StartSoundVControl( NULL, es->number, CHAN_AUTO, cgs.media.hWeaponHeatSnd_2, 255 ); + } else { + trap_S_StartSoundVControl( NULL, es->number, CHAN_AUTO, cgs.media.hWeaponHeatSnd, 255 ); + } + } else if ( cg_weapons[es->weapon].overheatSound ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cg_weapons[es->weapon].overheatSound ); + } + break; + +// JPW NERVE + case EV_SPINUP: + DEBUGNAME( "EV_SPINUP" ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cg_weapons[es->weapon].spinupSound ); + break; +// jpw + case EV_EMPTYCLIP: + DEBUGNAME( "EV_EMPTYCLIP" ); + break; + + case EV_FILL_CLIP: + DEBUGNAME( "EV_FILL_CLIP" ); + if ( cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS] >= 2 && BG_isLightWeaponSupportingFastReload( es->weapon ) && cg_weapons[es->weapon].reloadFastSound ) { + trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cg_weapons[es->weapon].reloadFastSound ); + } else if ( cg_weapons[es->weapon].reloadSound ) { + trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cg_weapons[es->weapon].reloadSound ); // JPW NERVE following sherman's SP fix, should allow killing reload sound when player dies + } + break; + +// JPW NERVE play a sound when engineer fixes MG42 + case EV_MG42_FIXED: + DEBUGNAME( "EV_MG42_FIXED" ); + //trap_S_StartSound(NULL,es->number,CHAN_WEAPON,cg_weapons[WP_MAUSER].reloadSound); // Arnout: needs updating + break; +// jpw + + case EV_NOAMMO: + case EV_WEAPONSWITCHED: + DEBUGNAME( "EV_NOAMMO" ); + if ( ( es->weapon != WP_GRENADE_LAUNCHER ) && + ( es->weapon != WP_GRENADE_PINEAPPLE ) && + ( es->weapon != WP_DYNAMITE ) && + ( es->weapon != WP_LANDMINE ) && + ( es->weapon != WP_SATCHEL ) && + ( es->weapon != WP_SATCHEL_DET ) && + ( es->weapon != WP_TRIPMINE ) && + ( es->weapon != WP_SMOKE_BOMB ) && + ( es->weapon != WP_AMMO ) && + ( es->weapon != WP_MEDKIT ) ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound ); + } + + if ( es->number == cg.snap->ps.clientNum && ( + ( cg_noAmmoAutoSwitch.integer > 0 && !CG_WeaponSelectable( cg.weaponSelect ) ) || + es->weapon == WP_MORTAR_SET || + es->weapon == WP_MOBILE_MG42_SET || + es->weapon == WP_GRENADE_LAUNCHER || + es->weapon == WP_GRENADE_PINEAPPLE || + es->weapon == WP_DYNAMITE || + es->weapon == WP_SMOKE_MARKER || + es->weapon == WP_PANZERFAUST || + es->weapon == WP_ARTY || + es->weapon == WP_LANDMINE || + es->weapon == WP_SATCHEL || + es->weapon == WP_SATCHEL_DET || + es->weapon == WP_TRIPMINE || + es->weapon == WP_SMOKE_BOMB || + es->weapon == WP_AMMO || + es->weapon == WP_MEDKIT ) ) { + CG_OutOfAmmoChange( event == EV_WEAPONSWITCHED ? qfalse : qtrue ); + } + break; + case EV_CHANGE_WEAPON: + DEBUGNAME( "EV_CHANGE_WEAPON" ); + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); + break; + case EV_CHANGE_WEAPON_2: + DEBUGNAME( "EV_CHANGE_WEAPON" ); + + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound ); + + if ( es->number == cg.snap->ps.clientNum ) { + int newweap = 0; + + // client will get this message if reloading while using an alternate weapon + // client should voluntarily switch back to primary at that point + switch ( es->weapon ) { + case WP_FG42SCOPE: + newweap = WP_FG42; + break; + case WP_GARAND_SCOPE: + newweap = WP_GARAND; + break; + case WP_K43_SCOPE: + newweap = WP_K43; + break; + default: + break; + } + + if ( newweap ) { + CG_FinishWeaponChange( es->weapon, newweap ); + } + } + break; + + case EV_FIRE_WEAPON_MOUNTEDMG42: + case EV_FIRE_WEAPON_MG42: + VectorCopy( cent->currentState.pos.trBase, gorg ); + VectorCopy( cg.refdef_current->vieworg, porg ); + VectorSubtract( gorg, porg, norm ); + gdist = VectorNormalize( norm ); + if ( gdist > 512 && gdist < 4096 ) { + VectorMA( cg.refdef_current->vieworg, 64, norm, gorg ); + if ( cg_entities[cg_entities[cg_entities[ cent->currentState.number ].tagParent].tankparent].currentState.density & 8 ) { // should we use a browning? + trap_S_StartSoundEx( gorg, cent->currentState.number, CHAN_WEAPON, cgs.media.hWeaponEchoSnd_2, SND_NOCUT ); + } else { + trap_S_StartSoundEx( gorg, cent->currentState.number, CHAN_WEAPON, cgs.media.hWeaponEchoSnd, SND_NOCUT ); + } + } + DEBUGNAME( "EV_FIRE_WEAPON_MG42" ); + CG_FireWeapon( cent ); + break; + case EV_FIRE_WEAPON_AAGUN: + DEBUGNAME( "EV_FIRE_WEAPON_AAGUN" ); + CG_FireWeapon( cent ); + break; + case EV_FIRE_WEAPON: + case EV_FIRE_WEAPONB: + DEBUGNAME( "EV_FIRE_WEAPON" ); + if ( cent->currentState.clientNum == cg.snap->ps.clientNum && cg.snap->ps.eFlags & EF_ZOOMING ) { // to stop airstrike sfx + break; + } + CG_FireWeapon( cent ); + if ( event == EV_FIRE_WEAPONB ) { // akimbo firing + cent->akimboFire = qtrue; + } else { + cent->akimboFire = qfalse; + } + break; + case EV_FIRE_WEAPON_LASTSHOT: + DEBUGNAME( "EV_FIRE_WEAPON_LASTSHOT" ); + CG_FireWeapon( cent ); + break; + + case EV_NOFIRE_UNDERWATER: + DEBUGNAME( "EV_NOFIRE_UNDERWATER" ); + if ( cgs.media.noFireUnderwater ) { + trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cgs.media.noFireUnderwater ); + } + break; + + case EV_PLAYER_TELEPORT_IN: + break; + + case EV_PLAYER_TELEPORT_OUT: + break; + + case EV_ITEM_POP: + break; + case EV_ITEM_RESPAWN: + break; + + case EV_GRENADE_BOUNCE: + DEBUGNAME( "EV_GRENADE_BOUNCE" ); + + // DYNAMITE // Gordon: or LANDMINE FIXME: change this? (mebe a metallic sound) + if ( es->weapon == WP_SATCHEL ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.satchelbounce1 ); + } else if ( es->weapon == WP_DYNAMITE ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.dynamitebounce1 ); + } else if ( es->weapon == WP_LANDMINE ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landminebounce1 ); + } else { + // GRENADES + if ( es->eventParm != FOOTSTEP_TOTAL ) { + if ( rand() & 1 ) { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.grenadebounce[ es->eventParm ][0] ); + } else { + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.grenadebounce[ es->eventParm ][1] ); + } + } + } + break; + +/* case EV_FLAMEBARREL_BOUNCE: + DEBUGNAME("EV_FLAMEBARREL_BOUNCE"); + if ( rand() & 1 ) { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fbarrelexp1 ); + } else { + trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.fbarrelexp2 ); + } + break;*/ + + case EV_RAILTRAIL: + CG_RailTrail( &cgs.clientinfo[ es->otherEntityNum2 ], es->origin2, es->pos.trBase, es->dmgFlags ); //----(SA) added 'type' field + break; + + // + // missile impacts + // + case EV_MISSILE_HIT: + DEBUGNAME( "EV_MISSILE_HIT" ); + ByteToDir( es->eventParm, dir ); + CG_MissileHitPlayer( cent, es->weapon, position, dir, es->otherEntityNum ); + if ( es->weapon == WP_MORTAR_SET ) { + if ( !es->legsAnim ) { + CG_MortarImpact( cent, position, 3, qtrue ); + } else { + CG_MortarImpact( cent, position, -1, qtrue ); + } + } + break; + + case EV_MISSILE_MISS_SMALL: + DEBUGNAME( "EV_MISSILE_MISS" ); + ByteToDir( es->eventParm, dir ); + CG_MissileHitWallSmall( es->weapon, 0, position, dir ); + break; + + case EV_MISSILE_MISS: + DEBUGNAME( "EV_MISSILE_MISS" ); + ByteToDir( es->eventParm, dir ); + CG_MissileHitWall( es->weapon, 0, position, dir, 0 ); // (SA) modified to send missilehitwall surface parameters + if ( es->weapon == WP_MORTAR_SET ) { + if ( !es->legsAnim ) { + CG_MortarImpact( cent, position, 3, qtrue ); + } else { + CG_MortarImpact( cent, position, -1, qtrue ); + } + } + break; + + case EV_MISSILE_MISS_LARGE: + DEBUGNAME( "EV_MISSILE_MISS_LARGE" ); + ByteToDir( es->eventParm, dir ); + if ( es->weapon == WP_ARTY || es->weapon == WP_SMOKE_MARKER ) { + CG_MissileHitWall( es->weapon, 0, position, dir, 0 ); // (SA) modified to send missilehitwall surface parameters + } else { + CG_MissileHitWall( VERYBIGEXPLOSION, 0, position, dir, 0 ); // (SA) modified to send missilehitwall surface parameters + } + break; + + case EV_MORTAR_IMPACT: + DEBUGNAME( "EV_MORTAR_IMPACT" ); + CG_MortarImpact( cent, position, rand() % 3, qfalse ); + break; + case EV_MORTAR_MISS: + DEBUGNAME( "EV_MORTAR_MISS" ); + CG_MortarMiss( cent, position ); + break; + + case EV_MG42BULLET_HIT_WALL: + DEBUGNAME( "EV_MG42BULLET_HIT_WALL" ); + ByteToDir( es->eventParm, dir ); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD, es->otherEntityNum2, es->origin2[0], es->effect1Time ); + break; + + case EV_MG42BULLET_HIT_FLESH: + DEBUGNAME( "EV_MG42BULLET_HIT_FLESH" ); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm, es->otherEntityNum2, 0, es->effect1Time ); + break; + + + case EV_BULLET_HIT_WALL: + DEBUGNAME( "EV_BULLET_HIT_WALL" ); + ByteToDir( es->eventParm, dir ); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD, es->otherEntityNum2, es->origin2[0], 0 ); + break; + + case EV_BULLET_HIT_FLESH: + DEBUGNAME( "EV_BULLET_HIT_FLESH" ); + CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm, es->otherEntityNum2, 0, 0 ); + break; + + case EV_POPUPBOOK: + case EV_POPUP: + case EV_GIVEPAGE: + break; + + case EV_GENERAL_SOUND: + DEBUGNAME( "EV_GENERAL_SOUND" ); + // Ridah, check for a sound script + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + if ( !strstr( s, ".wav" ) ) { + if ( CG_SoundPlaySoundScript( s, NULL, es->number, qfalse ) ) { + break; + } + // try with .wav + Q_strncpyz( tempStr, s, sizeof( tempStr ) ); + Q_strcat( tempStr, sizeof( tempStr ), ".wav" ); + s = tempStr; + } + + // done. + if ( cgs.gameSounds[ es->eventParm ] ) { + // xkan, 10/31/2002 - crank up the volume + trap_S_StartSoundVControl( NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ], 255 ); + } else { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + // xkan, 10/31/2002 - crank up the volume + trap_S_StartSoundVControl( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ), 255 ); + } + break; + + case EV_FX_SOUND: + { + sfxHandle_t sound; + + DEBUGNAME( "EV_FX_SOUND" ); + + sound = random() * fxSounds[ es->eventParm ].max; + + if ( fxSounds[ es->eventParm ].sound[ sound ] == -1 ) { + fxSounds[ es->eventParm ].sound[ sound ] = trap_S_RegisterSound( fxSounds[ es->eventParm ].soundfile[ sound ], qfalse ); + } + + sound = fxSounds[ es->eventParm ].sound[ sound ]; + + trap_S_StartSoundVControl( NULL, es->number, CHAN_VOICE, sound, 255 ); + } + break; + case EV_GENERAL_SOUND_VOLUME: + { + int sound = es->eventParm; + int volume = es->onFireStart; + + DEBUGNAME( "EV_GENERAL_SOUND_VOLUME" ); + // Ridah, check for a sound script + s = CG_ConfigString( CS_SOUNDS + sound ); + if ( !strstr( s, ".wav" ) ) { + if ( CG_SoundPlaySoundScript( s, NULL, es->number, qfalse ) ) { + break; + } + // try with .wav + Q_strncpyz( tempStr, s, sizeof( tempStr ) ); + Q_strcat( tempStr, sizeof( tempStr ), ".wav" ); + s = tempStr; + } + // done. + if ( cgs.gameSounds[ sound ] ) { + trap_S_StartSoundVControl( NULL, es->number, CHAN_VOICE, cgs.gameSounds[ sound ], volume ); + } else { + s = CG_ConfigString( CS_SOUNDS + sound ); + trap_S_StartSoundVControl( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ), volume ); + } + } + break; + + + case EV_GLOBAL_TEAM_SOUND: + DEBUGNAME( "EV_GLOBAL_TEAM_SOUND" ); + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team != es->teamNum ) { + break; + } + case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes + DEBUGNAME( "EV_GLOBAL_SOUND" ); + // Ridah, check for a sound script + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + if ( !strstr( s, ".wav" ) ) { + if ( CG_SoundPlaySoundScript( s, NULL, -1, qtrue ) ) { + break; + } + + // try with .wav + Q_strncpyz( tempStr, s, sizeof( tempStr ) ); + Q_strcat( tempStr, sizeof( tempStr ), ".wav" ); + s = tempStr; + } + + if ( cgs.gameSounds[ es->eventParm ] ) { + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); + } else { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); + } + break; + + // DHM - Nerve + case EV_GLOBAL_CLIENT_SOUND: + DEBUGNAME( "EV_GLOBAL_CLIENT_SOUND" ); + + if ( cg.snap->ps.clientNum == es->teamNum ) { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + if ( !strstr( s, ".wav" ) ) { + if ( CG_SoundPlaySoundScript( s, NULL, -1, ( es->effect1Time ? qfalse : qtrue ) ) ) { + break; + } + // try with .wav + Q_strncpyz( tempStr, s, sizeof( tempStr ) ); + Q_strcat( tempStr, sizeof( tempStr ), ".wav" ); + s = tempStr; + } + // done. + if ( cgs.gameSounds[ es->eventParm ] ) { + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] ); + } else { + s = CG_ConfigString( CS_SOUNDS + es->eventParm ); + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) ); + } + } + + break; + // dhm - end + + case EV_PAIN: + // local player sounds are triggered in CG_CheckLocalSounds, + // so ignore events on the player + DEBUGNAME( "EV_PAIN" ); + if ( cent->currentState.number != cg.snap->ps.clientNum ) { + CG_PainEvent( cent, es->eventParm, qfalse ); + } + break; + + case EV_CROUCH_PAIN: + // local player sounds are triggered in CG_CheckLocalSounds, + // so ignore events on the player + DEBUGNAME( "EV_PAIN" ); + if ( cent->currentState.number != cg.snap->ps.clientNum ) { + CG_PainEvent( cent, es->eventParm, qtrue ); + } + break; + + case EV_DEATH1: + case EV_DEATH2: + case EV_DEATH3: + DEBUGNAME( "EV_DEATHx" ); + trap_S_StartSound( NULL, es->number, CHAN_VOICE, + CG_CustomSound( es->number, va( "*death%i.wav", event - EV_DEATH1 + 1 ) ) ); + break; + + + case EV_OBITUARY: + DEBUGNAME( "EV_OBITUARY" ); + CG_Obituary( es ); + break; + + // JPW NERVE -- swiped from SP/Sherman + case EV_STOPSTREAMINGSOUND: + DEBUGNAME( "EV_STOPLOOPINGSOUND" ); +// trap_S_StopStreamingSound( es->number ); + trap_S_StartSoundEx( NULL, es->number, CHAN_WEAPON, 0, SND_CUTOFF_ALL ); // kill weapon sound (could be reloading) + break; + + case EV_LOSE_HAT: + DEBUGNAME( "EV_LOSE_HAT" ); + ByteToDir( es->eventParm, dir ); + CG_LoseHat( cent, dir ); + break; + + case EV_GIB_PLAYER: + DEBUGNAME( "EV_GIB_PLAYER" ); + trap_S_StartSound( es->pos.trBase, -1, CHAN_AUTO, cgs.media.gibSound ); + ByteToDir( es->eventParm, dir ); + CG_GibPlayer( cent, cent->lerpOrigin, dir ); + break; + + case EV_STOPLOOPINGSOUND: + DEBUGNAME( "EV_STOPLOOPINGSOUND" ); + es->loopSound = 0; + break; + + case EV_DEBUG_LINE: + DEBUGNAME( "EV_DEBUG_LINE" ); + CG_Beam( cent ); + break; + + // Rafael particles + case EV_SMOKE: + DEBUGNAME( "EV_SMOKE" ); + if ( cent->currentState.density == 3 ) { + CG_ParticleSmoke( cgs.media.smokePuffShaderdirty, cent ); + } else if ( !( cent->currentState.density ) ) { + CG_ParticleSmoke( cgs.media.smokePuffShader, cent ); + } else { + CG_ParticleSmoke( cgs.media.smokePuffShader, cent ); + } + break; + + case EV_FLAMETHROWER_EFFECT: + CG_FireFlameChunks( cent, cent->currentState.origin, cent->currentState.apos.trBase, 0.6, 2 ); + break; + + case EV_DUST: + CG_ParticleDust( cent, cent->currentState.origin, cent->currentState.angles ); + break; + + case EV_RUMBLE_EFX: + { + float pitch, yaw; + pitch = cent->currentState.angles[0]; + yaw = cent->currentState.angles[1]; + CG_RumbleEfx( pitch, yaw ); + } + break; + + case EV_CONCUSSIVE: + CG_Concussive( cent ); + break; + + case EV_EMITTER: + { + localEntity_t *le; + le = CG_AllocLocalEntity(); + le->leType = LE_EMITTER; + le->startTime = cg.time; + le->endTime = le->startTime + 20000; + le->pos.trType = TR_STATIONARY; + VectorCopy( cent->currentState.origin, le->pos.trBase ); + VectorCopy( cent->currentState.origin2, le->angles.trBase ); + le->ownerNum = 0; + } + break; + + case EV_OILPARTICLES: + CG_Particle_OilParticle( cgs.media.oilParticle, cent->currentState.origin, cent->currentState.origin2, cent->currentState.time, cent->currentState.density ); + break; + case EV_OILSLICK: + CG_Particle_OilSlick( cgs.media.oilSlick, cent ); + break; + case EV_OILSLICKREMOVE: + CG_OilSlickRemove( cent ); + break; + + case EV_MG42EFX: + CG_MG42EFX( cent ); + break; + + case EV_SPARKS_ELECTRIC: + case EV_SPARKS: + { + int numsparks; + int i; + int duration; + float x,y; + float speed; + vec3_t source, dest; + + if ( !( cent->currentState.density ) ) { + cent->currentState.density = 1; + } + numsparks = rand() % cent->currentState.density; + duration = cent->currentState.frame; + x = cent->currentState.angles2[0]; + y = cent->currentState.angles2[1]; + speed = cent->currentState.angles2[2]; + + if ( !numsparks ) { + numsparks = 1; + } + for ( i = 0; i < numsparks; i++ ) + { + + if ( event == EV_SPARKS_ELECTRIC ) { + VectorCopy( cent->currentState.origin, source ); + + VectorCopy( source, dest ); + dest[0] += ( ( rand() & 31 ) - 16 ); + dest[1] += ( ( rand() & 31 ) - 16 ); + dest[2] += ( ( rand() & 31 ) - 16 ); + + CG_Tracer( source, dest, 1 ); + } else { + CG_ParticleSparks( cent->currentState.origin, cent->currentState.angles, duration, x, y, speed ); + } + + } + + } + break; + + case EV_GUNSPARKS: + { + int numsparks; + int speed; + //int count; + + numsparks = cent->currentState.density; + speed = cent->currentState.angles2[2]; + + CG_AddBulletParticles( cent->currentState.origin, cent->currentState.angles, speed, 800, numsparks, 1.0f ); + + } + break; + + // Rafael snow pvs check + case EV_SNOW_ON: + CG_SnowLink( cent, qtrue ); + break; + + case EV_SNOW_OFF: + CG_SnowLink( cent, qfalse ); + break; + + + case EV_SNOWFLURRY: + CG_ParticleSnowFlurry( cgs.media.snowShader, cent ); + break; + + // for func_exploding + case EV_EXPLODE: + DEBUGNAME( "EV_EXPLODE" ); + ByteToDir( es->eventParm, dir ); + CG_Explode( cent, position, dir, 0 ); + break; + + case EV_RUBBLE: + DEBUGNAME( "EV_RUBBLE" ); + ByteToDir( es->eventParm, dir ); + CG_Rubble( cent, position, dir, 0 ); + break; + + // for target_effect + case EV_EFFECT: + DEBUGNAME( "EV_EFFECT" ); + ByteToDir( es->eventParm, dir ); + CG_Effect( cent, position, dir ); + break; + + case EV_MORTAREFX: // mortar firing + DEBUGNAME( "EV_MORTAREFX" ); + CG_MortarEFX( cent ); + break; + + case EV_SHARD: + ByteToDir( es->eventParm, dir ); + CG_Shard( cent, position, dir ); + break; + + case EV_JUNK: + ByteToDir( es->eventParm, dir ); + { + int i; + int rval; + + rval = rand() % 3 + 3; + + for ( i = 0; i < rval; i++ ) + CG_ShardJunk( cent, position, dir ); + } + break; + + case EV_DISGUISE_SOUND: + trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cgs.media.uniformPickup ); + break; + case EV_BUILDDECAYED_SOUND: + trap_S_StartSound( cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, cgs.media.buildDecayedSound ); + break; + + // Gordon: debris test + case EV_DEBRIS: + CG_Debris( cent, position, cent->currentState.origin2 ); + break; + // =================== + + case EV_SHAKE: + { + vec3_t v; + float len; + + DEBUGNAME( "EV_SHAKE" ); + + VectorSubtract( cg.snap->ps.origin, cent->lerpOrigin, v ); + len = VectorLength( v ); + + if ( len > cent->currentState.onFireStart ) { + break; + } + + len = 1.0f - ( len / (float)cent->currentState.onFireStart ); + len = min( 1.f, len ); + + CG_StartShakeCamera( len ); + } + + break; + + case EV_ALERT_SPEAKER: + DEBUGNAME( "EV_ALERT_SPEAKER" ); + switch ( cent->currentState.otherEntityNum2 ) + { + case 1: CG_UnsetActiveOnScriptSpeaker( cent->currentState.otherEntityNum ); break; + case 2: CG_SetActiveOnScriptSpeaker( cent->currentState.otherEntityNum ); break; + case 0: + default: CG_ToggleActiveOnScriptSpeaker( cent->currentState.otherEntityNum ); break; + } + break; + + case EV_POPUPMESSAGE: + { + const char* str = CG_GetPMItemText( cent ); + qhandle_t shader = CG_GetPMItemIcon( cent ); + if ( str ) { + CG_AddPMItem( cent->currentState.effect1Time, str, shader ); + } + CG_PlayPMItemSound( cent ); + } + break; + + case EV_AIRSTRIKEMESSAGE: + { + const char* wav = NULL; + + switch ( cent->currentState.density ) { + case 0: // too many called + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_airstrike_denied"; + } else { + wav = "allies_hq_airstrike_denied"; + } + break; + case 1: // aborting can't see target + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_airstrike_abort"; + } else { + wav = "allies_hq_airstrike_abort"; + } + break; + case 2: // firing for effect + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_airstrike"; + } else { + wav = "allies_hq_airstrike"; + } + break; + } + + if ( wav ) { + CG_SoundPlaySoundScript( wav, NULL, -1, ( es->effect1Time ? qfalse : qtrue ) ); + } + } + break; + + case EV_ARTYMESSAGE: + { + const char* wav = NULL; + + switch ( cent->currentState.density ) { + case 0: // too many called + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_ffe_denied"; + } else { + wav = "allies_hq_ffe_denied"; + } + break; + case 1: // aborting can't see target + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_ffe_abort"; + } else { + wav = "allies_hq_ffe_abort"; + } + break; + case 2: // firing for effect + if ( cgs.clientinfo[ cg.snap->ps.clientNum ].team == TEAM_AXIS ) { + wav = "axis_hq_ffe"; + } else { + wav = "allies_hq_ffe"; + } + break; + } + + if ( wav ) { + CG_SoundPlaySoundScript( wav, NULL, -1, ( es->effect1Time ? qfalse : qtrue ) ); + } + } + break; + + case EV_MEDIC_CALL: + switch ( cgs.clientinfo[ cent->currentState.number ].team ) { + case TEAM_AXIS: + trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.sndMedicCall[0] ); + break; + case TEAM_ALLIES: + trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.sndMedicCall[1] ); + break; + default: // shouldn't happen + break; + } + + break; + + default: + DEBUGNAME( "UNKNOWN" ); + CG_Error( "Unknown event: %i", event ); + break; + } + + + { + int rval; + + rval = rand() & 3; + + if ( splashfootstepcnt != rval ) { + splashfootstepcnt = rval; + } else { + splashfootstepcnt++; + } + + if ( splashfootstepcnt > 3 ) { + splashfootstepcnt = 0; + } + + + if ( footstepcnt != rval ) { + footstepcnt = rval; + } else { + footstepcnt++; + } + + if ( footstepcnt > 3 ) { + footstepcnt = 0; + } + } +} + + +/* +============== +CG_CheckEvents + +============== +*/ +void CG_CheckEvents( centity_t *cent ) { + int i, event; + + // calculate the position at exactly the frame time + BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin, qfalse, cent->currentState.effect2Time ); + CG_SetEntitySoundPosition( cent ); + + // check for event-only entities + if ( cent->currentState.eType > ET_EVENTS ) { + if ( cent->previousEvent ) { + //goto skipEvent; + return; // already fired + } + // if this is a player event set the entity number of the client entity number +//(SA) note: EF_PLAYER_EVENT never set +// if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { +// cent->currentState.number = cent->currentState.otherEntityNum; +// } + + cent->previousEvent = 1; + + cent->currentState.event = cent->currentState.eType - ET_EVENTS; + } else { + + // DHM - Nerve :: Entities that make it here are Not TempEntities. + // As far as we could tell, for all non-TempEntities, the + // circular 'events' list contains the valid events. So we + // skip processing the single 'event' field and go straight + // to the circular list. + + goto skipEvent; + /* + // check for events riding with another entity + if ( cent->currentState.event == cent->previousEvent ) { + goto skipEvent; + //return; + } + cent->previousEvent = cent->currentState.event; + if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { + goto skipEvent; + //return; + } + */ + // dhm - end + } + + CG_EntityEvent( cent, cent->lerpOrigin ); + // DHM - Nerve :: Temp ents return after processing + return; + +skipEvent: + + // check the sequencial list + // if we've added more events than can fit into the list, make sure we only add them once + if ( cent->currentState.eventSequence < cent->previousEventSequence ) { + cent->previousEventSequence -= ( 1 << 8 ); // eventSequence is sent as an 8-bit through network stream + } + if ( cent->currentState.eventSequence - cent->previousEventSequence > MAX_EVENTS ) { + cent->previousEventSequence = cent->currentState.eventSequence - MAX_EVENTS; + } + for ( i = cent->previousEventSequence ; i != cent->currentState.eventSequence; i++ ) { + event = cent->currentState.events[ i & ( MAX_EVENTS - 1 ) ]; + + cent->currentState.event = event; + cent->currentState.eventParm = cent->currentState.eventParms[ i & ( MAX_EVENTS - 1 ) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + } + cent->previousEventSequence = cent->currentState.eventSequence; + + // set the event back so we don't think it's changed next frame (unless it really has) + cent->currentState.event = cent->previousEvent; +} diff --git a/src/cgame/cg_fireteamoverlay.c b/src/cgame/cg_fireteamoverlay.c new file mode 100644 index 0000000..177f6cc --- /dev/null +++ b/src/cgame/cg_fireteamoverlay.c @@ -0,0 +1,559 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +/****************************************************************************** +***** teh firetams! (sic) +****/ + +#include "cg_local.h" + +/****************************************************************************** +***** Defines, constants, etc +****/ + +static int sortedFireTeamClients[MAX_CLIENTS]; + +/****************************************************************************** +***** Support Routines +****/ + +int QDECL CG_SortFireTeam( const void *a, const void *b ) { + clientInfo_t *ca, *cb; + int cna, cnb; + + cna = *(int*)a; + cnb = *(int*)b; + + ca = &cgs.clientinfo[cna]; + cb = &cgs.clientinfo[cnb]; + + // Not on our team, so shove back + if ( !CG_IsOnSameFireteam( cnb, cg.clientNum ) ) { + return -1; + } + if ( !CG_IsOnSameFireteam( cna, cg.clientNum ) ) { + return 1; + } + + // Leader comes first + if ( CG_IsFireTeamLeader( cna ) ) { + return -1; + } + if ( CG_IsFireTeamLeader( cnb ) ) { + return 1; + } + + // Then higher ranks + if ( ca->rank > cb->rank ) { + return -1; + } + if ( cb->rank > ca->rank ) { + return 1; + } + + // Then score +/* if ( ca->score > cb->score ) { + return -1; + } + if ( cb->score > ca->score ) { + return 1; + }*/ // not atm + + return 0; +} + +// Sorts client's fireteam by leader then rank +void CG_SortClientFireteam() { + int i; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + sortedFireTeamClients[i] = i; + } + + qsort( sortedFireTeamClients, MAX_CLIENTS, sizeof( sortedFireTeamClients[0] ), CG_SortFireTeam ); + +/* for(i = 0; i < MAX_CLIENTS; i++) { + CG_Printf( "%i ", sortedFireTeamClients[i] ); + } + + CG_Printf( "\n" );*/ +} + +// Parses fireteam servercommand +void CG_ParseFireteams() { + int i, j; + char* s; + const char* p; + int clnts[2]; + + qboolean onFireteam2; + qboolean isLeader2; + +// qboolean onFireteam = CG_IsOnFireteam( cg.clientNum ) ? qtrue : qfalse; +// qboolean isLeader = CG_IsFireTeamLeader( cg.clientNum ) ? qtrue : qfalse; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + cgs.clientinfo[i].fireteamData = NULL; + } + + for ( i = 0; i < MAX_FIRETEAMS; i++ ) { + char hexbuffer[11] = "0x00000000"; + p = CG_ConfigString( CS_FIRETEAMS + i ); + +/* s = Info_ValueForKey(p, "n"); + if(!s || !*s) { + cg.fireTeams[i].inuse = qfalse; + continue; + } else { + cg.fireTeams[i].inuse = qtrue; + }*/ + +// Q_strncpyz(cg.fireTeams[i].name, s, 32); +// CG_Printf("Fireteam: %s\n", cg.fireTeams[i].name); + + j = atoi( Info_ValueForKey( p, "id" ) ); + if ( j == -1 ) { + cg.fireTeams[i].inuse = qfalse; + continue; + } else { + cg.fireTeams[i].inuse = qtrue; + cg.fireTeams[i].ident = j; + } + + s = Info_ValueForKey( p, "l" ); + cg.fireTeams[i].leader = atoi( s ); + + s = Info_ValueForKey( p, "c" ); + Q_strncpyz( hexbuffer + 2, s, 9 ); + sscanf( hexbuffer, "%x", &clnts[1] ); + Q_strncpyz( hexbuffer + 2, s + 8, 9 ); + sscanf( hexbuffer, "%x", &clnts[0] ); + + for ( j = 0; j < MAX_CLIENTS; j++ ) { + if ( COM_BitCheck( clnts, j ) ) { + cg.fireTeams[i].joinOrder[j] = qtrue; + cgs.clientinfo[j].fireteamData = &cg.fireTeams[i]; +// CG_Printf("%s\n", cgs.clientinfo[j].name); + } else { + cg.fireTeams[i].joinOrder[j] = qfalse; + } + } + } + + CG_SortClientFireteam(); + + onFireteam2 = CG_IsOnFireteam( cg.clientNum ) ? qtrue : qfalse; + isLeader2 = CG_IsFireTeamLeader( cg.clientNum ) ? qtrue : qfalse; +} + +// Fireteam that both specified clients are on, if they both are on the same team +fireteamData_t* CG_IsOnSameFireteam( int clientNum, int clientNum2 ) { + if ( CG_IsOnFireteam( clientNum ) == CG_IsOnFireteam( clientNum2 ) ) { + return CG_IsOnFireteam( clientNum ); + } + + return NULL; +} + +// Fireteam that specified client is leader of, or NULL if none +fireteamData_t* CG_IsFireTeamLeader( int clientNum ) { + fireteamData_t* f; + + if ( !( f = CG_IsOnFireteam( clientNum ) ) ) { + return NULL; + } + + if ( f->leader != clientNum ) { + return NULL; + } + + return f ; +} + +// Client, not on a fireteam, not sorted, but on your team +clientInfo_t* CG_ClientInfoForPosition( int pos, int max ) { + int i, cnt = 0; + + for ( i = 0; i < MAX_CLIENTS && cnt < max; i++ ) { + if ( cg.clientNum != i && cgs.clientinfo[i].infoValid && !CG_IsOnFireteam( i ) && cgs.clientinfo[cg.clientNum].team == cgs.clientinfo[i].team ) { + if ( cnt == pos ) { + return &cgs.clientinfo[i]; + } + cnt++; + } + } + + return NULL; +} + +// Fireteam, that's on your same team +fireteamData_t* CG_FireTeamForPosition( int pos, int max ) { + int i, cnt = 0; + + for ( i = 0; i < MAX_FIRETEAMS && cnt < max; i++ ) { + if ( cg.fireTeams[i].inuse && cgs.clientinfo[cg.fireTeams[i].leader].team == cgs.clientinfo[cg.clientNum].team ) { + if ( cnt == pos ) { + return &cg.fireTeams[i]; + } + cnt++; + } + } + + return NULL; +} + +// Client, not sorted by rank, on CLIENT'S fireteam +clientInfo_t* CG_FireTeamPlayerForPosition( int pos, int max ) { + int i, cnt = 0; + fireteamData_t* f = CG_IsOnFireteam( cg.clientNum ); + + if ( !f ) { + return NULL; + } + + for ( i = 0; i < MAX_CLIENTS && cnt < max; i++ ) { + if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[cg.clientNum].team == cgs.clientinfo[i].team ) { + if ( !( f == CG_IsOnFireteam( i ) ) ) { + continue; + } + + if ( cnt == pos ) { + return &cgs.clientinfo[i]; + } + cnt++; + } + } + + return NULL; +} + +// Client, sorted by rank, on CLIENT'S fireteam +clientInfo_t* CG_SortedFireTeamPlayerForPosition( int pos, int max ) { + int i, cnt = 0; + fireteamData_t* f = CG_IsOnFireteam( cg.clientNum ); + + if ( !f ) { + return NULL; + } + + for ( i = 0; i < MAX_CLIENTS && cnt < max; i++ ) { + if ( !( f == CG_IsOnFireteam( sortedFireTeamClients[i] ) ) ) { + return NULL; + } + + if ( cnt == pos ) { + return &cgs.clientinfo[sortedFireTeamClients[i]]; + } + cnt++; + } + + return NULL; +} + +/****************************************************************************** +***** Main Functions +****/ + +#define FT_BAR_YSPACING 2.f +#define FT_BAR_HEIGHT 10.f +void CG_DrawFireTeamOverlay( rectDef_t* rect ) { + int x = rect->x; + int y = rect->y + 1; // +1, jitter it into place in 1024 :) + float h; + clientInfo_t* ci = NULL; + char buffer[64]; + fireteamData_t* f = NULL; + int i; + vec4_t clr1 = { .16f, .2f, .17f, .8f }; + vec4_t clr2 = { 0.f, 0.f, 0.f, .2f }; + vec4_t clr3 = { 0.25f, 0.f, 0.f, 153 / 255.f }; + vec4_t tclr = { 0.6f, 0.6f, 0.6f, 1.0f }; + vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.6f }; // window + vec4_t borderColor = { 0.5f, 0.5f, 0.5f, 0.5f }; // window + + if ( !( f = CG_IsOnFireteam( cg.clientNum ) ) ) { + return; + } + + h = 12 + 2 + 2; + for ( i = 0; i < 6; i++ ) { + ci = CG_SortedFireTeamPlayerForPosition( i, 6 ); + if ( !ci ) { + break;; + } + + h += FT_BAR_HEIGHT + FT_BAR_YSPACING; + } + + CG_DrawRect( x, y, 204, h, 1, borderColor ); + CG_FillRect( x + 1, y + 1, 204 - 2, h - 2, bgColor ); + + x += 2; + y += 2; + + CG_FillRect( x, y, 204 - 4, 12, clr1 ); + + sprintf( buffer, "Fireteam: %s", bg_fireteamNames[f->ident] ); + Q_strupr( buffer ); + CG_Text_Paint_Ext( x + 3, y + FT_BAR_HEIGHT, .19f, .19f, tclr, buffer, 0, 0, 0, &cgs.media.limboFont1 ); + + x += 2; + //y += 2; + + for ( i = 0; i < 6; i++ ) { + y += FT_BAR_HEIGHT + FT_BAR_YSPACING; + x = rect->x + 2; + + ci = CG_SortedFireTeamPlayerForPosition( i, 6 ); + if ( !ci ) { + break;; + } + + if ( ci->selected ) { + CG_FillRect( x, y + FT_BAR_YSPACING, 204 - 4, FT_BAR_HEIGHT, clr3 ); + } else { + CG_FillRect( x, y + FT_BAR_YSPACING, 204 - 4, FT_BAR_HEIGHT, clr2 ); + } + + x += 4; + + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, BG_ClassLetterForNumber( ci->cls ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + x += 10; + + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, ci->team == TEAM_AXIS ? miniRankNames_Axis[ci->rank] : miniRankNames_Allies[ci->rank], 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + x += 22; + + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, ci->name, 0, 17, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + x += 90; + +/* CG_DrawPic(x + 2, y + 2, FT_BAR_HEIGHT - 4, FT_BAR_HEIGHT - 4, cgs.media.movementAutonomyIcons[0]); + x += FT_BAR_HEIGHT; + + CG_DrawPic(x + 2, y + 2, FT_BAR_HEIGHT - 4, FT_BAR_HEIGHT - 4, cgs.media.weaponAutonomyIcons[0]); + x += FT_BAR_HEIGHT; + x += 4;*/ + +/* if( isLeader ) { + CG_Text_Paint_Ext(x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, va("%i", i+4), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + }*/ + x += 20; + + if ( ci->health > 80 ) { + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, va( "%i", ci->health < 0 ? 0 : ci->health ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } else if ( ci->health > 0 ) { + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, colorYellow, va( "%i", ci->health < 0 ? 0 : ci->health ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } else { + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, colorRed, va( "%i", ci->health < 0 ? 0 : ci->health ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } + //x += 20; + + { + vec2_t loc; + char *s; + + loc[0] = ci->location[0]; + loc[1] = ci->location[1]; + + s = va( "^3(%s)", BG_GetLocationString( loc ) ); + + x = rect->x + ( 204 - 4 - CG_Text_Width_Ext( s, .2f, 0, &cgs.media.limboFont2 ) ); + + CG_Text_Paint_Ext( x, y + FT_BAR_HEIGHT, .2f, .2f, tclr, va( "^3(%s)", BG_GetLocationString( loc ) ), 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } + } +} + +qboolean CG_FireteamGetBoxNeedsButtons() { + if ( cgs.applicationEndTime > cg.time ) { + if ( cgs.applicationClient < 0 ) { + return qfalse; + } + return qtrue; + } + + if ( cgs.invitationEndTime > cg.time ) { + if ( cgs.invitationClient < 0 ) { + return qfalse; + } + return qtrue; + } + + if ( cgs.propositionEndTime > cg.time ) { + if ( cgs.propositionClient < 0 ) { + return qfalse; + } + return qtrue; + } + + return qfalse; +} + +const char* CG_FireteamGetBoxText() { + if ( cgs.applicationEndTime > cg.time ) { + if ( cgs.applicationClient == -1 ) { + return "Sent"; + } + + if ( cgs.applicationClient == -2 ) { + return "Failed"; + } + + if ( cgs.applicationClient == -3 ) { + return "Accepted"; + } + + if ( cgs.applicationClient == -4 ) { + return "Sent"; + } + + if ( cgs.applicationClient < 0 ) { + return NULL; + } + + return va( "Accept application from %s?", cgs.clientinfo[cgs.applicationClient].name ); + } + + if ( cgs.invitationEndTime > cg.time ) { + if ( cgs.invitationClient == -1 ) { + return "Sent"; + } + + if ( cgs.invitationClient == -2 ) { + return "Failed"; + } + + if ( cgs.invitationClient == -3 ) { + return "Accepted"; + } + + if ( cgs.invitationClient == -4 ) { + return "Sent"; + } + + if ( cgs.invitationClient < 0 ) { + return NULL; + } + + return va( "Accept invitiation from %s?", cgs.clientinfo[cgs.invitationClient].name ); + } + + if ( cgs.propositionEndTime > cg.time ) { + if ( cgs.propositionClient == -1 ) { + return "Sent"; + } + + if ( cgs.propositionClient == -2 ) { + return "Failed"; + } + + if ( cgs.propositionClient == -3 ) { + return "Accepted"; + } + + if ( cgs.propositionClient == -4 ) { + return "Sent"; + } + + if ( cgs.propositionClient < 0 ) { + return NULL; + } + + return va( "Accept %s's proposition to invite %s to join your fireteam?", cgs.clientinfo[cgs.propositionClient2].name, cgs.clientinfo[cgs.propositionClient].name ); + } + + return NULL; +} + +qboolean CG_FireteamHasClass( int classnum, qboolean selectedonly ) { + fireteamData_t* ft; + int i; + + if ( !( ft = CG_IsOnFireteam( cg.clientNum ) ) ) { + return qfalse; + } + + for ( i = 0; i < MAX_CLIENTS; i++ ) { +/* if( i == cgs.clientinfo ) { + continue; + }*/ + + if ( !cgs.clientinfo[ i ].infoValid ) { + continue; + } + + if ( ft != CG_IsOnFireteam( i ) ) { + continue; + } + + if ( cgs.clientinfo[ i ].cls != classnum ) { + continue; + } + + if ( selectedonly && !cgs.clientinfo[ i ].selected ) { + continue; + } + + return qtrue; + } + + return qfalse; +} + +const char* CG_BuildSelectedFirteamString( void ) { + char buffer[256]; + clientInfo_t* ci; + int cnt = 0; + int i; + + *buffer = '\0'; + for ( i = 0; i < 6; i++ ) { + ci = CG_SortedFireTeamPlayerForPosition( i, 6 ); + if ( !ci ) { + break; + } + + if ( !ci->selected ) { + continue; + } + + cnt++; + + Q_strcat( buffer, sizeof( buffer ), va( "%i ", ci->clientNum ) ); + } + + if ( cnt == 0 ) { + return "0"; + } + + if ( !cgs.clientinfo[cg.clientNum].selected ) { + Q_strcat( buffer, sizeof( buffer ), va( "%i ", cg.clientNum ) ); + cnt++; + } + + return va( "%i %s", cnt, buffer ); +} diff --git a/src/cgame/cg_fireteams.c b/src/cgame/cg_fireteams.c new file mode 100644 index 0000000..76dba2c --- /dev/null +++ b/src/cgame/cg_fireteams.c @@ -0,0 +1,1237 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "cg_local.h" + +panel_button_text_t fireteamTitleFont = { + 0.19f, 0.19f, + { 0.6f, 0.6f, 0.6f, 1.f }, + 0, 0, + &cgs.media.limboFont1_lo, +}; + +panel_button_text_t fireteamFont = { + 0.2f, 0.2f, + { 0.6f, 0.6f, 0.6f, 1.f }, + ITEM_TEXTSTYLE_SHADOWED, 0, + &cgs.media.limboFont2, +}; + +panel_button_t fireteamTopBorder = { + NULL, + NULL, + { 10, 129, 204, 136 }, + { 1, 255 * .5f, 255 * .5f, 255 * .5f, 255 * .5f, 1, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t fireteamTopBorderBack = { + "white", + NULL, + { 11, 130, 202, 134 }, + { 1, 0, 0, 0, 255 * 0.75f, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t fireteamTopBorderInner = { + "white", + NULL, + { 12, 131, 200, 12 }, + { 1, 41, 51, 43, 204, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t fireteamTopBorderInnerText = { + NULL, + NULL, + { 15, 141, 200, 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &fireteamTitleFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Fireteams_MenuTitleText_Draw, + NULL, +}; + +panel_button_t fireteamMenuItemText = { + NULL, + NULL, + { 16, 153, 128, 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &fireteamFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_Fireteams_MenuText_Draw, + NULL, +}; + + +panel_button_t* fireteamButtons[] = { + &fireteamTopBorderBack, &fireteamTopBorder, &fireteamTopBorderInner, &fireteamTopBorderInnerText, + + &fireteamMenuItemText, + + NULL +}; + +const char* ftMenuRootStrings[] = { + "Soldier", + "Medic", + "Engineer", + "Field Ops", + "Covert Ops", + "General", + "Attack", + "Fall Back", + NULL +}; + +const char* ftMenuRootStringsMsg[] = { + "", + "", + "", + "", + "", + "", + "FTAttack", + "FTFallBack", + NULL +}; + +const char* ftMenuRootStringsAlphachars[] = { + "S", + "M", + "E", + "F", + "C", + "G", + "A", + "B", + NULL +}; + + + +const char* ftMenuSoliderStrings[] = { + "Cover Me", + "Covering Fire", + "Mortar", + NULL +}; + +const char* ftMenuSoliderStringsAlphachars[] = { + "C", + "F", + "M", + NULL +}; + +const char* ftMenuSoliderStringsMsg[] = { + "FTCoverMe", + "FTCoveringFire", + "FTMortar", + NULL +}; + + + +const char* ftMenuMedicStrings[] = { + "Heal Squad", + "Heal Me", + "Revive Team Mate", + "Revive Me", + NULL +}; + +const char* ftMenuMedicStringsAlphachars[] = { + "H", + "M", + "R", + "E", + NULL +}; + +const char* ftMenuMedicStringsMsg[] = { + "FTHealSquad", + "FTHealMe", + "FTReviveTeamMate", + "FTReviveMe", + NULL +}; + + +const char* ftMenuEngineerStrings[] = { + "Destroy Objective", + "Repair Objective", + "Construct Objective", + "Disarm Dynamite", + "Deploy Landmines", + "Disarm Landmines", + NULL +}; + +const char* ftMenuEngineerStringsAlphachars[] = { + "D", + "R", + "C", + "A", + "L", + "M", + NULL +}; + +const char* ftMenuEngineerStringsMsg[] = { + "FTDestroyObjective", + "FTRepairObjective", + "FTConstructObjective", + "FTDisarmDynamite", + "FTDeployLandmines", + "FTDisarmLandmines", + NULL +}; + + +const char* ftMenuFieldOpsStrings[] = { + "Call Air-Strike", + "Call Artillery", + "Resupply Squad", + "Resupply Me", + NULL +}; + +const char* ftMenuFieldOpsStringsAlphachars[] = { + "A", + "T", + "R", + "S", + NULL +}; + +const char* ftMenuFieldOpsStringsMsg[] = { + "FTCallAirStrike", + "FTCallArtillery", + "FTResupplySquad", + "FTResupplyMe", + NULL +}; + + +const char* ftMenuCovertOpsStrings[] = { + "Explore Area", + "Destroy Objective", + "Infiltrate", + "Go Undercover", + "Provide Sniper Cover", + NULL +}; + +const char* ftMenuCovertOpsStringsAlphachars[] = { + "E", + "D", + "I", + "U", + "S", + NULL +}; + +const char* ftMenuCovertOpsStringsMsg[] = { + "FTExploreArea", + "FTSatchelObjective", + "FTInfiltrate", + "FTGoUndercover", + "FTProvideSniperCover", + NULL +}; + +const char** ftMenuStrings[] = { + ftMenuSoliderStrings, + ftMenuMedicStrings, + ftMenuEngineerStrings, + ftMenuFieldOpsStrings, + ftMenuCovertOpsStrings, +}; + +const char** ftMenuStringsAlphachars[] = { + ftMenuSoliderStringsAlphachars, + ftMenuMedicStringsAlphachars, + ftMenuEngineerStringsAlphachars, + ftMenuFieldOpsStringsAlphachars, + ftMenuCovertOpsStringsAlphachars, +}; + +const char** ftMenuStringsMsg[] = { + ftMenuSoliderStringsMsg, + ftMenuMedicStringsMsg, + ftMenuEngineerStringsMsg, + ftMenuFieldOpsStringsMsg, + ftMenuCovertOpsStringsMsg, +}; + +void CG_Fireteams_MenuTitleText_Draw( panel_button_t* button ) { + switch ( cgs.ftMenuMode ) { + case 0: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "MESSAGE", 0, 0, button->font->style, button->font->font ); + break; + case 1: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "FIRETEAMS", 0, 0, button->font->style, button->font->font ); + break; + case 2: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "JOIN", 0, 0, button->font->style, button->font->font ); + break; + case 3: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "PROPOSE", 0, 0, button->font->style, button->font->font ); + break; + case 4: + switch ( cgs.ftMenuPos ) { + case 2: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "INVITE", 0, 0, button->font->style, button->font->font ); + break; + case 3: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "KICK", 0, 0, button->font->style, button->font->font ); + break; + case 4: + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->data[0], button->font->scalex, button->font->scaley, button->font->colour, "WARN", 0, 0, button->font->style, button->font->font ); + break; + } + } +} + +const char* ftOffMenuList[] = { + "Apply", + "Create", + NULL, +}; + +const char* ftOffMenuListAlphachars[] = { + "A", + "C", + NULL, +}; + +const char* ftOnMenuList[] = { + "Propose", + "Leave", + NULL, +}; + +const char* ftOnMenuListAlphachars[] = { + "P", + "L", + NULL, +}; + +const char* ftLeaderMenuList[] = { + "Disband", + "Leave", + "Invite", + "Kick", + "Warn", + NULL, +}; + +const char* ftLeaderMenuListAlphachars[] = { + "D", + "L", + "I", + "K", + "W", + NULL, +}; + +int CG_CountFireteamsByTeam( team_t t ) { + int cnt = 0; + int i; + + if ( t != TEAM_AXIS && t != TEAM_ALLIES ) { + return 0; + } + + for ( i = 0; i < MAX_FIRETEAMS; i++ ) { + if ( !cg.fireTeams[i].inuse ) { + continue; + } + + if ( cgs.clientinfo[cg.fireTeams[i].leader].team != t ) { + continue; + } + + cnt++; + } + + return cnt; +} + +void CG_DrawFireteamsByTeam( panel_button_t* button, team_t t ) { + float y = button->rect.y; + const char* str; + int i; + + if ( t != TEAM_AXIS && t != TEAM_ALLIES ) { + return; + } + + for ( i = 0; i < MAX_FIRETEAMS; i++ ) { + if ( !cg.fireTeams[i].inuse ) { + continue; + } + + if ( cgs.clientinfo[cg.fireTeams[i].leader].team != t ) { + continue; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( cg.fireTeams[i].ident + 1 ) % 10, bg_fireteamNames[cg.fireTeams[i].ident] ); + } else { + str = va( "%c. %s", 'A' + cg.fireTeams[i].ident, bg_fireteamNames[cg.fireTeams[i].ident] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } +} + +int CG_CountPlayersSF( void ) { + int i, cnt = 0; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( i == cg.clientNum ) { + continue; + } + + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.clientNum].team ) { + continue; + } + + if ( CG_IsOnFireteam( i ) != CG_IsOnFireteam( cg.clientNum ) ) { + continue; + } + + cnt++; + } + + return cnt; +} + + +int CG_CountPlayersNF( void ) { + int i, cnt = 0; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( i == cg.clientNum ) { + continue; + } + + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.clientNum].team ) { + continue; + } + + if ( CG_IsOnFireteam( i ) ) { + continue; + } + + cnt++; + } + + return cnt; +} + +int CG_PlayerSFFromPos( int pos, int* pageofs ) { + int x, i; + + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + *pageofs = 0; + return -1; + } + + x = CG_CountPlayersSF(); + if ( x < ( ( *pageofs ) * 8 ) ) { + *pageofs = 0; + } + + x = 0; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( i == cg.clientNum ) { + continue; + } + + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.clientNum].team ) { + continue; + } + + if ( CG_IsOnFireteam( i ) != CG_IsOnFireteam( cg.clientNum ) ) { + continue; + } + + if ( x >= ( ( *pageofs ) * 8 ) && x < ( ( *pageofs + 1 ) * 8 ) ) { + int ofs = x - ( ( *pageofs ) * 8 ); + + if ( pos == ofs ) { + return i; + } + } + + x++; + } + + return -1; +} + +int CG_PlayerNFFromPos( int pos, int* pageofs ) { + int x, i; + + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + *pageofs = 0; + return -1; + } + + x = CG_CountPlayersNF(); + if ( x < ( ( *pageofs ) * 8 ) ) { + *pageofs = 0; + } + + x = 0; + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( i == cg.clientNum ) { + continue; + } + + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != cgs.clientinfo[cg.clientNum].team ) { + continue; + } + + if ( CG_IsOnFireteam( i ) ) { + continue; + } + + if ( x >= ( ( *pageofs ) * 8 ) && x < ( ( *pageofs + 1 ) * 8 ) ) { + int ofs = x - ( ( *pageofs ) * 8 ); + + if ( pos == ofs ) { + return i; + } + } + + x++; + } + + return -1; +} + +void CG_DrawPlayerSF( panel_button_t* button, int* pageofs ) { + float y = button->rect.y; + const char* str; + int i, x; + + for ( i = 0; i < 8; i++ ) { + x = CG_PlayerSFFromPos( i, pageofs ); + if ( x == -1 ) { + break; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, cgs.clientinfo[x].name ); + } else { + str = va( "%c. %s", 'A' + i, cgs.clientinfo[x].name ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + + if ( *pageofs ) { + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( 8 + 1 ) % 10, "Previous" ); + } else { + str = va( "%c. %s", 'P', "Previous" ); + } + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + + y += button->rect.h; + } + + if ( CG_CountPlayersSF() > ( *pageofs + 1 ) * 8 ) { + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( 9 + 1 ) % 10, "Next" ); + } else { + str = va( "%c. %s", 'N', "Next" ); + } + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + + y += button->rect.h; + } +} + +void CG_DrawPlayerNF( panel_button_t* button, int* pageofs ) { + float y = button->rect.y; + const char* str; + int i, x; + + for ( i = 0; i < 8; i++ ) { + x = CG_PlayerNFFromPos( i, pageofs ); + if ( x == -1 ) { + break; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, cgs.clientinfo[x].name ); + } else { + str = va( "%c. %s", 'A' + i, cgs.clientinfo[x].name ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + + if ( *pageofs ) { + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( 8 + 1 ) % 10, "Previous" ); + } else { + str = va( "%c. %s", 'P', "Previous" ); + } + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + + y += button->rect.h; + } + + if ( CG_CountPlayersNF() > ( *pageofs + 1 ) * 8 ) { + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( 9 + 1 ) % 10, "Next" ); + } else { + str = va( "%c. %s", 'N', "Next" ); + } + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + + y += button->rect.h; + } +} + +void CG_Fireteams_MenuText_Draw( panel_button_t* button ) { + float y = button->rect.y; + int i; + + switch ( cgs.ftMenuMode ) { + case 0: + if ( cgs.ftMenuPos == -1 ) { + for ( i = 0; ftMenuRootStrings[i]; i++ ) { + const char* str; + + if ( i < 5 ) { + if ( !CG_FireteamHasClass( i, qtrue ) ) { + continue; + } + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, ftMenuRootStrings[i] ); + } else { + str = va( "%s. %s", ftMenuRootStringsAlphachars[i], ftMenuRootStrings[i] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + } else { + if ( cgs.ftMenuPos < 0 || cgs.ftMenuPos > 4 ) { + return; + } else { + const char** strings = ftMenuStrings[cgs.ftMenuPos]; + + for ( i = 0; strings[i]; i++ ) { + const char* str; + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, strings[i] ); + } else { + str = va( "%s. %s", ( ftMenuStringsAlphachars[cgs.ftMenuPos] )[i], strings[i] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + } + } + break; + case 1: + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + for ( i = 0; ftOffMenuList[i]; i++ ) { + const char* str; + + if ( i == 0 && !CG_CountFireteamsByTeam( cgs.clientinfo[cg.clientNum].team ) ) { + continue; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, ftOffMenuList[i] ); + } else { + str = va( "%s. %s", ftOffMenuListAlphachars[i], ftOffMenuList[i] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + } else { + if ( !CG_IsFireTeamLeader( cg.clientNum ) ) { + for ( i = 0; ftOnMenuList[i]; i++ ) { + const char* str; + + if ( i == 0 && !CG_CountPlayersNF() ) { + continue; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, ftOnMenuList[i] ); + } else { + str = va( "%s. %s", ftOnMenuListAlphachars[i], ftOnMenuList[i] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + } else { + for ( i = 0; ftLeaderMenuList[i]; i++ ) { + const char* str; + + if ( i == 2 && !CG_CountPlayersNF() ) { + continue; + } + + if ( ( i == 3 || i == 4 ) && !CG_CountPlayersSF() ) { + continue; + } + + if ( cg_quickMessageAlt.integer ) { + str = va( "%i. %s", ( i + 1 ) % 10, ftLeaderMenuList[i] ); + } else { + str = va( "%s. %s", ftLeaderMenuListAlphachars[i], ftLeaderMenuList[i] ); + } + + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, str, 0, 0, button->font->style, button->font->font ); + + y += button->rect.h; + } + } + } + break; + + case 2: + if ( !CG_CountFireteamsByTeam( cgs.clientinfo[cg.clientNum].team ) || CG_IsOnFireteam( cg.clientNum ) ) { + cgs.ftMenuMode = 1; + break; + } + + CG_DrawFireteamsByTeam( button, cgs.clientinfo[cg.clientNum].team ); + break; + + case 3: + if ( !CG_CountPlayersNF() ) { + cgs.ftMenuMode = 1; + break; + } + + CG_DrawPlayerNF( button, &cgs.ftMenuModeEx ); + break; + + case 4: + switch ( cgs.ftMenuPos ) { + case 2: + if ( !CG_CountPlayersNF() ) { + cgs.ftMenuMode = 1; + break; + } + + CG_DrawPlayerNF( button, &cgs.ftMenuModeEx ); + break; + case 3: + case 4: + if ( !CG_CountPlayersSF() ) { + cgs.ftMenuMode = 1; + break; + } + + CG_DrawPlayerSF( button, &cgs.ftMenuModeEx ); + break; + } + break; + } +} + +void CG_Fireteams_Setup( void ) { + BG_PanelButtonsSetup( fireteamButtons ); +} + +void CG_Fireteams_KeyHandling( int key, qboolean down ) { + if ( down ) { + CG_FireteamCheckExecKey( key, qtrue ); + } +} + +void CG_Fireteams_Draw( void ) { + BG_PanelButtonsRender( fireteamButtons ); +} + +void CG_QuickFireteamMessage_f( void ); + +qboolean CG_FireteamCheckExecKey( int key, qboolean doaction ) { + if ( key == K_ESCAPE ) { + return qtrue; + } + + if ( ( key & K_CHAR_FLAG ) ) { + return qfalse; + } + + key &= ~K_CHAR_FLAG; + + switch ( cgs.ftMenuMode ) { + case 0: + if ( cgs.ftMenuPos == -1 ) { + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + int i = ( ( key - '0' ) + 9 ) % 10; + + if ( i < 5 ) { + if ( !CG_FireteamHasClass( i, qtrue ) ) { + return qfalse; + } + } + + if ( i > 7 ) { + return qfalse; + } + + if ( doaction ) { + if ( i < 5 ) { + cgs.ftMenuPos = i; + } else if ( i == 5 ) { + CG_QuickFireteamMessage_f(); + } else { + trap_SendClientCommand( va( "vsay_buddy -1 %s %s", CG_BuildSelectedFirteamString(), ftMenuRootStringsMsg[i] ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + } + + return qtrue; + } + } else { + int i; + + if ( key >= 'a' || key <= 'z' ) { + for ( i = 0; ftMenuRootStrings[i]; i++ ) { + if ( key == tolower( *ftMenuRootStringsAlphachars[i] ) ) { + if ( i < 5 ) { + if ( !CG_FireteamHasClass( i, qtrue ) ) { + return qfalse; + } + } + + if ( doaction ) { + if ( i < 5 ) { + cgs.ftMenuPos = i; + } else if ( i == 5 ) { + CG_QuickFireteamMessage_f(); + } else { + trap_SendClientCommand( va( "vsay_buddy -1 %s %s", CG_BuildSelectedFirteamString(), ftMenuRootStringsMsg[i] ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + } + return qtrue; + } + } + } + } + } else { + if ( cgs.ftMenuPos < 0 || cgs.ftMenuPos > 4 ) { + return qfalse; + } + + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + int i = ( ( key - '0' ) + 9 ) % 10; + int x; + + const char** strings = ftMenuStrings[cgs.ftMenuPos]; + + for ( x = 0; strings[x]; x++ ) { + if ( x == i ) { + if ( doaction ) { + trap_SendClientCommand( va( "vsay_buddy %i %s %s", cgs.ftMenuPos, CG_BuildSelectedFirteamString(), ( ftMenuStringsMsg[cgs.ftMenuPos] )[i] ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + + return qtrue; + } + } + } + } else { + int i; + const char** strings = ftMenuStrings[cgs.ftMenuPos]; + + if ( key >= 'a' || key <= 'z' ) { + for ( i = 0; strings[i]; i++ ) { + if ( key == tolower( *ftMenuStringsAlphachars[cgs.ftMenuPos][i] ) ) { + + if ( doaction ) { + + trap_SendClientCommand( va( "vsay_buddy %i %s %s", cgs.ftMenuPos, CG_BuildSelectedFirteamString(), ( ftMenuStringsMsg[cgs.ftMenuPos] )[i] ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + return qtrue; + } + } + } + } + } + break; + case 1: + { + int i = -1, x; + + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + i = ( ( key - '0' ) + 9 ) % 10; + } + } else { + const char** strings; + + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + strings = ftOffMenuListAlphachars; + } else { + if ( !CG_IsFireTeamLeader( cg.clientNum ) ) { + strings = ftOnMenuListAlphachars; + } else { + strings = ftLeaderMenuListAlphachars; + } + } + + if ( key >= 'a' || key <= 'z' ) { + for ( x = 0; strings[x]; x++ ) { + if ( key == tolower( *strings[x] ) ) { + i = x; + break; + } + } + } + } + + if ( i == -1 ) { + break; + } + + if ( !CG_IsOnFireteam( cg.clientNum ) ) { + if ( i >= 2 ) { + break; + } + + if ( i == 0 && !CG_CountFireteamsByTeam( cgs.clientinfo[cg.clientNum].team ) ) { + return qfalse; + } + + if ( doaction ) { + if ( i == 1 ) { + trap_SendConsoleCommand( "fireteam create\n" ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + cgs.ftMenuMode = 2; + cgs.ftMenuModeEx = 0; + cgs.ftMenuPos = i; + } + } + + return qtrue; + } else { + if ( !CG_IsFireTeamLeader( cg.clientNum ) ) { + if ( i >= 2 ) { + break; + } + + if ( i == 0 && !CG_CountPlayersNF() ) { + break; + } + + if ( doaction ) { + if ( i == 1 ) { + trap_SendConsoleCommand( "fireteam leave\n" ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + cgs.ftMenuMode = 3; + cgs.ftMenuModeEx = 0; + cgs.ftMenuPos = i; + } + } + + return qtrue; + } else { + if ( i >= 5 ) { + break; + } + + if ( i == 2 && !CG_CountPlayersNF() ) { + break; + } + + if ( ( i == 3 || i == 4 ) && !CG_CountPlayersSF() ) { + break; + } + + if ( doaction ) { + if ( i == 0 ) { + trap_SendConsoleCommand( "fireteam disband\n" ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else if ( i == 1 ) { + trap_SendConsoleCommand( "fireteam leave\n" ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } else { + cgs.ftMenuMode = 4; + cgs.ftMenuModeEx = 0; + cgs.ftMenuPos = i; + } + } + + return qtrue; + } + } + } + break; + case 2: + { + int i; + + for ( i = 0; i < MAX_FIRETEAMS; i++ ) { + if ( !cg.fireTeams[i].inuse ) { + continue; + } + + if ( cgs.clientinfo[cg.fireTeams[i].leader].team != cgs.clientinfo[cg.clientNum].team ) { + continue; + } + + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + if ( ( ( key - '0' ) + 9 ) % 10 == cg.fireTeams[i].ident ) { + if ( doaction ) { + trap_SendConsoleCommand( va( "fireteam apply %i", i + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + return qtrue; + } + } + } else { + if ( key >= 'a' || key <= 'z' ) { + if ( key - 'a' == cg.fireTeams[i].ident ) { + if ( doaction ) { + trap_SendConsoleCommand( va( "fireteam apply %i", i + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + return qtrue; + } + } + } + } + } + break; + case 3: + { + int i = -1, x; + + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + i = ( ( key - '0' ) + 9 ) % 10; + } + } else { + if ( key >= 'a' || key <= 'g' ) { + i = key - 'a'; + } + + if ( key == 'n' ) { + i = 9; + } + + if ( key == 'p' ) { + i = 0; + } + } + + if ( i == -1 ) { + break; + } + + if ( CG_CountPlayersNF() > ( cgs.ftMenuModeEx + 1 ) * 8 ) { + if ( i == 0 ) { + cgs.ftMenuModeEx++; + } + } + if ( cgs.ftMenuModeEx ) { + if ( i == 9 ) { + cgs.ftMenuModeEx--; + } + } + x = CG_PlayerNFFromPos( i, &cgs.ftMenuModeEx ); + if ( x != -1 ) { + if ( doaction ) { + trap_SendConsoleCommand( va( "fireteam propose %i", x + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + + return qtrue; + } + break; + } + break; + case 4: + { + int i = -1, x; + + if ( cg_quickMessageAlt.integer ) { + if ( key >= '0' && key <= '9' ) { + i = ( ( key - '0' ) + 9 ) % 10; + } + } else { + if ( key >= 'a' || key <= 'g' ) { + i = key - 'a'; + } + + if ( key == 'n' ) { + i = 9; + } + + if ( key == 'p' ) { + i = 8; + } + } + + if ( i == -1 ) { + break; + } + + switch ( cgs.ftMenuPos ) { + case 2: + if ( CG_CountPlayersNF() > ( cgs.ftMenuModeEx + 1 ) * 8 ) { + if ( i == 9 ) { + if ( doaction ) { + cgs.ftMenuModeEx++; + } + + return qtrue; + } + } + if ( cgs.ftMenuModeEx ) { + if ( i == 8 ) { + if ( doaction ) { + cgs.ftMenuModeEx--; + } + + return qtrue; + } + } + x = CG_PlayerNFFromPos( i, &cgs.ftMenuModeEx ); + if ( x != -1 ) { + if ( doaction ) { + trap_SendConsoleCommand( va( "fireteam invite %i", x + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + + return qtrue; + } + break; + case 3: + case 4: + if ( CG_CountPlayersSF() > ( cgs.ftMenuModeEx + 1 ) * 8 ) { + if ( i == 0 ) { + cgs.ftMenuModeEx++; + } + } + if ( cgs.ftMenuModeEx ) { + if ( i == 9 ) { + cgs.ftMenuModeEx--; + } + } + x = CG_PlayerSFFromPos( i, &cgs.ftMenuModeEx ); + if ( x != -1 ) { + if ( doaction ) { + switch ( cgs.ftMenuPos ) { + case 4: + trap_SendConsoleCommand( va( "fireteam warn %i", x + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + break; + case 3: + trap_SendConsoleCommand( va( "fireteam kick %i", x + 1 ) ); + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + break; + } + } + + return qtrue; + } + break; + } + } + break; + } + + return qfalse; +} diff --git a/src/cgame/cg_flamethrower.c b/src/cgame/cg_flamethrower.c new file mode 100644 index 0000000..aec8892 --- /dev/null +++ b/src/cgame/cg_flamethrower.c @@ -0,0 +1,1294 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +// cg_flamethrower.c - special code for the flamethrower effects +// +// the flameChunks behave similarly to the trailJunc's, except they are rendered differently, and +// also interact with the environment +// +// NOTE: some AI's are treated different, mostly for aesthetical reasons. + +#include "cg_local.h" + +// a flameChunk is a ball or section of fuel which goes from fuel->blue ignition->flame ball +// optimization is necessary, since lots of these will be spawned, but as they grow, they can be +// merged so that less overdraw occurs +typedef struct flameChunk_s +{ + struct flameChunk_s *nextGlobal, *prevGlobal; // next junction in the global list it is in (free or used) + struct flameChunk_s *nextFlameChunk; // next junction in the trail + struct flameChunk_s *nextHead, *prevHead; // next head junc in the world + + qboolean inuse; + qboolean dead; // set when a chunk is effectively inactive, but waiting to be freed + int ownerCent; // cent that spawned us + + int timeStart, timeEnd; + float sizeMax; // start small, increase if we slow down + float sizeRand; + float sizeRate; // rate per ms, variable according to speed (larger if moving slower) + vec3_t baseOrg; + int baseOrgTime; + vec3_t velDir; + float velSpeed; // flame chunks should start with a fast velocity, then slow down if there is nothing behind them pushing them along + float rollAngle; + qboolean ignitionOnly; + int blueLife; + float gravity; + vec3_t startVelDir; + float speedScale; + + // current variables + vec3_t org; + float size; + float lifeFrac; // 0.0 (baby) -> 1.0 (aged) + + int lastFriction, lastFrictionTake; + vec3_t parentFwd; +} flameChunk_t; + +// DHM - Nerve :: lowered this from 2048. Still allows 6-9 people flaming. +#define MAX_FLAME_CHUNKS 1024 +static flameChunk_t flameChunks[MAX_FLAME_CHUNKS]; +static flameChunk_t *freeFlameChunks, *activeFlameChunks, *headFlameChunks; + +static qboolean initFlameChunks = qfalse; + +static int numFlameChunksInuse; + +// this structure stores information relevant to each cent in the game, this way we keep +// the flamethrower data seperate to the rest of the code, which helps if we decide against +// using this weapon in the game +typedef struct centFlameInfo_s +{ + int lastClientFrame; // client frame that we last fired the flamethrower + vec3_t lastAngles; // angles at last firing + vec3_t lastOrigin; // origin at last firing + flameChunk_t + *lastFlameChunk; // flame chunk we last spawned + int lastSoundUpdate; + + qboolean lastFiring; + + int lastDmgUpdate; // time we last told server about this ent's flame damage + int lastDmgCheck; // only check once per 100ms + int lastDmgEnemy; // entity that inflicted the damage +} centFlameInfo_t; + +static centFlameInfo_t centFlameInfo[MAX_GENTITIES]; + +typedef struct +{ +// float fireVolume; // not needed, since we add individual loop sources for the flame, so it gets spacialized + float blowVolume; + float streamVolume; +} flameSoundStatus_t; + +static flameSoundStatus_t centFlameStatus[MAX_GENTITIES]; + +// procedure defs +flameChunk_t *CG_SpawnFlameChunk( flameChunk_t *headFlameChunk ); +void CG_FlameCalcOrg( flameChunk_t *f, int time, vec3_t outOrg ); +void CG_FlameGetMuzzlePoint( vec3_t org, vec3_t fwd, vec3_t right, vec3_t up, vec3_t outPos ); + +// these must be globals, since they cannot expand or contract, since that might result in them getting +// stuck in geometry. therefore when a chunk hits a surface, we should deflect it away from the surface +// slightly, rather than running along it, so that as the chink grows, the sprites don't sink into the +// wall too much. +static vec3_t flameChunkMins = {0, 0, 0}; +static vec3_t flameChunkMaxs = {0, 0, 0}; + +// these define how the flame looks +#define FLAME_START_SIZE 1.0 +#define FLAME_START_MAX_SIZE 140.0 // when the flame is spawned, it should endevour to reach this size +#define FLAME_START_MAX_SIZE_RAND 60.0 +#define FLAME_MAX_SIZE 200.0 // flame sprites cannot be larger than this +#define FLAME_MIN_MAXSIZE 40.0 // don't ever let the sizeMax go less than this +#define FLAME_START_SPEED 1200.0 //1200.0 // speed of flame as it leaves the nozzle +#define FLAME_MIN_SPEED 60.0 //200.0 +#define FLAME_CHUNK_DIST 8.0 // space in between chunks when fired + +#define FLAME_BLUE_LENGTH 130.0 +#define FLAME_BLUE_MAX_ALPHA 1.0 + +#define FLAME_FUEL_LENGTH 48.0 +#define FLAME_FUEL_MAX_ALPHA 0.35 +#define FLAME_FUEL_MIN_WIDTH 1.0 + +// these are calculated (don't change) +#define FLAME_LENGTH ( FLAMETHROWER_RANGE + 50.0 ) // NOTE: only modify the range, since this should always reflect that range + +#define FLAME_LIFETIME (int)( ( FLAME_LENGTH / FLAME_START_SPEED ) * 1000 ) // life duration in milliseconds +#define FLAME_FRICTION_PER_SEC ( 2.0 * FLAME_START_SPEED ) +#define FLAME_BLUE_LIFE (int)( ( FLAME_BLUE_LENGTH / FLAME_START_SPEED ) * 1000 ) +#define FLAME_FUEL_LIFE (int)( ( FLAME_FUEL_LENGTH / FLAME_START_SPEED ) * 1000 ) +#define FLAME_FUEL_FADEIN_TIME ( 0.2 * FLAME_FUEL_LIFE ) + +#define FLAME_BLUE_FADEIN_TIME( x ) ( 0.2 * x ) +#define FLAME_BLUE_FADEOUT_TIME( x ) ( 0.05 * x ) +#define GET_FLAME_BLUE_SIZE_SPEED( x ) ( ( (float)x / FLAME_LIFETIME ) / 1.0 ) // x is the current sizeMax +#define GET_FLAME_SIZE_SPEED( x ) ( ( (float)x / FLAME_LIFETIME ) / 0.3 ) // x is the current sizeMax + +//#define FLAME_MIN_DRAWSIZE 20 + +// enable this for the fuel stream +//#define FLAME_ENABLE_FUEL_STREAM + +// enable this for dynamic lighting around flames +//#define FLAMETHROW_LIGHTS + +// disable this to stop rotating flames (this is variable so we can change it at run-time) +int rotatingFlames = qtrue; + +/* +=============== +CG_FlameLerpVec +=============== +*/ +void CG_FlameLerpVec( const vec3_t oldV, const vec3_t newV, float backLerp, vec3_t outV ) { + VectorScale( newV, ( 1.0 - backLerp ), outV ); + VectorMA( outV, backLerp, oldV, outV ); +} + +/* +=============== +CG_FlameAdjustSpeed +=============== +*/ +void CG_FlameAdjustSpeed( flameChunk_t *f, float change ) { + if ( !f->velSpeed && !change ) { + return; + } + + f->velSpeed += change; + if ( f->velSpeed < FLAME_MIN_SPEED ) { + f->velSpeed = FLAME_MIN_SPEED; + } +} + +/* +=============== +CG_FireFlameChunks + + The given entity is firing a flamethrower +=============== +*/ +void CG_FireFlameChunks( centity_t *cent, vec3_t origin, vec3_t angles, float speedScale, qboolean firing ) { + centFlameInfo_t *centInfo; + flameChunk_t *f, *of; + vec3_t lastFwd, thisFwd, fwd; + vec3_t lastUp, thisUp, up; + vec3_t lastRight, thisRight, right; + vec3_t thisOrg, lastOrg, org; + double timeInc, backLerp, fracInc; + int t, numFrameChunks; + double ft; + trace_t trace; + vec3_t parentFwd; + //float frametime, dot; + + centInfo = ¢FlameInfo[cent->currentState.number]; + + // for any other character or in 3rd person view, use entity angles for friction + if ( cent->currentState.number != cg.snap->ps.clientNum || cg_thirdPerson.integer ) { + AngleVectors( cent->currentState.angles, parentFwd, NULL, NULL ); + } else { + AngleVectors( angles, parentFwd, NULL, NULL ); + } + + AngleVectors( angles, thisFwd, thisRight, thisUp ); + VectorCopy( origin, thisOrg ); + + // if this entity was firing last frame, interpolate the angles as we spawn the chunks that + // fired over the last frame + if ( ( centInfo->lastClientFrame == cent->currentState.frame ) && + ( centInfo->lastFlameChunk && centInfo->lastFiring == firing ) ) { + AngleVectors( centInfo->lastAngles, lastFwd, lastRight, lastUp ); + VectorCopy( centInfo->lastOrigin, lastOrg ); + centInfo->lastFiring = firing; + + of = centInfo->lastFlameChunk; + timeInc = 1000.0 * ( firing ? 1.0 : 0.5 ) * ( FLAME_CHUNK_DIST / ( FLAME_START_SPEED * speedScale ) ); + ft = ( (double)of->timeStart + timeInc ); + t = (int)ft; + fracInc = timeInc / (double)( cg.time - of->timeStart ); + backLerp = 1.0 - fracInc; + + numFrameChunks = 0; // CHANGE: id + + while ( t <= cg.time ) { + // spawn a new chunk + CG_FlameLerpVec( lastOrg, thisOrg, backLerp, org ); + + CG_Trace( &trace, org, flameChunkMins, flameChunkMaxs, org, cent->currentState.number, MASK_SHOT | MASK_WATER ); // JPW NERVE water fixes + if ( trace.startsolid ) { + return; // don't spawn inside a wall + + } + f = CG_SpawnFlameChunk( of ); + + if ( !f ) { + //CG_Printf( "Out of flame chunks\n" ); + // CHANGE: id + // to make sure we do not keep trying to add more and more chunks + centInfo->lastFlameChunk->timeStart = cg.time; + // end CHANGE: id + return; + } + + CG_FlameLerpVec( lastFwd, thisFwd, backLerp, fwd ); + VectorNormalize( fwd ); + CG_FlameLerpVec( lastRight, thisRight, backLerp, right ); + VectorNormalize( right ); + CG_FlameLerpVec( lastUp, thisUp, backLerp, up ); + VectorNormalize( up ); + + f->timeStart = t; + f->timeEnd = t + FLAME_LIFETIME * ( 1.0 / ( 0.5 + 0.5 * speedScale ) ); + f->size = FLAME_START_SIZE * speedScale; + f->sizeMax = speedScale * ( FLAME_START_MAX_SIZE + f->sizeRand * ( firing ? 1.0 : 0.0 ) ); + f->sizeRand = 0; + + if ( f->sizeMax > FLAME_MAX_SIZE ) { + f->sizeMax = FLAME_MAX_SIZE; + } + + f->sizeRate = GET_FLAME_BLUE_SIZE_SPEED( f->sizeMax * speedScale * ( 1.0 + ( 0.5 * (float)!firing ) ) ); + VectorCopy( org, f->baseOrg ); + f->baseOrgTime = t; + VectorCopy( fwd, f->velDir ); + VectorCopy( fwd, f->startVelDir ); + f->speedScale = speedScale; + + VectorNormalize( f->velDir ); + f->velSpeed = FLAME_START_SPEED * ( 0.5 + 0.5 * speedScale ) * ( firing ? 1.0 : 4.5 ); + f->ownerCent = cent->currentState.number; + f->rollAngle = crandom() * 179; + f->ignitionOnly = !firing; + + if ( !firing ) { + f->gravity = -150; + f->blueLife = FLAME_BLUE_LIFE * 0.1; + } else { + f->gravity = 0; + f->blueLife = FLAME_BLUE_LIFE; + } + f->lastFriction = cg.time; + f->lastFrictionTake = cg.time; + VectorCopy( parentFwd, f->parentFwd ); + + ft += timeInc; + // always spawn a chunk right on the current time + if ( (int)ft > cg.time && t < cg.time ) { + ft = (double)cg.time; + backLerp = fracInc; // so it'll get set to zero a few lines down + } + t = (int)ft; + backLerp -= fracInc; + centInfo->lastFlameChunk = of = f; + // CHANGE: id + // don't spawn too many chunks each frame + if ( ++numFrameChunks > 50 ) { + // to make sure we do not keep trying to add more and more chunks + centInfo->lastFlameChunk->timeStart = cg.time; + break; + } + // end CHANGE: id + } + } else { + + centInfo->lastFiring = firing; + + // just fire a single chunk to get us started + f = CG_SpawnFlameChunk( NULL ); + + if ( !f ) { + //CG_Printf( "Out of flame chunks\n" ); + return; + } + + VectorCopy( thisOrg, org ); + VectorCopy( thisFwd, fwd ); + VectorCopy( thisUp, up ); + VectorCopy( thisRight, right ); + + f->timeStart = cg.time; + f->timeEnd = cg.time + FLAME_LIFETIME * ( 1.0 / ( 0.5 + 0.5 * speedScale ) ); + f->size = FLAME_START_SIZE * speedScale; + f->sizeMax = FLAME_START_MAX_SIZE * speedScale; + if ( f->sizeMax > FLAME_MAX_SIZE ) { + f->sizeMax = FLAME_MAX_SIZE; + } + + f->sizeRand = 0; + f->sizeRate = GET_FLAME_BLUE_SIZE_SPEED( f->sizeMax * speedScale ); + VectorCopy( org, f->baseOrg ); + f->baseOrgTime = cg.time; + VectorCopy( fwd, f->velDir ); + VectorCopy( fwd, f->startVelDir ); + f->velSpeed = FLAME_START_SPEED * ( 0.5 + 0.5 * speedScale ); + f->ownerCent = cent->currentState.number; + f->rollAngle = crandom() * 179; + f->ignitionOnly = !firing; + f->speedScale = speedScale; + if ( !firing ) { + f->gravity = -100; + f->blueLife = (int)( 0.3 * ( 1.0 / speedScale ) * (float)FLAME_BLUE_LIFE ); + } else { + f->gravity = 0; + f->blueLife = FLAME_BLUE_LIFE; + } + f->lastFriction = cg.time; + f->lastFrictionTake = cg.time; + VectorCopy( parentFwd, f->parentFwd ); + + centInfo->lastFlameChunk = f; + } + + // push them along + /* + f = centInfo->lastFlameChunk; + while (f) { + + if (f->lastFriction < cg.time - 50) { + frametime = (float)(cg.time - f->lastFriction) / 1000.0; + f->lastFriction = cg.time; + dot = DotProduct(parentFwd, f->parentFwd); + if (dot >= 0.99) { + dot -= 0.99; + dot *= (1.0/(1.0-0.99)); + CG_FlameAdjustSpeed( f, 0.5 * frametime * FLAME_FRICTION_PER_SEC * pow(dot,4) ); + } + } + + f = f->nextFlameChunk; + } + */ + + VectorCopy( angles, centInfo->lastAngles ); + VectorCopy( origin, centInfo->lastOrigin ); + centInfo->lastClientFrame = cent->currentState.frame; +} + +/* +=============== +CG_ClearFlameChunks +=============== +*/ +void CG_ClearFlameChunks( void ) { + int i; + + memset( flameChunks, 0, sizeof( flameChunks ) ); + memset( centFlameInfo, 0, sizeof( centFlameInfo ) ); + + freeFlameChunks = flameChunks; + activeFlameChunks = NULL; + headFlameChunks = NULL; + + for ( i = 0 ; i < MAX_FLAME_CHUNKS ; i++ ) + { + flameChunks[i].nextGlobal = &flameChunks[i + 1]; + + if ( i > 0 ) { + flameChunks[i].prevGlobal = &flameChunks[i - 1]; + } else { + flameChunks[i].prevGlobal = NULL; + } + + flameChunks[i].inuse = qfalse; + } + flameChunks[MAX_FLAME_CHUNKS - 1].nextGlobal = NULL; + + initFlameChunks = qtrue; + numFlameChunksInuse = 0; +} + +/* +=============== +CG_SpawnFlameChunk +=============== +*/ +flameChunk_t *CG_SpawnFlameChunk( flameChunk_t *headFlameChunk ) { + flameChunk_t *f; + + if ( !freeFlameChunks ) { + return NULL; + } + + if ( headFlameChunks && headFlameChunks->dead ) { + headFlameChunks = NULL; + } + + // select the first free trail, and remove it from the list + f = freeFlameChunks; + freeFlameChunks = f->nextGlobal; + if ( freeFlameChunks ) { + freeFlameChunks->prevGlobal = NULL; + } + + f->nextGlobal = activeFlameChunks; + if ( activeFlameChunks ) { + activeFlameChunks->prevGlobal = f; + } + activeFlameChunks = f; + f->prevGlobal = NULL; + f->inuse = qtrue; + f->dead = qfalse; + + // if this owner has a headJunc, add us to the start + if ( headFlameChunk ) { + // remove the headJunc from the list of heads + if ( headFlameChunk == headFlameChunks ) { + headFlameChunks = headFlameChunks->nextHead; + if ( headFlameChunks ) { + headFlameChunks->prevHead = NULL; + } + } else { + if ( headFlameChunk->nextHead ) { + headFlameChunk->nextHead->prevHead = headFlameChunk->prevHead; + } + if ( headFlameChunk->prevHead ) { + headFlameChunk->prevHead->nextHead = headFlameChunk->nextHead; + } + } + headFlameChunk->prevHead = NULL; + headFlameChunk->nextHead = NULL; + } + // make us the headTrail + if ( headFlameChunks ) { + headFlameChunks->prevHead = f; + } + f->nextHead = headFlameChunks; + f->prevHead = NULL; + headFlameChunks = f; + + f->nextFlameChunk = headFlameChunk; // if headJunc is NULL, then we'll just be the end of the list + + numFlameChunksInuse++; + + return f; +} + +/* +=========== +CG_FreeFlameChunk +=========== +*/ +void CG_FreeFlameChunk( flameChunk_t *f ) { + // kill any juncs after us, so they aren't left hanging + if ( f->nextFlameChunk ) { + CG_FreeFlameChunk( f->nextFlameChunk ); + f->nextFlameChunk = NULL; + } + + // make it non-active + f->inuse = qfalse; + f->dead = qfalse; + if ( f->nextGlobal ) { + f->nextGlobal->prevGlobal = f->prevGlobal; + } + if ( f->prevGlobal ) { + f->prevGlobal->nextGlobal = f->nextGlobal; + } + if ( f == activeFlameChunks ) { + activeFlameChunks = f->nextGlobal; + } + + // if it's a head, remove it + if ( f == headFlameChunks ) { + headFlameChunks = f->nextHead; + } + if ( f->nextHead ) { + f->nextHead->prevHead = f->prevHead; + } + if ( f->prevHead ) { + f->prevHead->nextHead = f->nextHead; + } + f->nextHead = NULL; + f->prevHead = NULL; + + // stick it in the free list + f->prevGlobal = NULL; + f->nextGlobal = freeFlameChunks; + if ( freeFlameChunks ) { + freeFlameChunks->prevGlobal = f; + } + freeFlameChunks = f; + + numFlameChunksInuse--; +} + +/* +=============== +CG_MergeFlameChunks + + Assumes f1 comes before f2 +=============== +*/ +void CG_MergeFlameChunks( flameChunk_t *f1, flameChunk_t *f2 ) { + if ( f1->nextFlameChunk != f2 ) { + CG_Error( "CG_MergeFlameChunks: f2 doesn't follow f1, cannot merge\n" ); + } + + f1->nextFlameChunk = f2->nextFlameChunk; + f2->nextFlameChunk = NULL; + + VectorCopy( f2->velDir, f1->velDir ); + + VectorCopy( f2->baseOrg, f1->baseOrg ); + f1->baseOrgTime = f2->baseOrgTime; + + f1->velSpeed = f2->velSpeed; + f1->sizeMax = f2->sizeMax; + f1->size = f2->size; + f1->timeStart = f2->timeStart; + f1->timeEnd = f2->timeEnd; + + CG_FreeFlameChunk( f2 ); +} + +/* +=============== +CG_FlameCalcOrg +=============== +*/ +void CG_FlameCalcOrg( flameChunk_t *f, int time, vec3_t outOrg ) { + VectorMA( f->baseOrg, f->velSpeed * ( (float)( time - f->baseOrgTime ) / 1000 ), f->velDir, outOrg ); + //outOrg[2] -= f->gravity * ((float)(time - f->timeStart)/1000.0) * ((float)(time - f->timeStart)/1000.0); +} + +/* +=============== +CG_MoveFlameChunk +=============== +*/ +void CG_MoveFlameChunk( flameChunk_t *f ) { + vec3_t newOrigin, sOrg; + trace_t trace; + int jiggleCount; + float dot; + // TTimo: unused + //static vec3_t umins = {-1,-1,-1}; + //static vec3_t umaxs = { 1, 1, 1}; + + // subtract friction from speed + if ( f->velSpeed > 1 && f->lastFrictionTake < cg.time - 50 ) { + CG_FlameAdjustSpeed( f, -( (float)( cg.time - f->lastFrictionTake ) / 1000.0 ) * FLAME_FRICTION_PER_SEC ); + f->lastFrictionTake = cg.time; + } + + // adjust size + if ( f->size < f->sizeMax ) { + if ( ( cg.time - f->timeStart ) < f->blueLife ) { + f->sizeRate = GET_FLAME_BLUE_SIZE_SPEED( FLAME_START_MAX_SIZE ); // use a constant so the blue flame doesn't distort + } else { + f->sizeRate = GET_FLAME_SIZE_SPEED( f->sizeMax ); + } + + f->size += f->sizeRate * (float)( cg.time - f->baseOrgTime ); + if ( f->size > f->sizeMax ) { + f->size = f->sizeMax; + } + } + + jiggleCount = 0; + VectorCopy( f->baseOrg, sOrg ); + while ( f->velSpeed > 1 && f->baseOrgTime != cg.time ) { + CG_FlameCalcOrg( f, cg.time, newOrigin ); + + // trace a line from previous position to new position + CG_Trace( &trace, sOrg, flameChunkMins, flameChunkMaxs, newOrigin, f->ownerCent, MASK_SHOT | MASK_WATER ); // JPW NERVE water fixes + + if ( trace.startsolid ) { + f->velSpeed = 0; + f->dead = 1; // JPW NERVE water fixes + break; + } + + if ( trace.surfaceFlags & SURF_NOIMPACT ) { + break; + } + + // moved some distance + VectorCopy( trace.endpos, f->baseOrg ); + f->baseOrgTime += (int)( (float)( cg.time - f->baseOrgTime ) * trace.fraction ); + + if ( trace.fraction == 1.0 ) { + // check for hitting client + if ( ( f->ownerCent != cg.snap->ps.clientNum ) && !( cg.snap->ps.eFlags & EF_DEAD ) && VectorDistance( newOrigin, cg.snap->ps.origin ) < 32 ) { + VectorNegate( f->velDir, trace.plane.normal ); + } else { + break; + } + } + + // reflect off surface + dot = DotProduct( f->velDir, trace.plane.normal ); + VectorMA( f->velDir, -2 * dot, trace.plane.normal, f->velDir ); + VectorNormalize( f->velDir ); + // subtract some speed + f->velSpeed *= 0.5 * ( 0.25 + 0.75 * ( ( dot + 1.0 ) * 0.5 ) ); + VectorCopy( f->velDir, f->parentFwd ); + + VectorCopy( f->baseOrg, sOrg ); + } + + CG_FlameCalcOrg( f, cg.time, f->org ); + f->baseOrgTime = cg.time; // incase we skipped the movement +} + +/* +=============== +CG_AddFlameSpriteToScene +=============== +*/ +static vec3_t vright, vup; +static vec3_t rright, rup; + +#ifdef _DEBUG // just in case we forget about it, but it should be disabled at all times (only enabled to generate updated shaders) +#ifdef ALLOW_GEN_SHADERS // secondary security measure + +//#define GEN_FLAME_SHADER + +#endif // ALLOW_GEN_SHADERS +#endif // _DEBUG + +#define FLAME_BLEND_SRC "GL_ONE" +#define FLAME_BLEND_DST "GL_ONE_MINUS_SRC_COLOR" + +#define NUM_FLAME_SPRITES 45 +#define FLAME_SPRITE_DIR "twiltb2" + +#define NUM_NOZZLE_SPRITES 8 + +static qhandle_t flameShaders[NUM_FLAME_SPRITES]; +static qhandle_t nozzleShaders[NUM_NOZZLE_SPRITES]; +static qboolean initFlameShaders = qtrue; + +#define MAX_CLIPPED_FLAMES 8 // dont draw more than this many per frame +static int numClippedFlames; + +void CG_FlameDamage( int owner, vec3_t org, float radius ) { + return; +} + +void CG_AddFlameSpriteToScene( flameChunk_t *f, float lifeFrac, float alpha ) { + vec3_t point, p2, sProj; + float radius, sdist; + int frameNum; + vec3_t vec, rotate_ang; + unsigned char alphaChar; + vec2_t rST; + static vec3_t lastPos; + polyBuffer_t* pPolyBuffer; + + if ( alpha < 0 ) { + return; // we dont want to see this + } + + radius = ( f->size / 2.0 ); + if ( radius < 6 ) { + radius = 6; + } + + if ( CG_CullPointAndRadius( f->org, radius ) ) { + return; + } + + rST[0] = radius * 1.0; + rST[1] = radius * 1.0 / 1.481; + alphaChar = ( unsigned char )( 255.0 * alpha ); + + frameNum = (int)floor( lifeFrac * NUM_FLAME_SPRITES ); + if ( frameNum < 0 ) { + frameNum = 0; + } else if ( frameNum > NUM_FLAME_SPRITES - 1 ) { + frameNum = NUM_FLAME_SPRITES - 1; + } + + pPolyBuffer = CG_PB_FindFreePolyBuffer( cg_fxflags & 1 ? getTestShader() : flameShaders[frameNum], 4, 6 ); + + pPolyBuffer->color[pPolyBuffer->numVerts + 0][0] = alphaChar; + pPolyBuffer->color[pPolyBuffer->numVerts + 0][1] = alphaChar; + pPolyBuffer->color[pPolyBuffer->numVerts + 0][2] = alphaChar; + pPolyBuffer->color[pPolyBuffer->numVerts + 0][3] = alphaChar; + + memcpy( pPolyBuffer->color[pPolyBuffer->numVerts + 1], pPolyBuffer->color[pPolyBuffer->numVerts + 0], sizeof( pPolyBuffer->color[0] ) ); + memcpy( pPolyBuffer->color[pPolyBuffer->numVerts + 2], pPolyBuffer->color[pPolyBuffer->numVerts + 0], sizeof( pPolyBuffer->color[0] ) ); + memcpy( pPolyBuffer->color[pPolyBuffer->numVerts + 3], pPolyBuffer->color[pPolyBuffer->numVerts + 0], sizeof( pPolyBuffer->color[0] ) ); + + // find the projected distance from the eye to the projection of the flame origin + // onto the view direction vector + VectorMA( cg.refdef_current->vieworg, 1024, cg.refdef_current->viewaxis[0], p2 ); + ProjectPointOntoVector( f->org, cg.refdef_current->vieworg, p2, sProj ); + + // make sure its infront of us + VectorSubtract( sProj, cg.refdef_current->vieworg, vec ); + sdist = VectorNormalize( vec ); + if ( !sdist || DotProduct( vec, cg.refdef_current->viewaxis[0] ) < 0 ) { + return; + } + + + if ( ( rotatingFlames ) && ( !( cg_fxflags & 1 ) ) ) { // JPW NERVE no rotate for alt flame shaders + vectoangles( cg.refdef_current->viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += f->rollAngle; + AngleVectors( rotate_ang, NULL, rright, rup ); + } else { + VectorCopy( vright, rright ); + VectorCopy( vup, rup ); + } + + VectorMA( f->org, -rST[1], rup, point ); + VectorMA( point, -rST[0], rright, point ); + VectorCopy( point, pPolyBuffer->xyz[pPolyBuffer->numVerts + 0] ); + pPolyBuffer->st[pPolyBuffer->numVerts + 0][0] = 0; + pPolyBuffer->st[pPolyBuffer->numVerts + 0][1] = 0; + + VectorMA( point, rST[1] * 2, rup, point ); + VectorCopy( point, pPolyBuffer->xyz[pPolyBuffer->numVerts + 1] ); + pPolyBuffer->st[pPolyBuffer->numVerts + 1][0] = 0; + pPolyBuffer->st[pPolyBuffer->numVerts + 1][1] = 1; + + VectorMA( point, rST[0] * 2, rright, point ); + VectorCopy( point, pPolyBuffer->xyz[pPolyBuffer->numVerts + 2] ); + pPolyBuffer->st[pPolyBuffer->numVerts + 2][0] = 1; + pPolyBuffer->st[pPolyBuffer->numVerts + 2][1] = 1; + + VectorMA( point, -rST[1] * 2, rup, point ); + VectorCopy( point, pPolyBuffer->xyz[pPolyBuffer->numVerts + 3] ); + pPolyBuffer->st[pPolyBuffer->numVerts + 3][0] = 1; + pPolyBuffer->st[pPolyBuffer->numVerts + 3][1] = 0; + + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 0] = pPolyBuffer->numVerts + 0; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 1] = pPolyBuffer->numVerts + 1; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 2] = pPolyBuffer->numVerts + 2; + + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 3] = pPolyBuffer->numVerts + 2; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 4] = pPolyBuffer->numVerts + 3; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 5] = pPolyBuffer->numVerts + 0; + + pPolyBuffer->numIndicies += 6; + pPolyBuffer->numVerts += 4; + + VectorCopy( f->org, lastPos ); +} + +static int nextFlameLight = 0; +static int lastFlameOwner = -1; + + +#define FLAME_SOUND_RANGE 1024.0 + +/* +=============== +CG_AddFlameToScene +=============== +*/ +void CG_AddFlameToScene( flameChunk_t *fHead ) { + flameChunk_t *f, *fNext; + int blueTrailHead = 0, fuelTrailHead = 0; + static vec3_t whiteColor = {1,1,1}; + vec3_t c; + float alpha; + float lived; + int headTimeStart; + float vdist, bdot; + flameChunk_t *lastBlowChunk = NULL; + qboolean isClientFlame, firing; + int shader; + flameChunk_t *lastBlueChunk = NULL; + qboolean skip = qfalse, droppedTrail; + vec3_t v; + vec3_t lightOrg; // origin to place light at + float lightSize; + float lightFlameCount; + float lastFuelAlpha; + + isClientFlame = ( fHead == centFlameInfo[fHead->ownerCent].lastFlameChunk ); + + if ( ( cg_entities[fHead->ownerCent].currentState.eFlags & EF_FIRING ) && ( centFlameInfo[fHead->ownerCent].lastFlameChunk == fHead ) ) { + headTimeStart = fHead->timeStart; + firing = qtrue; + } else { + headTimeStart = cg.time; + firing = qfalse; + } + + VectorClear( lightOrg ); + lightSize = 0; + lightFlameCount = 0; + + lastFuelAlpha = 1.0; + + f = fHead; + while ( f ) { + + if ( f->nextFlameChunk && f->nextFlameChunk->dead ) { + // kill it + CG_FreeFlameChunk( f->nextFlameChunk ); + f->nextFlameChunk = NULL; + } + + // draw this chunk + + fNext = f->nextFlameChunk; + lived = (float)( headTimeStart - f->timeStart ); + + // update the "blow" sound volume (louder as we sway it) + vdist = Distance( cg.refdef_current->vieworg, f->org ); // NOTE: this needs to be here or the flameSound code further below won't work + if ( lastBlowChunk && ( centFlameStatus[f->ownerCent].blowVolume < 1.0 ) && + ( ( bdot = DotProduct( lastBlowChunk->startVelDir, f->startVelDir ) ) < 1.0 ) ) { + if ( vdist < FLAME_SOUND_RANGE ) { + centFlameStatus[f->ownerCent].blowVolume += 500.0 * ( 1.0 - bdot ) * ( 1.0 - ( vdist / FLAME_SOUND_RANGE ) ); + if ( centFlameStatus[f->ownerCent].blowVolume > 1.0 ) { + centFlameStatus[f->ownerCent].blowVolume = 1.0; + } + } + } + lastBlowChunk = f; + + VectorMA( lightOrg, f->size / 20.0, f->org, lightOrg ); + lightSize += f->size; + lightFlameCount += f->size / 20.0; + + droppedTrail = qfalse; + + // is it a stream chunk? (no special handling) + if ( !f->ignitionOnly && f->velSpeed < 1 ) { + CG_AddFlameSpriteToScene( f, f->lifeFrac, 1.0 ); + + // is it in the blue ignition section of the flame? + } else if ( isClientFlame && f->blueLife > ( lived / 2.0 ) ) { + + skip = qfalse; + + // if this is backwards from the last chunk, then skip it + if ( fNext && f != fHead && lastBlueChunk ) { + VectorSubtract( f->org, lastBlueChunk->org, v ); + if ( VectorNormalize( v ) < f->size / 2 ) { + skip = qtrue; + } else if ( DotProduct( v, f->velDir ) < 0 ) { + skip = qtrue; + } + } + + // stream sound + if ( !f->ignitionOnly ) { + centFlameStatus[f->ownerCent].streamVolume += 0.05; + if ( centFlameStatus[f->ownerCent].streamVolume > 1.0 ) { + centFlameStatus[f->ownerCent].streamVolume = 1.0; + } + } + + + if ( !skip ) { + + // just call this for damage checking + //if (!f->ignitionOnly) + //CG_AddFlameSpriteToScene( f, f->lifeFrac, -1 ); + + lastBlueChunk = f; + + alpha = 1.0; // new nozzle sprite + VectorScale( whiteColor, alpha, c ); + + if ( f->blueLife > lived * ( f->ignitionOnly ? 3.0 : 3.0 ) ) { + + shader = nozzleShaders[( cg.time / 50 + ( cg.time / 50 >> 1 ) ) % NUM_NOZZLE_SPRITES]; + + blueTrailHead = CG_AddTrailJunc( blueTrailHead, + NULL, // rain - zinx's trail fix + shader, + cg.time, + STYPE_STRETCH, + f->org, + 1, + alpha, alpha, + f->size * ( f->ignitionOnly /*&& (cg.snap->ps.clientNum != f->ownerCent || cg_thirdPerson.integer)*/ ? 2.0 : 1.0 ), + FLAME_MAX_SIZE, + TJFL_NOCULL | TJFL_FIXDISTORT, + c, c, 1.0, 5.0 ); + } + + // fire stream + if ( !f->ignitionOnly ) { + float bscale; + qboolean fskip = qfalse; + + bscale = 1.0; + + if ( !f->nextFlameChunk ) { + alpha = 0; + } else if ( lived / 1.3 < bscale * FLAME_BLUE_FADEIN_TIME( f->blueLife ) ) { + alpha = FLAME_BLUE_MAX_ALPHA * ( ( lived / 1.3 ) / ( bscale * FLAME_BLUE_FADEIN_TIME( f->blueLife ) ) ); + } else if ( lived / 1.3 < ( f->blueLife - FLAME_BLUE_FADEOUT_TIME( f->blueLife ) ) ) { + alpha = FLAME_BLUE_MAX_ALPHA; + } else { + alpha = FLAME_BLUE_MAX_ALPHA * ( 1.0 - ( ( lived / 1.3 - ( f->blueLife - FLAME_BLUE_FADEOUT_TIME( f->blueLife ) ) ) / ( FLAME_BLUE_FADEOUT_TIME( f->blueLife ) ) ) ); + } + if ( alpha <= 0.0 ) { + alpha = 0.0; + if ( lastFuelAlpha <= 0.0 ) { + fskip = qtrue; + } + } + + if ( !fskip ) { + lastFuelAlpha = alpha; + + VectorScale( whiteColor, alpha, c ); + + droppedTrail = qtrue; + + fuelTrailHead = CG_AddTrailJunc( fuelTrailHead, + NULL, // rain - zinx's trail fix + cgs.media.flamethrowerFireStream, + cg.time, + ( f->ignitionOnly ? STYPE_STRETCH : STYPE_REPEAT ), + f->org, + 1, + alpha, alpha, + ( f->size / 2 < f->sizeMax / 4 ? f->size / 2 : f->sizeMax / 4 ), + FLAME_MAX_SIZE, + TJFL_NOCULL | TJFL_FIXDISTORT | TJFL_CROSSOVER, + c, c, 0.5, 1.5 ); + } + } + } + } + +#define FLAME_SPRITE_START_BLUE_SCALE 0.2 + + if ( !f->ignitionOnly && + ( (float)( FLAME_SPRITE_START_BLUE_SCALE * f->blueLife ) < (float)lived ) ) { + + float alpha, lifeFrac; + qboolean skip = qfalse; + + // should we merge it with the next sprite? + while ( fNext && !droppedTrail ) { + if ( ( Distance( f->org, fNext->org ) < ( ( 0.1 + 0.9 * f->lifeFrac ) * f->size * 0.35 ) ) + && ( fabs( f->size - fNext->size ) < ( 40.0 ) ) + && ( fabs( f->timeStart - fNext->timeStart ) < 100 ) + && ( DotProduct( f->velDir, fNext->velDir ) > 0.99 ) + ) { + if ( !droppedTrail ) { + CG_MergeFlameChunks( f, fNext ); + fNext = f->nextFlameChunk; // it may have changed + } else { + skip = qtrue; + break; + } + } else { + break; + } + } + + lifeFrac = ( lived - FLAME_SPRITE_START_BLUE_SCALE * f->blueLife ) / ( FLAME_LIFETIME - FLAME_SPRITE_START_BLUE_SCALE * f->blueLife ); + + alpha = ( 1.0 - lifeFrac ) * 1.4; + if ( alpha > 1.0 ) { + alpha = 1.0; + } + + if ( !skip ) { + // draw the sprite + CG_AddFlameSpriteToScene( f, lifeFrac, alpha ); + } + // update the sizeRate + f->sizeRate = GET_FLAME_SIZE_SPEED( f->sizeMax ); + } + + f = fNext; + } + + if ( lastFlameOwner == fHead->ownerCent && nextFlameLight == cg.clientFrame ) { + return; + } + + if ( !fHead->ignitionOnly ) { + nextFlameLight = cg.clientFrame; + lastFlameOwner = fHead->ownerCent; + } + + if ( lightSize < 80 ) { + lightSize = 80; + } + + if ( lightSize > 500 ) { + lightSize = 500; + } + lightSize *= 1.0 + 0.2 * ( sin( 1.0 * cg.time / 50.0 ) * cos( 1.0 * cg.time / 43.0 ) ); + // set the alpha + //% alpha = lightSize / 500.0; + alpha = lightSize * 0.005; // ydnar + if ( alpha > 2.0 ) { + alpha = 2.0; + } + VectorScale( lightOrg, 1.0 / lightFlameCount, lightOrg ); + // if it's only a nozzle, make it blue + if ( fHead->ignitionOnly ) { + if ( lightSize > 80 ) { + lightSize = 80; + } + //% trap_R_AddLightToScene( lightOrg, 90 + lightSize, 0, 0, alpha, 0 + isClientFlame * (fHead->ownerCent == cg.snap->ps.clientNum) ); + trap_R_AddLightToScene( lightOrg, 80, alpha, 0.2, 0.21, 0.5, 0, 0 ); + } else if ( isClientFlame || ( fHead->ownerCent == cg.snap->ps.clientNum ) ) { + //% trap_R_AddLightToScene( lightOrg, 90 + lightSize, 1.000000*alpha, 0.603922*alpha, 0.207843*alpha, 0 ); + trap_R_AddLightToScene( lightOrg, 320, alpha, 1.000000, 0.603922, 0.207843, 0, 0 ); + } +} + +/* +============= +CG_GenerateShaders + + A util to create a bunch of shaders in a unique shader file, which represent an animation +============= +*/ +void CG_GenerateShaders( char *filename, char *shaderName, char *dir, int numFrames, char *srcBlend, char *dstBlend, char *extras, qboolean compressedVersionAvailable, qboolean nomipmap ) { + fileHandle_t f; + int b, c, d, lastNumber; + char str[512]; + int i; + + trap_FS_FOpenFile( filename, &f, FS_WRITE ); + for ( i = 0; i < numFrames; i++ ) { + lastNumber = i; + b = lastNumber / 100; + lastNumber -= b * 100; + c = lastNumber / 10; + lastNumber -= c * 10; + d = lastNumber; + + if ( compressedVersionAvailable ) { + Com_sprintf( str, sizeof( str ), "%s%i\n{\n\tnofog%s\n\tallowCompress\n\tcull none\n\t{\n\t\tmapcomp sprites/%s_lg/spr%i%i%i.tga\n\t\tmapnocomp sprites/%s/spr%i%i%i.tga\n\t\tblendFunc %s %s\n%s\t}\n}\n", shaderName, i + 1, nomipmap ? "\n\tnomipmaps" : "", dir, b, c, d, dir, b, c, d, srcBlend, dstBlend, extras ); + } else { + Com_sprintf( str, sizeof( str ), "%s%i\n{\n\tnofog%s\n\tallowCompress\n\tcull none\n\t{\n\t\tmap sprites/%s/spr%i%i%i.tga\n\t\tblendFunc %s %s\n%s\t}\n}\n", shaderName, i + 1, nomipmap ? "\n\tnomipmap" : "", dir, b, c, d, srcBlend, dstBlend, extras ); + } + trap_FS_Write( str, strlen( str ), f ); + } + trap_FS_FCloseFile( f ); +} + +/* +=============== +CG_InitFlameChunks +=============== +*/ +void CG_InitFlameChunks( void ) { + int i; + char filename[MAX_QPATH]; + + CG_ClearFlameChunks(); + +#ifdef GEN_FLAME_SHADER + CG_GenerateShaders( "scripts/flamethrower.shader", + "flamethrowerFire", + FLAME_SPRITE_DIR, + NUM_FLAME_SPRITES, + FLAME_BLEND_SRC, + FLAME_BLEND_DST, + "", + qtrue, qtrue ); + + CG_GenerateShaders( "scripts/blacksmokeanim.shader", + "blacksmokeanim", + "explode1", + 23, + "GL_ZERO", + "GL_ONE_MINUS_SRC_ALPHA", + "\t\talphaGen const 0.2\n", + qfalse, qfalse ); + + CG_GenerateShaders( "scripts/viewflames.shader", + "viewFlashFire", + "clnfire", + 16, + "GL_ONE", + "GL_ONE", + "\t\talphaGen vertex\n\t\trgbGen vertex\n", + qtrue, qtrue ); + + CG_GenerateShaders( "scripts/twiltb.shader", + "twiltb", + "twiltb", + 42, + "GL_SRC_ALPHA", + "GL_ONE_MINUS_SRC_COLOR", + "", + qtrue, qfalse ); + + CG_GenerateShaders( "scripts/twiltb2.shader", + "twiltb2", + "twiltb2", + 45, + "GL_ONE", + "GL_ONE_MINUS_SRC_COLOR", + "", + qtrue, qfalse ); +/* + CG_GenerateShaders( "scripts/expblue.shader", + "expblue", + "expblue", + 25, + "GL_ONE", + "GL_ONE_MINUS_SRC_COLOR", + "", + qfalse, qfalse ); +*/ + CG_GenerateShaders( "scripts/firest.shader", + "firest", + "firest", + 36, + "GL_ONE", + "GL_ONE_MINUS_SRC_COLOR", + "", + qtrue, qfalse ); + + CG_GenerateShaders( "scripts/explode1.shader", + "explode1", + "explode1", + 23, + "GL_ONE", + "GL_ONE_MINUS_SRC_COLOR", + "", + qtrue, qfalse ); + + CG_GenerateShaders( "scripts/funnel.shader", + "funnel", + "funnel", + 21, + "GL_ONE", + "GL_ONE_MINUS_SRC_COLOR", + "", + qfalse, qfalse ); +#endif + + for ( i = 0; i < NUM_FLAME_SPRITES; i++ ) { + Com_sprintf( filename, MAX_QPATH, "flamethrowerFire%i", i + 1 ); + flameShaders[i] = trap_R_RegisterShader( filename ); + } + for ( i = 0; i < NUM_NOZZLE_SPRITES; i++ ) { + Com_sprintf( filename, MAX_QPATH, "nozzleFlame%i", i + 1 ); + nozzleShaders[i] = trap_R_RegisterShader( filename ); + } + initFlameShaders = qfalse; +} + +/* +=============== +CG_AddFlameChunks +=============== +*/ +void CG_AddFlameChunks( void ) { + flameChunk_t *f, *fNext; + + //AngleVectors( cg.refdef.viewangles, NULL, vright, vup ); + VectorCopy( cg.refdef_current->viewaxis[1], vright ); + VectorCopy( cg.refdef_current->viewaxis[2], vup ); + + // clear out the volumes so we can rebuild them + memset( centFlameStatus, 0, sizeof( centFlameStatus ) ); + + numClippedFlames = 0; + + // age them + f = activeFlameChunks; + while ( f ) { + if ( !f->dead ) { + if ( cg.time > f->timeEnd ) { + f->dead = qtrue; + } else if ( f->ignitionOnly && ( f->blueLife < ( cg.time - f->timeStart ) ) ) { + f->dead = qtrue; + } else { + CG_MoveFlameChunk( f ); + f->lifeFrac = (float)( cg.time - f->timeStart ) / (float)( f->timeEnd - f->timeStart ); + } + } + f = f->nextGlobal; + } + + // draw each of the headFlameChunk's + f = headFlameChunks; + while ( f ) { + fNext = f->nextHead; // in case it gets removed + if ( f->dead ) { + if ( centFlameInfo[f->ownerCent].lastFlameChunk == f ) { + centFlameInfo[f->ownerCent].lastFlameChunk = NULL; + centFlameInfo[f->ownerCent].lastClientFrame = 0; + } + CG_FreeFlameChunk( f ); + } else if ( !f->ignitionOnly || ( centFlameInfo[f->ownerCent].lastFlameChunk == f ) ) { // don't draw the ignition flame after we start firing + CG_AddFlameToScene( f ); + } + f = fNext; + } +} + +/* +=============== +CG_UpdateFlamethrowerSounds +=============== +*/ +void CG_UpdateFlamethrowerSounds( void ) { + flameChunk_t *f, *trav; // , *lastSoundFlameChunk=NULL; // TTimo: unused + #define MIN_BLOW_VOLUME 30 + + // draw each of the headFlameChunk's + f = headFlameChunks; + while ( f ) { + // update this entity? + if ( centFlameInfo[f->ownerCent].lastSoundUpdate != cg.time ) { + // blow/ignition sound + if ( centFlameStatus[f->ownerCent].blowVolume * 255.0 > MIN_BLOW_VOLUME ) { + trap_S_AddLoopingSound( f->org, vec3_origin, cgs.media.flameBlowSound, (int)( 255.0 * centFlameStatus[f->ownerCent].blowVolume ), 0 ); // JPW NERVE + } else { + trap_S_AddLoopingSound( f->org, vec3_origin, cgs.media.flameBlowSound, MIN_BLOW_VOLUME, 0 ); // JPW NERVE + } + + if ( centFlameStatus[f->ownerCent].streamVolume ) { + trap_S_AddLoopingSound( f->org, vec3_origin, cgs.media.flameStreamSound, (int)( 255.0 * centFlameStatus[f->ownerCent].streamVolume ), 0 ); // JPW NERVE + } + + centFlameInfo[f->ownerCent].lastSoundUpdate = cg.time; + } + + // traverse the chunks, spawning flame sound sources as we go + for ( trav = f; trav; trav = trav->nextFlameChunk ) { + // update the sound volume + if ( trav->blueLife + 100 < ( cg.time - trav->timeStart ) ) { + trap_S_AddLoopingSound( trav->org, vec3_origin, cgs.media.flameSound, (int)( 255.0 * ( 0.2 * ( trav->size / FLAME_MAX_SIZE ) ) ), 0 ); + } + } + + f = f->nextHead; + } +} diff --git a/src/cgame/cg_info.c b/src/cgame/cg_info.c new file mode 100644 index 0000000..ca79abb --- /dev/null +++ b/src/cgame/cg_info.c @@ -0,0 +1,1044 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_info.c -- display information while data is being loading + +#include "cg_local.h" + +/* +=================== +CG_DrawLoadingIcons +=================== +*/ +/*static void CG_DrawLoadingIcons( void ) { + int n; + int x, y; + + // JOSEPH 5-2-00 Per MAXX + return; + + for( n = 0; n < loadingPlayerIconCount; n++ ) { + x = 16 + n * 78; + y = 324; + CG_DrawPic( x, y, 64, 64, loadingPlayerIcons[n] ); + } + + for( n = 0; n < loadingItemIconCount; n++ ) { + y = 400; + if( n >= 13 ) { + y += 40; + } + x = 16 + n % 13 * 48; + CG_DrawPic( x, y, 32, 32, loadingItemIcons[n] ); + } +}*/ + + +/* +====================== +CG_LoadingString + +====================== +*/ +void CG_LoadingString( const char *s ) { + Q_strncpyz( cg.infoScreenText, s, sizeof( cg.infoScreenText ) ); + + if ( s && *s ) { + CG_Printf( va( "LOADING... %s\n",s ) ); //----(SA) added so you can see from the console what's going on + + } + // Arnout: no need for this + //trap_UpdateScreen(); +} + +/* +==================== +CG_DrawInformation + +Draw all the status / pacifier stuff during level loading +==================== +*/ +void CG_DrawInformation( qboolean forcerefresh ) { + static int lastcalled = 0; + + if ( lastcalled && ( trap_Milliseconds() - lastcalled < 500 ) ) { + return; + } + lastcalled = trap_Milliseconds(); + + if ( cg.snap ) { + return; // we are in the world, no need to draw information + } + + CG_DrawConnectScreen( qfalse, forcerefresh ); + + // OSP - Server MOTD window +/* if(cg.motdWindow == NULL) { + CG_createMOTDWindow(); + } + if(cg.motdWindow != NULL) { + CG_windowDraw(); + }*/ + // OSP*/ +} + + +void CG_ShowHelp_On( int *status ) { + int milli = trap_Milliseconds(); + + if ( *status == SHOW_SHUTDOWN && milli < cg.fadeTime ) { + cg.fadeTime = 2 * milli + STATS_FADE_TIME - cg.fadeTime; + } else if ( *status != SHOW_ON ) { + cg.fadeTime = milli + STATS_FADE_TIME; + } + + *status = SHOW_ON; +} + +void CG_ShowHelp_Off( int *status ) { + if ( *status != SHOW_OFF ) { + int milli = trap_Milliseconds(); + + if ( milli < cg.fadeTime ) { + cg.fadeTime = 2 * milli + STATS_FADE_TIME - cg.fadeTime; + } else { + cg.fadeTime = milli + STATS_FADE_TIME; + } + + *status = SHOW_SHUTDOWN; + } +} + + +// Demo playback key catcher support +void CG_DemoClick( int key, qboolean down ) { + int milli = trap_Milliseconds(); + + // Avoid active console keypress issues + if ( !down && !cgs.fKeyPressed[key] ) { + return; + } + + cgs.fKeyPressed[key] = down; + + switch ( key ) + { + case K_ESCAPE: + CG_ShowHelp_Off( &cg.demohelpWindow ); + CG_keyOff_f(); + return; + + case K_TAB: + if ( down ) { + CG_ScoresDown_f(); + } else { CG_ScoresUp_f();} + return; + + // Help info + case K_BACKSPACE: + if ( !down ) { + if ( cg.demohelpWindow != SHOW_ON ) { + CG_ShowHelp_On( &cg.demohelpWindow ); + } else { + CG_ShowHelp_Off( &cg.demohelpWindow ); + } + } + return; + + // Screenshot keys + case K_F11: + if ( !down ) { + trap_SendConsoleCommand( va( "screenshot%s\n", ( ( cg_useScreenshotJPEG.integer ) ? "JPEG" : "" ) ) ); + } + return; + case K_F12: + if ( !down ) { + CG_autoScreenShot_f(); + } + return; + + // Window controls + case K_SHIFT: + case K_CTRL: + case K_MOUSE4: + cgs.fResize = down; + return; + case K_MOUSE1: + cgs.fSelect = down; + return; + case K_MOUSE2: + if ( !down ) { + CG_mvSwapViews_f(); // Swap the window with the main view + } + return; + case K_INS: + case K_KP_PGUP: + if ( !down ) { + CG_mvShowView_f(); // Make a window for the client + } + return; + case K_DEL: + case K_KP_PGDN: + if ( !down ) { + CG_mvHideView_f(); // Delete the window for the client + } + return; + case K_MOUSE3: + if ( !down ) { + CG_mvToggleView_f(); // Toggle a window for the client + } + return; + + // Third-person controls + case K_ENTER: + if ( !down ) { + trap_Cvar_Set( "cg_thirdperson", ( ( cg_thirdPerson.integer == 0 ) ? "1" : "0" ) ); + } + return; + case K_UPARROW: + if ( milli > cgs.thirdpersonUpdate ) { + float range = cg_thirdPersonRange.value; + + cgs.thirdpersonUpdate = milli + DEMO_THIRDPERSONUPDATE; + range -= ( ( range >= 4 * DEMO_RANGEDELTA ) ? DEMO_RANGEDELTA : ( range - DEMO_RANGEDELTA ) ); + trap_Cvar_Set( "cg_thirdPersonRange", va( "%f", range ) ); + } + return; + case K_DOWNARROW: + if ( milli > cgs.thirdpersonUpdate ) { + float range = cg_thirdPersonRange.value; + + cgs.thirdpersonUpdate = milli + DEMO_THIRDPERSONUPDATE; + range += ( ( range >= 120 * DEMO_RANGEDELTA ) ? 0 : DEMO_RANGEDELTA ); + trap_Cvar_Set( "cg_thirdPersonRange", va( "%f", range ) ); + } + return; + case K_RIGHTARROW: + if ( milli > cgs.thirdpersonUpdate ) { + float angle = cg_thirdPersonAngle.value - DEMO_ANGLEDELTA; + + cgs.thirdpersonUpdate = milli + DEMO_THIRDPERSONUPDATE; + if ( angle < 0 ) { + angle += 360.0f; + } + trap_Cvar_Set( "cg_thirdPersonAngle", va( "%f", angle ) ); + } + return; + case K_LEFTARROW: + if ( milli > cgs.thirdpersonUpdate ) { + float angle = cg_thirdPersonAngle.value + DEMO_ANGLEDELTA; + + cgs.thirdpersonUpdate = milli + DEMO_THIRDPERSONUPDATE; + if ( angle >= 360.0f ) { + angle -= 360.0f; + } + trap_Cvar_Set( "cg_thirdPersonAngle", va( "%f", angle ) ); + } + return; + + // Timescale controls + case K_KP_5: + case K_KP_INS: + case K_SPACE: + if ( !down ) { + trap_Cvar_Set( "timescale", "1" ); + cgs.timescaleUpdate = cg.time + 1000; + } + return; + case K_KP_DOWNARROW: + if ( !down ) { + float tscale = cg_timescale.value; + + if ( tscale <= 1.1f ) { + if ( tscale > 0.1f ) { + tscale -= 0.1f; + } + } else { tscale -= 1.0;} + trap_Cvar_Set( "timescale", va( "%f", tscale ) ); + cgs.timescaleUpdate = cg.time + (int)( 1000.0f * tscale ); + } + return; + case K_MWHEELDOWN: + if ( !cgs.fKeyPressed[K_SHIFT] ) { + if ( !down ) { + CG_ZoomOut_f(); + } + return; + } // Roll over into timescale changes + case K_KP_LEFTARROW: + if ( !down && cg_timescale.value > 0.1f ) { + trap_Cvar_Set( "timescale", va( "%f", cg_timescale.value - 0.1f ) ); + cgs.timescaleUpdate = cg.time + (int)( 1000.0f * cg_timescale.value - 0.1f ); + } + return; + case K_KP_UPARROW: + if ( !down ) { + trap_Cvar_Set( "timescale", va( "%f", cg_timescale.value + 1.0f ) ); + cgs.timescaleUpdate = cg.time + (int)( 1000.0f * cg_timescale.value + 1.0f ); + } + return; + case K_MWHEELUP: + if ( !cgs.fKeyPressed[K_SHIFT] ) { + if ( !down ) { + CG_ZoomIn_f(); + } + return; + } // Roll over into timescale changes + case K_KP_RIGHTARROW: + if ( !down ) { + trap_Cvar_Set( "timescale", va( "%f", cg_timescale.value + 0.1f ) ); + cgs.timescaleUpdate = cg.time + (int)( 1000.0f * cg_timescale.value + 0.1f ); + } + return; + + // AVI recording controls + case K_F1: + if ( down ) { + cgs.aviDemoRate = demo_avifpsF1.integer; + } else { trap_Cvar_Set( "cl_avidemo", demo_avifpsF1.string );} + return; + case K_F2: + if ( down ) { + cgs.aviDemoRate = demo_avifpsF2.integer; + } else { trap_Cvar_Set( "cl_avidemo", demo_avifpsF2.string );} + return; + case K_F3: + if ( down ) { + cgs.aviDemoRate = demo_avifpsF3.integer; + } else { trap_Cvar_Set( "cl_avidemo", demo_avifpsF3.string );} + return; + case K_F4: + if ( down ) { + cgs.aviDemoRate = demo_avifpsF4.integer; + } else { trap_Cvar_Set( "cl_avidemo", demo_avifpsF4.string );} + return; + case K_F5: + if ( down ) { + cgs.aviDemoRate = demo_avifpsF5.integer; + } else { trap_Cvar_Set( "cl_avidemo", demo_avifpsF5.string );} + return; + } +} + + + +// +// Color/font info used for all overlays (below) +// +#define COLOR_BG { 0.0f, 0.0f, 0.0f, 0.6f } +#define COLOR_BORDER { 0.5f, 0.5f, 0.5f, 0.5f } +#define COLOR_BG_TITLE { 0.16, 0.2f, 0.17f, 0.8f } +#define COLOR_BG_VIEW { 0.16, 0.2f, 0.17f, 0.8f } +#define COLOR_BORDER_TITLE { 0.1f, 0.1f, 0.1f, 0.2f } +#define COLOR_BORDER_VIEW { 0.2f, 0.2f, 0.2f, 0.4f } +#define COLOR_HDR { 0.6f, 0.6f, 0.6f, 1.0f } +#define COLOR_HDR2 { 0.6f, 0.6f, 0.4f, 1.0f } +#define COLOR_TEXT { 0.625f, 0.625f, 0.6f, 1.0f } + +#define FONT_HEADER &cgs.media.limboFont1 +#define FONT_SUBHEADER &cgs.media.limboFont1_lo +#define FONT_TEXT &cgs.media.limboFont2 + + + + +vec4_t color_bg = COLOR_BG_VIEW; +vec4_t color_border = COLOR_BORDER_VIEW; +vec4_t color_hdr = COLOR_HDR2; +vec4_t color_name = COLOR_TEXT; + +#define VD_X 4 +#define VD_Y 78 +#define VD_SCALE_X_HDR 0.25f +#define VD_SCALE_Y_HDR 0.30f +#define VD_SCALE_X_NAME 0.30f +#define VD_SCALE_Y_NAME 0.30f + +qboolean CG_ViewingDraw() { + if ( cg.mvTotalClients < 1 ) { + return( qfalse ); + + } else { + int w, wTag; + int tSpacing = 15; // Should derive from CG_Text_Height_Ext + int pID = cg.mvCurrentMainview->mvInfo & MV_PID; + char *viewInfo = "Viewing:"; + + wTag = CG_Text_Width_Ext( viewInfo, VD_SCALE_X_HDR, 0, FONT_HEADER ); + w = wTag + 3 + CG_Text_Width_Ext( cgs.clientinfo[pID].name, VD_SCALE_X_NAME, 0, FONT_TEXT ); + + CG_DrawRect( VD_X - 2, VD_Y, w + 7, tSpacing + 4, 1, color_border ); + CG_FillRect( VD_X - 2, VD_Y, w + 7, tSpacing + 4, color_bg ); + + CG_Text_Paint_Ext( VD_X, VD_Y + tSpacing, // x, y + VD_SCALE_X_HDR, VD_SCALE_Y_HDR, // scale_x, scale_y + color_hdr, + viewInfo, + 0.0f, 0, + ITEM_TEXTSTYLE_SHADOWED, + FONT_HEADER ); + + CG_Text_Paint_Ext( VD_X + wTag + 5, VD_Y + tSpacing, // x, y + VD_SCALE_X_NAME, VD_SCALE_Y_NAME, // scale_x, scale_y + color_name, + cgs.clientinfo[pID].name, + 0.0f, 0, + ITEM_TEXTSTYLE_SHADOWED, + FONT_TEXT ); + + return( qtrue ); + } +} + + + +#define GS_X 166 +#define GS_Y 10 +#define GS_W 308 + +void CG_GameStatsDraw() { + if ( cgs.gamestats.show == SHOW_OFF ) { + return; + + } else { + int i, x = GS_X + 4, y = GS_Y, h; + gameStats_t *gs = &cgs.gamestats; + + vec4_t bgColor = COLOR_BG; // window + vec4_t borderColor = COLOR_BORDER; // window + + vec4_t bgColorTitle = COLOR_BG_TITLE; // titlebar + vec4_t borderColorTitle = COLOR_BORDER_TITLE; // titlebar + + // Main header + int hStyle = ITEM_TEXTSTYLE_SHADOWED; + float hScale = 0.16f; + float hScaleY = 0.21f; + fontInfo_t *hFont = FONT_HEADER; + + // Sub header + int hStyle2 = 0; + float hScale2 = 0.16f; + float hScaleY2 = 0.20f; + fontInfo_t *hFont2 = FONT_SUBHEADER; + + vec4_t hdrColor = COLOR_HDR; // text +// vec4_t hdrColor2 = COLOR_HDR2; // text + + // Text settings + int tStyle = ITEM_TEXTSTYLE_SHADOWED; + int tSpacing = 9; // Should derive from CG_Text_Height_Ext + float tScale = 0.19f; + fontInfo_t *tFont = FONT_TEXT; + vec4_t tColor = COLOR_TEXT; // text + + float diff = cgs.gamestats.fadeTime - cg.time; + + + // FIXME: Should compute this beforehand + h = 2 + tSpacing + 2 + // Header + 2 + 2 + tSpacing + 2 + // Stats columns + 1 + // Stats + extra + tSpacing * ( ( gs->cWeapons > 0 ) ? gs->cWeapons : 1 ) + + tSpacing * ( ( gs->fHasStats ) ? 3 : 0 ) + + ( ( cgs.gametype == GT_WOLF_LMS ) ? 0 : + ( + 4 + 2 * tSpacing + // Rank/XP + 1 + tSpacing + + 4 + 2 * tSpacing + // Skill columns + 1 + // Skillz + tSpacing * ( ( gs->cSkills > 0 ) ? gs->cSkills : 1 ) + ) + ) + + 2; + + // Fade-in effects + if ( diff > 0.0f ) { + float scale = ( diff / STATS_FADE_TIME ); + + if ( cgs.gamestats.show == SHOW_ON ) { + scale = 1.0f - scale; + } + + bgColor[3] *= scale; + bgColorTitle[3] *= scale; + borderColor[3] *= scale; + borderColorTitle[3] *= scale; + hdrColor[3] *= scale; + tColor[3] *= scale; + + y -= h * ( 1.0f - scale ); + + } else if ( cgs.gamestats.show == SHOW_SHUTDOWN ) { + cgs.gamestats.show = SHOW_OFF; + return; + } + + CG_DrawRect( GS_X, y, GS_W, h, 1, borderColor ); + CG_FillRect( GS_X, y, GS_W, h, bgColor ); + + + + // Header + CG_FillRect( GS_X, y, GS_W, tSpacing + 4, bgColorTitle ); + CG_DrawRect( GS_X, y, GS_W, tSpacing + 4, 1, borderColorTitle ); + + y += 1; + y += tSpacing; + CG_Text_Paint_Ext( x, y, hScale, hScaleY, hdrColor, "PLAYER STATS", 0.0f, 0, hStyle, hFont ); + y += 3; + + y += 2; + + + + // Weapon stats + y += 2; + CG_FillRect( GS_X, y, GS_W, tSpacing + 3, bgColorTitle ); + CG_DrawRect( GS_X, y, GS_W, tSpacing + 3, 1, borderColorTitle ); + + y += 1 + tSpacing; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Weapon", 0.0f, 0, hStyle2, hFont2 ); + x += 66; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Accuracy", 0.0f, 0, hStyle2, hFont2 ); + x += 53; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Hits / Shots", 0.0f, 0, hStyle2, hFont2 ); + x += 62; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Kills", 0.0f, 0, hStyle2, hFont2 ); + x += 29; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Deaths", 0.0f, 0, hStyle2, hFont2 ); + x += 40; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Headshots", 0.0f, 0, hStyle2, hFont2 ); + + x = GS_X + 4; + y += 2; + + y += 1; + if ( gs->cWeapons == 0 ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, "No weapon info available.", 0.0f, 0, tStyle, tFont ); + } else { + for ( i = 0; i < gs->cWeapons; i++ ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, gs->strWS[i], 0.0f, 0, tStyle, tFont ); + } + + if ( gs->fHasStats ) { + y += tSpacing; + for ( i = 0; i < 2; i++ ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, gs->strExtra[i], 0.0f, 0, tStyle, tFont ); + } + } + } + + + // No rank/xp/skill info for LMS + if ( cgs.gametype == GT_WOLF_LMS ) { + return; + } + + + // Rank/XP info + y += tSpacing; + y += 2; + CG_FillRect( GS_X, y, GS_W, tSpacing + 3, bgColorTitle ); + CG_DrawRect( GS_X, y, GS_W, tSpacing + 3, 1, borderColorTitle ); + + y += 1 + tSpacing; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Rank", 0.0f, 0, hStyle2, hFont2 ); + x += 82; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "XP", 0.0f, 0, hStyle2, hFont2 ); + + x = GS_X + 4; + + y += 1; + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, gs->strRank, 0.0f, 0, tStyle, tFont ); + + + + // Skill info + y += tSpacing; + y += 2; + CG_FillRect( GS_X, y, GS_W, tSpacing + 3, bgColorTitle ); + CG_DrawRect( GS_X, y, GS_W, tSpacing + 3, 1, borderColorTitle ); + + y += 1 + tSpacing; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Skills", 0.0f, 0, hStyle2, hFont2 ); + x += 84; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Level", 0.0f, 0, hStyle2, hFont2 ); + x += 40; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "XP / Next Level", 0.0f, 0, hStyle2, hFont2 ); + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + x += 86; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Medals", 0.0f, 0, hStyle2, hFont2 ); + } + + x = GS_X + 4; + + y += 1; + if ( gs->cSkills == 0 ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, "No skills acquired!", 0.0f, 0, tStyle, tFont ); + } else { + for ( i = 0; i < gs->cSkills; i++ ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, gs->strSkillz[i], 0.0f, 0, tStyle, tFont ); + } + + } + } +} + +#define TS_X -20 // spacing from right +#define TS_Y -60 // spacing from bottom +#define TS_W 308 + +void CG_TopShotsDraw() { + if ( cgs.topshots.show == SHOW_OFF ) { + return; + + } else { + int i, x = 640 + TS_X - TS_W, y = 480, h; + topshotStats_t *ts = &cgs.topshots; + + vec4_t bgColor = COLOR_BG; // window + vec4_t borderColor = COLOR_BORDER; // window + + vec4_t bgColorTitle = COLOR_BG_TITLE; // titlebar + vec4_t borderColorTitle = COLOR_BORDER_TITLE; // titlebar + + // Main header + int hStyle = ITEM_TEXTSTYLE_SHADOWED; + float hScale = 0.16f; + float hScaleY = 0.21f; + fontInfo_t *hFont = FONT_HEADER; + + // Sub header + int hStyle2 = 0; + float hScale2 = 0.16f; + float hScaleY2 = 0.20f; + fontInfo_t *hFont2 = FONT_SUBHEADER; + + vec4_t hdrColor = COLOR_HDR; // text + vec4_t hdrColor2 = COLOR_HDR2; // text + + // Text settings + int tStyle = ITEM_TEXTSTYLE_SHADOWED; + int tSpacing = 9; // Should derive from CG_Text_Height_Ext + float tScale = 0.19f; + fontInfo_t *tFont = FONT_TEXT; + vec4_t tColor = COLOR_TEXT; // text + + float diff = cgs.topshots.fadeTime - cg.time; + + + // FIXME: Should compute this beforehand + h = 2 + tSpacing + 2 + // Header + 2 + 2 + tSpacing + 2 + // Stats columns + 1 + // Stats + extra + tSpacing * ( ( ts->cWeapons > 0 ) ? ts->cWeapons : 1 ) + + 1; + + // Fade-in effects + if ( diff > 0.0f ) { + float scale = ( diff / STATS_FADE_TIME ); + + if ( cgs.topshots.show == SHOW_ON ) { + scale = 1.0f - scale; + } + + bgColor[3] *= scale; + bgColorTitle[3] *= scale; + borderColor[3] *= scale; + borderColorTitle[3] *= scale; + hdrColor[3] *= scale; + hdrColor2[3] *= scale; + tColor[3] *= scale; + + y += ( TS_Y - h ) * scale; + + } else if ( cgs.topshots.show == SHOW_SHUTDOWN ) { + cgs.topshots.show = SHOW_OFF; + return; + } else { + y += TS_Y - h; + } + + CG_DrawRect( x, y, TS_W, h, 1, borderColor ); + CG_FillRect( x, y, TS_W, h, bgColor ); + + + + // Header + CG_FillRect( x, y, TS_W, tSpacing + 4, bgColorTitle ); + CG_DrawRect( x, y, TS_W, tSpacing + 4, 1, borderColorTitle ); + + y += 1; + y += tSpacing; + CG_Text_Paint_Ext( x + 4, y, hScale, hScaleY, hdrColor, "\"TOPSHOT\" ACCURACIES", 0.0f, 0, hStyle, hFont ); + y += 4; + + + + // Weapon stats + y += 2; + CG_FillRect( x, y, TS_W, tSpacing + 3, bgColorTitle ); + CG_DrawRect( x, y, TS_W, tSpacing + 3, 1, borderColorTitle ); + + x += 4; + y += 1 + tSpacing; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Weapon", 0.0f, 0, hStyle2, hFont2 ); + x += 60; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Accuracy", 0.0f, 0, hStyle2, hFont2 ); + x += 53; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Hits / Shots", 0.0f, 0, hStyle2, hFont2 ); + x += 62; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Kills", 0.0f, 0, hStyle2, hFont2 ); + x += 32; + CG_Text_Paint_Ext( x, y, hScale2, hScaleY2, hdrColor, "Player", 0.0f, 0, hStyle2, hFont2 ); + + x = 640 + TS_X - TS_W + 4; + y += 1; + + if ( ts->cWeapons == 0 ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, "No qualifying weapon info available.", 0.0f, 0, tStyle, tFont ); + } else { + for ( i = 0; i < ts->cWeapons; i++ ) { + y += tSpacing; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, ts->strWS[i], 0.0f, 0, tStyle, tFont ); + } + } + } +} + + + +#define DH_X -20 // spacing from right +#define DH_Y -60 // spacing from bottom +#define DH_W 148 + +void CG_DemoHelpDraw() { + if ( cg.demohelpWindow == SHOW_OFF ) { + return; + + } else { + const char *help[] = { + "^nTAB ^mscores", + "^nF1-F5 ^mavidemo record", + "^nF11-F12 ^mscreenshot", + NULL, + "^nKP_DOWN ^mslow down (--)", + "^nKP_LEFT ^mslow down (-)", + "^nKP_UP ^mspeed up (++)", + "^nKP_RIGHT ^mspeed up (+)", + "^nSPACE ^mnormal speed", + NULL, + "^nENTER ^mExternal view", + "^nLFT/RGHT ^mChange angle", + "^nUP/DOWN ^mMove in/out" + }; + + const char *mvhelp[] = { + NULL, + "^nMOUSE1 ^mSelect/move view", + "^nMOUSE2 ^mSwap w/main view", + "^nMOUSE3 ^mToggle on/off", + "^nSHIFT ^mHold to resize", + "^nKP_PGUP ^mEnable a view", + "^nKP_PGDN ^mClose a view" + }; + + int i, x, y = 480, w, h; + + vec4_t bgColor = COLOR_BG; // window + vec4_t borderColor = COLOR_BORDER; // window + + vec4_t bgColorTitle = COLOR_BG_TITLE; // titlebar + vec4_t borderColorTitle = COLOR_BORDER_TITLE; // titlebar + + // Main header + int hStyle = ITEM_TEXTSTYLE_SHADOWED; + float hScale = 0.16f; + float hScaleY = 0.21f; + fontInfo_t *hFont = FONT_HEADER; + vec4_t hdrColor2 = COLOR_HDR2; // text + + // Text settings + int tStyle = ITEM_TEXTSTYLE_SHADOWED; + int tSpacing = 9; // Should derive from CG_Text_Height_Ext + float tScale = 0.19f; + fontInfo_t *tFont = FONT_TEXT; + vec4_t tColor = COLOR_TEXT; // text + + float diff = cg.fadeTime - trap_Milliseconds(); + + + // FIXME: Should compute this beforehand + w = DH_W + ( ( cg.mvTotalClients > 1 ) ? 12 : 0 ); + x = 640 + DH_X - w; + h = 2 + tSpacing + 2 + // Header + 2 + 1 + + tSpacing * ( 2 + ( sizeof( help ) + ( ( cg.mvTotalClients > 1 ) ? sizeof( mvhelp ) : 0 ) ) / sizeof( char * ) ) + + 2; + + // Fade-in effects + if ( diff > 0.0f ) { + float scale = ( diff / STATS_FADE_TIME ); + + if ( cg.demohelpWindow == SHOW_ON ) { + scale = 1.0f - scale; + } + + bgColor[3] *= scale; + bgColorTitle[3] *= scale; + borderColor[3] *= scale; + borderColorTitle[3] *= scale; + hdrColor2[3] *= scale; + tColor[3] *= scale; + + y += ( DH_Y - h ) * scale; + + } else if ( cg.demohelpWindow == SHOW_SHUTDOWN ) { + cg.demohelpWindow = SHOW_OFF; + return; + } else { + y += DH_Y - h; + } + + CG_DrawRect( x, y, w, h, 1, borderColor ); + CG_FillRect( x, y, w, h, bgColor ); + + + + // Header + CG_FillRect( x, y, w, tSpacing + 4, bgColorTitle ); + CG_DrawRect( x, y, w, tSpacing + 4, 1, borderColorTitle ); + + x += 4; + y += 1; + y += tSpacing; + CG_Text_Paint_Ext( x, y, hScale, hScaleY, hdrColor2, "DEMO CONTROLS", 0.0f, 0, hStyle, hFont ); + y += 3; + + + + // Control info + for ( i = 0; i < sizeof( help ) / sizeof( char * ); i++ ) { + y += tSpacing; + if ( help[i] != NULL ) { + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, (char*)help[i], 0.0f, 0, tStyle, tFont ); + } + } + + if ( cg.mvTotalClients > 1 ) { + for ( i = 0; i < sizeof( mvhelp ) / sizeof( char * ); i++ ) { + y += tSpacing; + if ( mvhelp[i] != NULL ) { + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, (char*)mvhelp[i], 0.0f, 0, tStyle, tFont ); + } + } + } + + y += tSpacing * 2; + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, "^nBACKSPACE ^mhelp on/off", 0.0f, 0, tStyle, tFont ); + } +} + + + + +char *CG_getBindKeyName( const char *cmd, char *buf, int len ) { + int j; + + for ( j = 0; j < 256; j++ ) { + trap_Key_GetBindingBuf( j, buf, len ); + if ( *buf == 0 ) { + continue; + } + + if ( !Q_stricmp( buf, cmd ) ) { + trap_Key_KeynumToStringBuf( j, buf, MAX_STRING_TOKENS ); + Q_strupr( buf ); + return( buf ); + } + } + + Q_strncpyz( buf, va( "(%s)", cmd ), len ); + return( buf ); +} + +typedef struct +{ + char *cmd; + char *info; +} helpType_t; + + +#define SH_X 2 // spacing from left +#define SH_Y 155 // spacing from top + +void CG_SpecHelpDraw() { + if ( cg.spechelpWindow == SHOW_OFF ) { + return; + + } else { + const helpType_t help[] = + { + { "+zoom", "hold for pointer" }, + { "+attack", "window move/resize" }, + { "+sprint", "hold to resize" }, + { "weapnext", "window on/off" }, + { "weapprev", "swap w/main view" }, + { NULL, NULL }, + { "weapalt", "swingcam toggle" }, + { "spechelp", "help on/off" } + }; + + int i, x, y = 480, w, h; + int len, maxlen = 0; + char format[MAX_STRING_TOKENS], buf[MAX_STRING_TOKENS]; + char *lines[16]; + + vec4_t bgColor = COLOR_BG; // window + vec4_t borderColor = COLOR_BORDER; // window + + vec4_t bgColorTitle = COLOR_BG_TITLE; // titlebar + vec4_t borderColorTitle = COLOR_BORDER_TITLE; // titlebar + + // Main header + int hStyle = ITEM_TEXTSTYLE_SHADOWED; + float hScale = 0.16f; + float hScaleY = 0.21f; + fontInfo_t *hFont = FONT_HEADER; + vec4_t hdrColor2 = COLOR_HDR2; // text + + // Text settings + int tStyle = ITEM_TEXTSTYLE_SHADOWED; + int tSpacing = 9; // Should derive from CG_Text_Height_Ext + float tScale = 0.19f; + fontInfo_t *tFont = FONT_TEXT; + vec4_t tColor = COLOR_TEXT; // text + + float diff = cg.fadeTime - trap_Milliseconds(); + + + // FIXME: Should compute all this stuff beforehand + // Compute required width + for ( i = 0; i < sizeof( help ) / sizeof( helpType_t ); i++ ) { + if ( help[i].cmd != NULL ) { + len = strlen( CG_getBindKeyName( help[i].cmd, buf, sizeof( buf ) ) ); + if ( len > maxlen ) { + maxlen = len; + } + } + } + + Q_strncpyz( format, va( "^2%%%ds ^N%%s", maxlen ), sizeof( format ) ); + for ( i = 0, maxlen = 0; i < sizeof( help ) / sizeof( helpType_t ); i++ ) { + if ( help[i].cmd != NULL ) { + lines[i] = va( format, CG_getBindKeyName( help[i].cmd, buf, sizeof( buf ) ), help[i].info ); + len = CG_Text_Width_Ext( lines[i], tScale, 0, FONT_TEXT ); + if ( len > maxlen ) { + maxlen = len; + } + } else { + lines[i] = NULL; + } + } + + w = maxlen + 8; + x = SH_X; + y = SH_Y; + h = 2 + tSpacing + 2 + // Header + 2 + 1 + + tSpacing * ( sizeof( help ) / sizeof( helpType_t ) ) + + 2; + + // Fade-in effects + if ( diff > 0.0f ) { + float scale = ( diff / STATS_FADE_TIME ); + + if ( cg.spechelpWindow == SHOW_ON ) { + scale = 1.0f - scale; + } + + bgColor[3] *= scale; + bgColorTitle[3] *= scale; + borderColor[3] *= scale; + borderColorTitle[3] *= scale; + hdrColor2[3] *= scale; + tColor[3] *= scale; + + x -= w * ( 1.0f - scale ); + + } else if ( cg.spechelpWindow == SHOW_SHUTDOWN ) { + cg.spechelpWindow = SHOW_OFF; + return; + } + + CG_DrawRect( x, y, w, h, 1, borderColor ); + CG_FillRect( x, y, w, h, bgColor ); + + + + // Header + CG_FillRect( x, y, w, tSpacing + 4, bgColorTitle ); + CG_DrawRect( x, y, w, tSpacing + 4, 1, borderColorTitle ); + + x += 4; + y += 1; + y += tSpacing; + CG_Text_Paint_Ext( x, y, hScale, hScaleY, hdrColor2, "SPECTATOR CONTROLS", 0.0f, 0, hStyle, hFont ); + y += 3; + + + + // Control info + for ( i = 0; i < sizeof( help ) / sizeof( helpType_t ); i++ ) { + y += tSpacing; + if ( lines[i] != NULL ) { + CG_Text_Paint_Ext( x, y, tScale, tScale, tColor, lines[i], 0.0f, 0, tStyle, tFont ); + } + } + } +} + + +void CG_DrawOverlays( void ) { + CG_GameStatsDraw(); + CG_TopShotsDraw(); +#ifdef MV_SUPPORT + CG_SpecHelpDraw(); +#endif + if ( cg.demoPlayback ) { + CG_DemoHelpDraw(); + } +} diff --git a/src/cgame/cg_limbopanel.c b/src/cgame/cg_limbopanel.c new file mode 100644 index 0000000..8f3b400 --- /dev/null +++ b/src/cgame/cg_limbopanel.c @@ -0,0 +1,2903 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +#define SOUNDEVENT( sound ) trap_S_StartLocalSound( sound, CHAN_LOCAL_SOUND ) + +#define SOUND_SELECT SOUNDEVENT( cgs.media.sndLimboSelect ) +#define SOUND_FOCUS SOUNDEVENT( cgs.media.sndLimboFocus ) +#define SOUND_FILTER SOUNDEVENT( cgs.media.sndLimboFilter ) +#define SOUND_CANCEL SOUNDEVENT( cgs.media.sndLimboCancel ) + +void CG_DrawBorder( float x, float y, float w, float h, qboolean fill, qboolean drawMouseOver ); + +team_t teamOrder[3] = { + TEAM_ALLIES, + TEAM_AXIS, + TEAM_SPECTATOR, +}; + +panel_button_text_t nameEditFont = { + 0.22f, 0.24f, + { 1.f, 1.f, 1.f, 0.8f }, + ITEM_TEXTSTYLE_SHADOWED, 0, + &cgs.media.limboFont2, +}; + +panel_button_text_t classBarFont = { + 0.22f, 0.24f, + { 0.f, 0.f, 0.f, 0.8f }, + 0, 0, + &cgs.media.limboFont2, +}; + +panel_button_text_t titleLimboFont = { + 0.24f, 0.28f, + { 1.f, 1.f, 1.f, 0.6f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t titleLimboFontBig = { + 0.3f, 0.3f, + { 1.f, 1.f, 1.f, 0.6f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t titleLimboFontBigCenter = { + 0.3f, 0.3f, + { 1.f, 1.f, 1.f, 0.6f }, + 0, ITEM_ALIGN_CENTER, + &cgs.media.limboFont1, +}; + +panel_button_text_t spawnLimboFont = { + 0.18f, 0.22f, + { 1.f, 1.f, 1.f, 0.6f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t weaponButtonFont = { + 0.33f, 0.33f, + { 0.f, 0.f, 0.f, 0.6f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t weaponPanelNameFont = { + 0.20f, 0.24f, + { 1.0f, 1.0f, 1.0f, 0.4f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t weaponPanelFilterFont = { + 0.17f, 0.17f, + { 1.0f, 1.0f, 1.0f, 0.6f }, + 0, 0, + &cgs.media.limboFont1_lo, +}; + +panel_button_text_t weaponPanelStatsFont = { + 0.15f, 0.17f, + { 1.0f, 1.0f, 1.0f, 0.6f }, + 0, 0, + &cgs.media.limboFont1_lo, +}; + +panel_button_text_t weaponPanelStatsPercFont = { + 0.2f, 0.2f, + { 1.0f, 1.0f, 1.0f, 0.6f }, + 0, 0, + &cgs.media.limboFont1, +}; + +panel_button_text_t objectivePanelTxt = { + 0.2f, 0.2f, + { 0.0f, 0.0f, 0.0f, 0.5f }, + 0, 0, + &cgs.media.limboFont2, +}; + + +panel_button_t rightLimboPannel = { + "gfx/limbo/limbo_back", + NULL, + { 440, 0, 200, 480 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +#define MEDAL_PIC_GAP ( ( MEDAL_PIC_SIZE - ( MEDAL_PIC_WIDTH * MEDAL_PIC_COUNT ) ) / ( MEDAL_PIC_COUNT + 1.f ) ) +#define MEDAL_PIC_COUNT 7.f +#define MEDAL_PIC_WIDTH 22.f +#define MEDAL_PIC_X 450.f +#define MEDAL_PIC_SIZE ( 630.f - MEDAL_PIC_X ) +#define MEDAL_PIC( number ) \ + panel_button_t medalPic ## number = { \ + NULL, \ + NULL, \ + { MEDAL_PIC_X + MEDAL_PIC_GAP + ( number * ( MEDAL_PIC_GAP + MEDAL_PIC_WIDTH ) ), 119, MEDAL_PIC_WIDTH, 26 }, \ + { number, 0, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderMedal, \ + NULL, \ + } + +MEDAL_PIC( 0 ); +MEDAL_PIC( 1 ); +MEDAL_PIC( 2 ); +MEDAL_PIC( 3 ); +MEDAL_PIC( 4 ); +MEDAL_PIC( 5 ); +MEDAL_PIC( 6 ); + + +#define TEAM_COUNTER_GAP ( ( TEAM_COUNTER_SIZE - ( TEAM_COUNTER_WIDTH * TEAM_COUNTER_COUNT ) ) / ( TEAM_COUNTER_COUNT + 1.f ) ) +#define TEAM_COUNTER_COUNT 3.f +#define TEAM_COUNTER_WIDTH 20.f +#define TEAM_COUNTER_X 432.f +#define TEAM_COUNTER_SIZE ( 660.f - TEAM_COUNTER_X ) +#define TEAM_COUNTER_BUTTON_DIFF -24.f +#define TEAM_COUNTER_SPACING 4.f + +#define TEAM_COUNTER( number ) \ + panel_button_t teamCounter ## number = { \ + NULL, \ + NULL, \ + { TEAM_COUNTER_X + TEAM_COUNTER_GAP + ( number * ( TEAM_COUNTER_GAP + TEAM_COUNTER_WIDTH ) ), 236, TEAM_COUNTER_WIDTH, 14 }, \ + { 1, number, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderCounter, \ + NULL, \ + }; \ + panel_button_t teamCounterLight ## number = { \ + NULL, \ + NULL, \ + { TEAM_COUNTER_X + TEAM_COUNTER_GAP + ( number * ( TEAM_COUNTER_GAP + TEAM_COUNTER_WIDTH ) ) - 20, 236, 16, 16 }, \ + { 1, number, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderLight, \ + NULL, \ + }; \ + panel_button_t teamButton ## number = { \ + NULL, \ + NULL, \ + { TEAM_COUNTER_X + TEAM_COUNTER_GAP + ( number * ( TEAM_COUNTER_GAP + TEAM_COUNTER_WIDTH ) + ( TEAM_COUNTER_BUTTON_DIFF / 2.f ) ) - 17 + TEAM_COUNTER_SPACING, \ + 188 + TEAM_COUNTER_SPACING, \ + TEAM_COUNTER_WIDTH - TEAM_COUNTER_BUTTON_DIFF + 20 - 2 * TEAM_COUNTER_SPACING, \ + 44 - 2 * TEAM_COUNTER_SPACING }, \ + { number, 0, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + CG_LimboPanel_TeamButton_KeyDown, /* keyDown */\ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderTeamButton, \ + NULL, \ + } + +TEAM_COUNTER( 0 ); +TEAM_COUNTER( 1 ); +TEAM_COUNTER( 2 ); + +#define CLASS_COUNTER_GAP ( ( CLASS_COUNTER_SIZE - ( CLASS_COUNTER_WIDTH * CLASS_COUNTER_COUNT ) ) / ( CLASS_COUNTER_COUNT + 1.f ) ) +#define CLASS_COUNTER_COUNT 5.f +#define CLASS_COUNTER_WIDTH 20.f +#define CLASS_COUNTER_X 435.f +#define CLASS_COUNTER_SIZE ( 645.f - CLASS_COUNTER_X ) +#define CLASS_COUNTER_LIGHT_DIFF 4.f +#define CLASS_COUNTER_BUTTON_DIFF -18.f +#define CLASS_COUNTER( number ) \ + panel_button_t classCounter ## number = { \ + NULL, \ + NULL, \ + { CLASS_COUNTER_X + CLASS_COUNTER_GAP + ( number * ( CLASS_COUNTER_GAP + CLASS_COUNTER_WIDTH ) ), 302, CLASS_COUNTER_WIDTH, 14 }, \ + { 0, number, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderCounter, \ + NULL, \ + }; \ + panel_button_t classButton ## number = { \ + NULL, \ + NULL, \ + { CLASS_COUNTER_X + CLASS_COUNTER_GAP + ( number * ( CLASS_COUNTER_GAP + CLASS_COUNTER_WIDTH ) ) + ( CLASS_COUNTER_BUTTON_DIFF / 2.f ), 266, CLASS_COUNTER_WIDTH - CLASS_COUNTER_BUTTON_DIFF, 34 }, \ + { 0, number, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + CG_LimboPanel_ClassButton_KeyDown, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_RenderClassButton, \ + NULL, \ + } +/*panel_button_t classCounterLight##number = { \ + NULL, \ + NULL, \ + { CLASS_COUNTER_X + CLASS_COUNTER_GAP + (number*(CLASS_COUNTER_GAP + CLASS_COUNTER_WIDTH)) + (CLASS_COUNTER_LIGHT_DIFF/2.f), 266, CLASS_COUNTER_WIDTH - CLASS_COUNTER_LIGHT_DIFF, 16 }, \ + { 0, number, 0, 0, 0, 0, 0, 0 }, \ + NULL, \ + CG_LimboPanel_ClassButton_KeyDown, \ + NULL, \ + CG_LimboPanel_RenderLight, \ + NULL, \ +}; \*/ + +panel_button_t classBar = { + "gfx/limbo/lightup_bar", + NULL, + { 470, 320, 140, 20 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t classBarText = { + NULL, + NULL, + { 460, 334, 160, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &classBarFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_ClassBar_Draw, + NULL, +}; + +CLASS_COUNTER( 0 ); +CLASS_COUNTER( 1 ); +CLASS_COUNTER( 2 ); +CLASS_COUNTER( 3 ); +CLASS_COUNTER( 4 ); + +#define FILTER_BUTTON( number ) \ + panel_button_t filterButton ## number = { \ + NULL, \ + NULL, \ + { 15, 54 + ( number * 31 ), 26, 26 }, \ + { number, 0, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + CG_LimboPanel_Filter_KeyDown, /* keyDown */ \ + NULL, /* keyUp */ \ + CG_LimboPanel_Filter_Draw, \ + NULL, \ + } + +FILTER_BUTTON( 0 ); +FILTER_BUTTON( 1 ); +FILTER_BUTTON( 2 ); +FILTER_BUTTON( 3 ); +FILTER_BUTTON( 4 ); +FILTER_BUTTON( 5 ); +FILTER_BUTTON( 6 ); +FILTER_BUTTON( 7 ); + +panel_button_t filterTitleText = { + NULL, + "FILTERS", + { 8, 36, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelFilterFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +#define LEFT_FRAME( shader, number, x, y, w, h ) \ + panel_button_t leftFrame0 ## number = { \ + shader, \ + NULL, \ + { x, y, w, h }, \ + { 0, 0, 0, 0, 0, 0, 0, 0 }, \ + NULL, /* font */ \ + NULL, /* keyDown */ \ + NULL, /* keyUp */ \ + BG_PanelButtonsRender_Img, \ + NULL, \ + } + +#define LF_X1 64 +#define LF_X2 416 +#define LF_X3 440 + +#define LF_W1 ( LF_X1 - 0 ) +#define LF_W2 ( LF_X2 - LF_X1 ) +#define LF_W3 ( LF_X3 - LF_X2 ) + +#define LF_Y1 23 +#define LF_Y2 375 +#define LF_Y3 480 + +#define LF_H1 ( LF_Y1 - 0 ) +#define LF_H2 ( LF_Y2 - LF_Y1 ) +#define LF_H3 ( LF_Y3 - LF_Y2 ) + +LEFT_FRAME( "gfx/limbo/limbo_frame01", 1, 0, 0, LF_W1, LF_H1 ); +LEFT_FRAME( "gfx/limbo/limbo_frame02", 2, LF_X1, 0, LF_W2, LF_H1 ); +LEFT_FRAME( "gfx/limbo/limbo_frame03", 3, LF_X2, 0, LF_W3, LF_H1 ); + +LEFT_FRAME( "gfx/limbo/limbo_frame04", 4, LF_X2, LF_Y1, LF_W3, LF_H2 ); + +LEFT_FRAME( "gfx/limbo/limbo_frame05", 5, LF_X2, LF_Y2, LF_W3, LF_H3 ); +LEFT_FRAME( "gfx/limbo/limbo_frame06", 6, LF_X1, LF_Y2, LF_W2, LF_H3 ); +LEFT_FRAME( "gfx/limbo/limbo_frame07", 7, 0, LF_Y2, LF_W1, LF_H3 ); + +LEFT_FRAME( "gfx/limbo/limbo_frame08", 8, 0, LF_Y1, LF_W1, LF_H2 ); + +panel_button_t playerLimboHead = { + NULL, + NULL, + { 456, 30, 68, 84 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderHead, + NULL, +}; + +panel_button_t playerXPCounterText = { + NULL, + "XP", + { 546, 108, 60, 16 }, + { 2, 0, 0, 0, 0, 0, 0, 0 }, + &spawnLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanelRenderText_NoLMS, + NULL, +}; + +panel_button_t playerXPCounter = { + NULL, + NULL, + { 564, 96, 60, 16 }, + { 2, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t playerSkillCounter0 = { + NULL, + NULL, + { 552, 36, 60, 16 }, + { 4, 0, 0, 0, 0, 0, 4, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t playerSkillCounter1 = { + NULL, + NULL, + { 552, 56, 60, 16 }, + { 4, 1, 0, 0, 0, 0, 4, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t playerSkillCounter2 = { + NULL, + NULL, + { 552, 76, 60, 16 }, + { 4, 2, 0, 0, 0, 0, 4, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t playerSkillIcon0 = { + NULL, + NULL, + { 532, 36, 16, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderSkillIcon, + NULL, +}; + +panel_button_t playerSkillIcon1 = { + NULL, + NULL, + { 532, 56, 16, 16 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderSkillIcon, + NULL, +}; + +panel_button_t playerSkillIcon2 = { + NULL, + NULL, + { 532, 76, 16, 16 }, + { 2, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderSkillIcon, + NULL, +}; + +// ======================= + + +panel_button_t mapTimeCounter = { + NULL, + NULL, + { 276, 5, 20, 14 }, + { 5, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t mapTimeCounter2 = { + NULL, + NULL, + { 252, 5, 20, 14 }, + { 5, 1, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t mapTimeCounterText = { + NULL, + "MISSION TIME", + { 172, 16, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &spawnLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +// ======================= + +panel_button_t respawnCounter = { + NULL, + NULL, + { 400, 5, 20, 14 }, + { 3, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t respawnCounterText = { + NULL, + "REINFORCEMENTS", + { 300, 16, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &spawnLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +// ======================= + +panel_button_t limboTitleText = { + NULL, + "COMMAND MAP", + { 8, 16, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t playerSetupText = { + NULL, + "PLAYER SETUP", + { 448, 16, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t skillsText = { + NULL, + "SKILLS", + { 532, 32, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelStatsFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanelRenderText_SkillsText, + NULL, +}; + +// ======================= + +panel_button_t weaponPanel = { + NULL, + NULL, + { 455, 353, 140, 56 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_WeaponPanel_KeyDown, /* keyDown */ + CG_LimboPanel_WeaponPanel_KeyUp, /* keyUp */ + CG_LimboPanel_WeaponPanel, + NULL, +}; + +panel_button_t weaponLight1 = { + NULL, + NULL, + { 605, 362, 20, 20 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_WeaponLights_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_WeaponLights, + NULL, +}; + +panel_button_t weaponLight1Text = { + NULL, + "1", + { 609, 378, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponButtonFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t weaponLight2 = { + NULL, + NULL, + { 605, 386, 20, 20 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_WeaponLights_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_WeaponLights, + NULL, +}; + +panel_button_t weaponLight2Text = { + NULL, + "2", + { 609, 402, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponButtonFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t weaponStatsShotsText = { + NULL, + "SHOTS", + { 460, 422, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelStatsFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t weaponStatsShotsCounter = { + NULL, + NULL, + { 460, 426, 40, 14 }, + { 6, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + + +panel_button_t weaponStatsHitsText = { + NULL, + "HITS", + { 516, 422, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelStatsFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t weaponStatsHitsCounter = { + NULL, + NULL, + { 516, 426, 40, 14 }, + { 6, 1, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + + +panel_button_t weaponStatsAccText = { + NULL, + "ACC", + { 570, 422, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelStatsFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t weaponStatsAccCounter = { + NULL, + NULL, + { 570, 426, 30, 14 }, + { 6, 2, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCounter, + NULL, +}; + +panel_button_t weaponStatsAccPercentage = { + NULL, + "%", + { 600, 436, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &weaponPanelStatsPercFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +// ======================= + +panel_button_t commandmapPanel = { + NULL, + NULL, + { CC_2D_X, CC_2D_Y, CC_2D_W, CC_2D_H }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderCommandMap, + NULL, +}; + +// ======================= + +panel_button_t objectivePanel = { + NULL, + NULL, + { 8, 398, 240, 74 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderObjectiveBack, + NULL, +}; + +panel_button_t objectivePanelText = { + NULL, + NULL, + { 8, 398, 240, 74 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &objectivePanelTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_RenderObjectiveText, + NULL, +}; + +panel_button_t objectivePanelTitle = { + NULL, + "OBJECTIVES", + { 8, 392, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t objectivePanelButtonUp = { + "gfx/limbo/but_objective_up", + NULL, + { 252, 416, 24, 24 }, + { 0, 0, 0, 0, 0, 0, 0, 1 }, + NULL, /* font */ + CG_LimboPanel_ObjectiveText_KeyDown, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t briefingButton = { + NULL, + NULL, + { 252, 388, 24, 24 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_BriefingButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_BriefingButton_Draw, + NULL, +}; + +panel_button_t objectivePanelButtonDown = { + "gfx/limbo/but_objective_dn", + NULL, + { 252, 444, 24, 24 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_ObjectiveText_KeyDown, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +// ======================= + +panel_button_t okButtonText = { + NULL, + "OK", + { 484, 469, 100, 40 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t okButton = { + NULL, + NULL, + { 454 + 2, 454 + 2, 82 - 4, 18 - 4 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_OkButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_Border_Draw, + NULL, +}; + +panel_button_t cancelButtonText = { + NULL, + "CANCEL", + { 556, 469, 100, 40 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFont, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t cancelButton = { + NULL, + NULL, + { 543 + 2, 454 + 2, 82 - 4, 18 - 4 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_CancelButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_Border_Draw, + NULL, +}; + +// ======================= + +panel_button_t nameEdit = { + NULL, + "limboname", + { 480, 150, 120, 20 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &nameEditFont, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButton_RenderEdit, + CG_LimboPanel_NameEditFinish, +}; + +panel_button_t plusButton = { + NULL, + NULL, + { 19, 320, 18, 14 }, + { 12, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_PlusButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_Border_Draw, + NULL, +}; + +panel_button_t plusButtonText = { + NULL, + "+", + { 19, 321, 18, 14 }, + { 12, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFontBigCenter, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t minusButton = { + NULL, + NULL, + { 19, 346, 18, 14 }, + { 12, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + CG_LimboPanel_MinusButton_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_LimboPanel_Border_Draw, + NULL, +}; + +panel_button_t minusButtonText = { + NULL, + "-", + { 19, 346, 18, 14 }, + { 12, 0, 0, 0, 0, 0, 0, 0 }, + &titleLimboFontBigCenter, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t* limboPanelButtons[] = { + &rightLimboPannel, + + &classCounter0, &classCounter1, &classCounter2, &classCounter3, &classCounter4, +// &classCounterLight0, &classCounterLight1, &classCounterLight2, &classCounterLight3, &classCounterLight4, + &classButton0, &classButton1, &classButton2, &classButton3, &classButton4, + + &classBar, &classBarText, + + &leftFrame01, &leftFrame02, &leftFrame03, &leftFrame04, + &leftFrame05, &leftFrame06, &leftFrame07, &leftFrame08, + + &filterButton0, &filterButton1, &filterButton2, &filterButton3, &filterButton4, + &filterButton5, &filterButton6, &filterButton7, /* &filterButton8,*/ + &filterTitleText, + + &medalPic0, &medalPic1, &medalPic2, &medalPic3, &medalPic4, &medalPic5, &medalPic6, + + &teamCounter0, &teamCounter1, &teamCounter2, + &teamCounterLight0, &teamCounterLight1, &teamCounterLight2, + &teamButton0, &teamButton1, &teamButton2, + + &playerLimboHead, + &playerXPCounter, &playerXPCounterText, + + &respawnCounter, &respawnCounterText, + &mapTimeCounter, &mapTimeCounter2, &mapTimeCounterText, + + &playerSkillCounter0, &playerSkillCounter1, &playerSkillCounter2, + &playerSkillIcon0, &playerSkillIcon1, &playerSkillIcon2, + + &objectivePanel, &objectivePanelTitle, &objectivePanelText, + &objectivePanelButtonUp, &objectivePanelButtonDown, + + &limboTitleText, + &playerSetupText, + &skillsText, + + &commandmapPanel, + + &okButton, &okButtonText, + &cancelButton, &cancelButtonText, + + &nameEdit, + + &weaponLight1, &weaponLight2, + &weaponLight1Text, &weaponLight2Text, + &weaponPanel, + &weaponStatsShotsText, &weaponStatsHitsText, &weaponStatsAccText, + &weaponStatsShotsCounter, &weaponStatsHitsCounter, &weaponStatsAccCounter, + &weaponStatsAccPercentage, + + &briefingButton, + + &plusButton, &plusButtonText, + &minusButton, &minusButtonText, + + NULL, +}; + +qboolean CG_LimboPanel_BriefingButton_KeyDown( panel_button_t* button, int key ) { + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return qfalse; + } + + if ( key == K_MOUSE1 ) { + + SOUND_SELECT; + + if ( cg.limboEndCinematicTime > cg.time ) { + trap_S_StopStreamingSound( -1 ); + cg.limboEndCinematicTime = 0; + + return qtrue; + } + + cg.limboEndCinematicTime = cg.time + CG_SoundPlaySoundScript( va( "news_%s", cgs.rawmapname ), NULL, -1, qfalse ); + + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_BriefingButton_Draw( panel_button_t* button ) { + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return; + } + + if ( cg.limboEndCinematicTime > cg.time ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, BG_CursorInRect( &button->rect ) ? cgs.media.limboBriefingButtonStopOn : cgs.media.limboBriefingButtonStopOff ); + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, BG_CursorInRect( &button->rect ) ? cgs.media.limboBriefingButtonOn : cgs.media.limboBriefingButtonOff ); + } +} + +void CG_LimboPanel_NameEditFinish( panel_button_t* button ) { + char buffer[256]; + trap_Cvar_VariableStringBuffer( button->text, buffer, 256 ); + trap_Cvar_Set( "name", buffer ); +} + +qboolean CG_LimboPanel_CancelButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_CANCEL; + + if ( cgs.limboLoadoutModified ) { + trap_SendClientCommand( "rs" ); + + cgs.limboLoadoutSelected = qfalse; + } + + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + + return qtrue; + } + return qfalse; +} + +qboolean CG_LimboPanel_PlusButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + cgs.ccZoomFactor /= 0.75f; + + if ( cgs.ccZoomFactor > 1.f ) { + cgs.ccZoomFactor = 1.f; + } + + return qtrue; + } + + return qfalse; +} + +qboolean CG_LimboPanel_MinusButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + cgs.ccZoomFactor *= 0.75f; + + if ( cgs.ccZoomFactor < ( 0.75f * 0.75f * 0.75f * 0.75f * 0.75f ) ) { + cgs.ccZoomFactor = ( 0.75f * 0.75f * 0.75f * 0.75f * 0.75f ); + } + + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_SendSetupMsg( qboolean forceteam ) { + weapon_t weap1, weap2; + const char* str; + team_t team; + + if ( forceteam ) { + team = CG_LimboPanel_GetTeam(); + } else { + team = cgs.clientinfo[ cg.clientNum ].team; + } + + if ( team == TEAM_SPECTATOR ) { + if ( forceteam ) { + if ( cgs.clientinfo[ cg.clientNum ].team != TEAM_SPECTATOR ) { + trap_SendClientCommand( "team s 0 0 0\n" ); + } + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + return; + } + + weap1 = CG_LimboPanel_GetSelectedWeaponForSlot( 1 ); + weap2 = CG_LimboPanel_GetSelectedWeaponForSlot( 0 ); + + switch ( team ) { + case TEAM_AXIS: + str = "r"; + break; + case TEAM_ALLIES: + str = "b"; + break; + default: + str = NULL; // rain - don't go spec + break; + } + + // rain - if this happens, we're dazed and confused, abort + if ( !str ) { + return; + } + + trap_SendClientCommand( va( "team %s %i %i %i\n", str, CG_LimboPanel_GetClass(), weap1, weap2 ) ); + + if ( forceteam ) { + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + } + + // print center message + switch ( CG_LimboPanel_GetTeam() ) { + case TEAM_AXIS: + str = "Axis"; + break; + case TEAM_ALLIES: + str = "Allied"; + break; + default: // rain - added default + str = "unknown"; + break; + } + + + { + weaponType_t* wt = WM_FindWeaponTypeForWeapon( weap1 ); + CG_PriorityCenterPrint( va( "You will spawn as an %s %s with a %s.", str, BG_ClassnameForNumber( CG_LimboPanel_GetClass() ), wt ? wt->desc : "^1UNKNOWN WEAPON" ), SCREEN_HEIGHT - 88, SMALLCHAR_WIDTH, -1 ); + } + + cgs.limboLoadoutSelected = qtrue; + cgs.limboLoadoutModified = qtrue; +} + +qboolean CG_LimboPanel_OkButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + CG_LimboPanel_SendSetupMsg( qtrue ); + + return qtrue; + } + + return qfalse; +} + +qboolean CG_LimboPanel_TeamButton_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + if ( cgs.ccSelectedTeam != button->data[0] ) { + int oldmax = CG_LimboPanel_GetMaxObjectives(); + + cgs.ccSelectedTeam = button->data[0]; + + if ( cgs.ccSelectedObjective == oldmax ) { + cgs.ccSelectedObjective = CG_LimboPanel_GetMaxObjectives(); + } + + CG_LimboPanel_SetSelectedWeaponNumForSlot( 0, 0 ); + + CG_LimboPanel_RequestWeaponStats(); + + cgs.limboLoadoutModified = qtrue; + } + + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_RenderTeamButton( panel_button_t* button ) { +// vec4_t clr = { 1.f, 1.f, 1.f, 1.0f }; + vec4_t clr2 = { 1.f, 1.f, 1.f, 0.4f }; + + qhandle_t shader; + + trap_R_SetColor( colorBlack ); + CG_DrawPic( button->rect.x + 1, button->rect.y + 1, button->rect.w, button->rect.h, cgs.media.limboTeamButtonBack_off ); + + trap_R_SetColor( NULL ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboTeamButtonBack_off ); + + if ( CG_LimboPanel_GetTeam() == teamOrder[button->data[0]] ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboTeamButtonBack_on ); + } else if ( BG_CursorInRect( &button->rect ) ) { + trap_R_SetColor( clr2 ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboTeamButtonBack_on ); + trap_R_SetColor( NULL ); + } + + switch ( button->data[0] ) { + case 0: + shader = cgs.media.limboTeamButtonAllies; + break; + case 1: + shader = cgs.media.limboTeamButtonAxis; + break; + case 2: + shader = cgs.media.limboTeamButtonSpec; + break; + default: + return; + } + + trap_R_SetColor( NULL ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, shader ); +} + +qboolean CG_LimboPanel_ClassButton_KeyDown( panel_button_t* button, int key ) { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qfalse; + } + + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + if ( cgs.ccSelectedClass != button->data[1] ) { + cgs.ccSelectedClass = button->data[1]; + + CG_LimboPanel_SetSelectedWeaponNumForSlot( 0, 0 ); + + CG_LimboPanel_RequestWeaponStats(); + + CG_LimboPanel_SendSetupMsg( qfalse ); + } + + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_ClassBar_Draw( panel_button_t* button ) { + const char* text = NULL; + char buffer[64]; + float w; + + if ( BG_CursorInRect( &medalPic0.rect ) ) { + text = skillNames[0]; + } else if ( BG_CursorInRect( &medalPic1.rect ) ) { + text = skillNames[1]; + } else if ( BG_CursorInRect( &medalPic2.rect ) ) { + text = skillNames[2]; + } else if ( BG_CursorInRect( &medalPic3.rect ) ) { + text = skillNames[3]; + } else if ( BG_CursorInRect( &medalPic4.rect ) ) { + text = skillNames[4]; + } else if ( BG_CursorInRect( &medalPic5.rect ) ) { + text = skillNames[5]; + } else if ( BG_CursorInRect( &medalPic6.rect ) ) { + text = skillNames[6]; + } else if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + text = "JOIN A TEAM"; + } else if ( BG_CursorInRect( &classButton0.rect ) ) { + text = BG_ClassnameForNumber( 0 ); + } else if ( BG_CursorInRect( &classButton1.rect ) ) { + text = BG_ClassnameForNumber( 1 ); + } else if ( BG_CursorInRect( &classButton2.rect ) ) { + text = BG_ClassnameForNumber( 2 ); + } else if ( BG_CursorInRect( &classButton3.rect ) ) { + text = BG_ClassnameForNumber( 3 ); + } else if ( BG_CursorInRect( &classButton4.rect ) ) { + text = BG_ClassnameForNumber( 4 ); + } + + if ( !text ) { + text = BG_ClassnameForNumber( CG_LimboPanel_GetClass() ); + } + + Q_strncpyz( buffer, text, sizeof( buffer ) ); + Q_strupr( buffer ); + + w = CG_Text_Width_Ext( buffer, button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x + ( button->rect.w - w ) * 0.5f, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, buffer, 0, 0, button->font->style, button->font->font ); +} + +void CG_LimboPanel_RenderClassButton( panel_button_t* button ) { + vec4_t clr = { 1.f, 1.f, 1.f, 0.4f }; + vec4_t clr2 = { 1.f, 1.f, 1.f, 0.75f }; + vec4_t clr3 = { 1.f, 1.f, 1.f, 0.6f }; + + int i; + float s0, t0, s1, t1; + float x, y, w, h; + + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboClassButton2Back_off ); + + if ( CG_LimboPanel_GetTeam() != TEAM_SPECTATOR ) { + if ( button->data[1] == CG_LimboPanel_GetClass() ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboClassButton2Back_on ); + } else if ( BG_CursorInRect( &button->rect ) ) { + trap_R_SetColor( clr ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboClassButton2Back_on ); + trap_R_SetColor( NULL ); + } + } + + for ( i = 0; i < 4; i++ ) { + if ( cgs.clientinfo[cg.clientNum].skill[BG_ClassSkillForClass( button->data[1] )] <= i ) { + break; + } + + if ( i == 0 || i == 1 ) { + s0 = 0.5f; + s1 = 1.0f; + } else { + s0 = 0.0f; + s1 = 0.5f; + } + if ( i == 1 || i == 2 ) { + t0 = 0.5f; + t1 = 1.0f; + } else { + t0 = 0.0f; + t1 = 0.5f; + } + + x = button->rect.x + button->rect.w * s0; + y = button->rect.y + button->rect.h * t0; + w = button->rect.w * 0.5f; + h = button->rect.h * 0.5f; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, cgs.media.limboClassButton2Wedge_off ); + } else { + if ( button->data[1] == CG_LimboPanel_GetClass() ) { + trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, cgs.media.limboClassButton2Wedge_on ); + } else if ( BG_CursorInRect( &button->rect ) ) { + trap_R_SetColor( clr3 ); + trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, cgs.media.limboClassButton2Wedge_on ); + trap_R_SetColor( NULL ); + } else { + trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, cgs.media.limboClassButton2Wedge_off ); + } + } + } + + + if ( CG_LimboPanel_GetTeam() != TEAM_SPECTATOR && button->data[1] == CG_LimboPanel_GetClass() ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboClassButtons2[button->data[1]] ); + } else { + trap_R_SetColor( clr2 ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboClassButtons2[button->data[1]] ); + trap_R_SetColor( NULL ); + } +} + +int CG_LimboPanel_GetMaxObjectives( void ) { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return 0; + } + + return atoi( Info_ValueForKey( CG_ConfigString( CS_MULTI_INFO ), "numobjectives" ) ); +} + +qboolean CG_LimboPanel_ObjectiveText_KeyDown( panel_button_t* button, int key ) { + int max; + + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + max = CG_LimboPanel_GetMaxObjectives(); + + if ( button->data[7] == 0 ) { + if ( ++cgs.ccSelectedObjective > max ) { + cgs.ccSelectedObjective = 0; + } + } else { + if ( --cgs.ccSelectedObjective < 0 ) { + cgs.ccSelectedObjective = max; + } + } + + CG_LimboPanel_RequestObjective(); + + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_RenderObjectiveText( panel_button_t* button ) { + const char* cs; + char *info, *s, *p; + float y; + char buffer[1024]; + int status = 0; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + //cs = CG_ConfigString( CS_MULTI_MAPDESC ); + //Q_strncpyz( buffer, cs, sizeof(buffer) ); + + Q_strncpyz( buffer, cg.objMapDescription_Neutral, sizeof( buffer ) ); + } else { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + //cs = CG_ConfigString( CS_MULTI_MAPDESC3 ); + //Q_strncpyz( buffer, cs, sizeof(buffer) ); + + Q_strncpyz( buffer, cg.objMapDescription_Neutral, sizeof( buffer ) ); + } else { + if ( cgs.ccSelectedObjective != CG_LimboPanel_GetMaxObjectives() ) { + cs = CG_ConfigString( CS_MULTI_OBJECTIVE ); + + if ( CG_LimboPanel_GetTeam() == TEAM_AXIS ) { + //info = Info_ValueForKey( cs, "axis_desc" ); + info = cg.objDescription_Axis[cgs.ccSelectedObjective]; + status = atoi( Info_ValueForKey( cs, va( "x%i", cgs.ccSelectedObjective + 1 ) ) ); + } else { + //info = Info_ValueForKey( cs, "allied_desc" ); + info = cg.objDescription_Allied[cgs.ccSelectedObjective]; + status = atoi( Info_ValueForKey( cs, va( "a%i", cgs.ccSelectedObjective + 1 ) ) ); + } + + if ( !( info && *info ) ) { + info = "No Information Supplied"; + } + + Q_strncpyz( buffer, info, sizeof( buffer ) ); + } else { + //cs = CG_ConfigString( CG_LimboPanel_GetTeam() == TEAM_AXIS ? CS_MULTI_MAPDESC2 : CS_MULTI_MAPDESC ); + //Q_strncpyz( buffer, cs, sizeof(buffer) ); + + if ( CG_LimboPanel_GetTeam() == TEAM_AXIS ) { + Q_strncpyz( buffer, cg.objMapDescription_Axis, sizeof( buffer ) ); + } else { + Q_strncpyz( buffer, cg.objMapDescription_Allied, sizeof( buffer ) ); + } + } + } + } + + while ( ( s = strchr( buffer, '*' ) ) ) { + *s = '\n'; + } + + CG_FitTextToWidth_Ext( buffer, button->font->scalex, button->rect.w - 16, sizeof( buffer ), &cgs.media.limboFont2 ); + + y = button->rect.y + 12; + + s = p = buffer; + while ( *p ) { + if ( *p == '\n' ) { + *p++ = '\0'; + CG_Text_Paint_Ext( button->rect.x + 4, y, button->font->scalex, button->font->scaley, button->font->colour, s, 0, 0, 0, &cgs.media.limboFont2 ); + y += 8; + s = p; + } else { + p++; + } + } + + if ( cg_gameType.integer != GT_WOLF_LMS && CG_LimboPanel_GetTeam() != TEAM_SPECTATOR ) { + const char* ofTxt; + float w, x; + + if ( cgs.ccSelectedObjective == CG_LimboPanel_GetMaxObjectives() ) { + ofTxt = va( "1of%i", CG_LimboPanel_GetMaxObjectives() + 1 ); + } else { + ofTxt = va( "%iof%i", cgs.ccSelectedObjective + 2, CG_LimboPanel_GetMaxObjectives() + 1 ); + } + + w = CG_Text_Width_Ext( ofTxt, 0.2f, 0, &cgs.media.limboFont2 ); + + x = button->rect.x + button->rect.w - w - 4; + + CG_Text_Paint_Ext( x, button->rect.y + button->rect.h - 2, 0.2f, 0.2f, colorBlack, ofTxt, 0, 0, 0, &cgs.media.limboFont2 ); + } + + if ( status == 1 ) { + CG_DrawPic( button->rect.x + 87, button->rect.y + 8, button->rect.w - 174, button->rect.h - 8, cgs.media.ccStamps[0] ); + } else if ( status == 2 ) { + CG_DrawPic( button->rect.x + 87, button->rect.y + 8, button->rect.w - 174, button->rect.h - 8, cgs.media.ccStamps[1] ); + } +} + +void CG_LimboPanel_RenderObjectiveBack( panel_button_t* button ) { +// int max = CG_LimboPanel_GetMaxObjectives(); + +// if( cgs.ccSelectedObjective == max ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboObjectiveBack[ TEAM_SPECTATOR - TEAM_AXIS ] ); +// } else { +// CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboObjectiveBack[ CG_LimboPanel_GetTeam() - TEAM_AXIS ] ); +// } +} + +void CG_LimboPanel_RenderCommandMap( panel_button_t* button ) { + CG_DrawMap( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.ccFilter, NULL, qtrue, 1.f, qtrue ); + CG_CommandMap_DrawHighlightText(); +} + +qboolean CG_LimboPanel_RenderLight_GetValue( panel_button_t* button ) { + switch ( button->data[0] ) { + case 0: + return ( CG_LimboPanel_GetClass() == button->data[1] ) ? qtrue : qfalse; + case 1: + return ( CG_LimboPanel_GetTeam() == teamOrder[button->data[1]] ) ? qtrue : qfalse; + } + + return qfalse; +} + +void CG_LimboPanel_RenderLight( panel_button_t* button ) { + if ( CG_LimboPanel_RenderLight_GetValue( button ) ) { +// if( !button->data[2] || (button->data[2] - cg.time < 0) ) { + button->data[3] = button->data[3] ^ 1; +// if( button->data[3] ) { +// button->data[2] = cg.time + rand() % 200; +// } else { +// button->data[2] = cg.time + rand() % 1000; +// } +// } + + CG_DrawPic( button->rect.x - 4, button->rect.y - 2, button->rect.w + 4, button->rect.h + 4, button->data[3] ? cgs.media.limboLight_on2 : cgs.media.limboLight_on ); + } else { + CG_DrawPic( button->rect.x - 4, button->rect.y - 2, button->rect.w + 4, button->rect.h + 4, cgs.media.limboLight_off ); + } +} + +void CG_DrawPlayerHead( rectDef_t *rect, bg_character_t* character, bg_character_t* headcharacter, float yaw, float pitch, qboolean drawHat, hudHeadAnimNumber_t animation, qhandle_t painSkin, int rank, qboolean spectator ) { + float len; + vec3_t origin; + vec3_t mins, maxs, angles; + float x, y, w, h; + refdef_t refdef; + refEntity_t head, hat, mrank; + + if ( !character ) { + return; + } + + trap_R_SaveViewParms(); + + x = rect->x; + y = rect->y; + w = rect->w; + h = rect->h; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + memset( &refdef, 0, sizeof( refdef ) ); + + refdef.rdflags = RDF_NOWORLDMODEL; + AxisClear( refdef.viewaxis ); + + refdef.fov_x = 8; + refdef.fov_y = 10; + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + + refdef.time = cg.time; + + trap_R_ClearScene(); + + // offset the origin y and z to center the head + trap_R_ModelBounds( character->hudhead, mins, maxs ); + + origin[2] = -0.7 * ( mins[2] + maxs[2] ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + + // calculate distance so the head nearly fills the box + // assume heads are taller than wide + len = 3.5f * ( maxs[2] - mins[2] ); + origin[0] = len / tan( 20 / 2 ); // 0.268; // len / tan( fov/2 ) + + angles[PITCH] = pitch; + angles[YAW] = yaw; + angles[ROLL] = 0; + + memset( &head, 0, sizeof( head ) ); + AnglesToAxis( angles, head.axis ); + VectorCopy( origin, head.origin ); + head.hModel = headcharacter->hudhead; + head.customSkin = headcharacter->hudheadskin; + head.renderfx = RF_NOSHADOW | RF_FORCENOLOD; // no stencil shadows + + // ydnar: light the model with the current lightgrid + //VectorCopy( cg.refdef.vieworg, head.lightingOrigin ); + if ( !cg.showGameView ) { + head.renderfx |= /*RF_LIGHTING_ORIGIN |*/ RF_MINLIGHT; + } + + CG_HudHeadAnimation( headcharacter, &cg.predictedPlayerEntity.pe.hudhead, &head.oldframe, &head.frame, &head.backlerp, animation ); + + if ( drawHat ) { + memset( &hat, 0, sizeof( hat ) ); + hat.hModel = character->accModels[ACC_HAT]; + hat.customSkin = character->accSkins[ACC_HAT]; + hat.renderfx = RF_NOSHADOW | RF_FORCENOLOD; // no stencil shadows + + // ydnar: light the model with the current lightgrid + //VectorCopy( cg.refdef.vieworg, hat.lightingOrigin ); + if ( !cg.showGameView ) { + hat.renderfx |= /*RF_LIGHTING_ORIGIN |*/ RF_MINLIGHT; + } + + CG_PositionEntityOnTag( &hat, &head, "tag_mouth", 0, NULL ); + + if ( rank ) { + memset( &mrank, 0, sizeof( mrank ) ); + + mrank.hModel = character->accModels[ ACC_RANK ]; + mrank.customShader = rankicons[ rank ][ 1 ].shader; + mrank.renderfx = RF_NOSHADOW | RF_FORCENOLOD; // no stencil shadows + + CG_PositionEntityOnTag( &mrank, &head, "tag_mouth", 0, NULL ); + } + } + + head.shaderRGBA[0] = 255; + head.shaderRGBA[1] = 255; + head.shaderRGBA[2] = 255; + head.shaderRGBA[3] = 255; + + hat.shaderRGBA[0] = 255; + hat.shaderRGBA[1] = 255; + hat.shaderRGBA[2] = 255; + hat.shaderRGBA[3] = 255; + + mrank.shaderRGBA[0] = 255; + mrank.shaderRGBA[1] = 255; + mrank.shaderRGBA[2] = 255; + mrank.shaderRGBA[3] = 255; + + /*if( spectator ) { + head.customShader = cgs.media.limboSpectator; + head.customSkin = 0; + }*/ + trap_R_AddRefEntityToScene( &head ); + + if ( painSkin ) { + head.customShader = 0; + head.customSkin = painSkin; + trap_R_AddRefEntityToScene( &head ); + } + + if ( drawHat ) { + /*if( spectator ) { + hat.customShader = cgs.media.limboSpectator; + hat.customSkin = 0; + }*/ + trap_R_AddRefEntityToScene( &hat ); + + if ( rank ) { + trap_R_AddRefEntityToScene( &mrank ); + } + } + trap_R_RenderScene( &refdef ); + +//bani - render to texture api example +//draws the player head on one of the fueldump textures. +#ifdef TEST_API_RENDERTOTEXTURE + { + static int texid = 0; + + if ( !texid ) { + texid = trap_R_GetTextureId( "textures/stone/mxsnow3.tga" ); + } + trap_R_RenderToTexture( texid, 0, 0, 256, 256 ); + } +#endif + + trap_R_RestoreViewParms(); +} + +void CG_LimboPanel_RenderHead( panel_button_t* button ) { + vec4_t clrBack = { 0.05f, 0.05f, 0.05f, 1.f }; + + if ( CG_LimboPanel_GetTeam() != TEAM_SPECTATOR ) { + CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, clrBack ); + CG_DrawPlayerHead( &button->rect, CG_LimboPanel_GetCharacter(), CG_LimboPanel_GetCharacter(), 180, 0, qtrue, HD_IDLE4, 0, cgs.clientinfo[ cg.clientNum ].rank, qfalse ); + } else { + //CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorBlack ); + //CG_DrawPlayerHead( &button->rect, BG_GetCharacter( TEAM_ALLIES, PC_SOLDIER ), BG_GetCharacter( TEAM_ALLIES, PC_SOLDIER ), 180, 0, qtrue, HD_IDLE4, 0, 0, qtrue ); + + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboSpectator ); + } + + VectorSet( clrBack, .6f, .6f, .6f ); + trap_R_SetColor( clrBack ); + + // top / bottom + CG_DrawPic( button->rect.x, button->rect.y - 2, button->rect.w, 2, cgs.media.limboWeaponCardSurroundH ); + CG_DrawPicST( button->rect.x, button->rect.y + button->rect.h, button->rect.w, 2, 0.f, 1.f, 1.f, 0.f, cgs.media.limboWeaponCardSurroundH ); + + CG_DrawPic( button->rect.x - 2, button->rect.y, 2, button->rect.h, cgs.media.limboWeaponCardSurroundV ); + CG_DrawPicST( button->rect.x + button->rect.w, button->rect.y, 2, button->rect.h, 1.f, 0.f, 0.f, 1.f, cgs.media.limboWeaponCardSurroundV ); + + CG_DrawPicST( button->rect.x - 2, button->rect.y - 2, 2, 2, 0.f, 0.f, 1.f, 1.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( button->rect.x + button->rect.w, button->rect.y - 2, 2, 2, 1.f, 0.f, 0.f, 1.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( button->rect.x + button->rect.w, button->rect.y + button->rect.h, 2, 2, 1.f, 1.f, 0.f, 0.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( button->rect.x - 2, button->rect.y + button->rect.h, 2, 2, 0.f, 1.f, 1.f, 0.f, cgs.media.limboWeaponCardSurroundC ); + + trap_R_SetColor( NULL ); +} + +qboolean CG_LimboPanel_Filter_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + SOUND_FILTER; + + cgs.ccFilter ^= ( 1 << button->data[0] ); + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_Filter_Draw( panel_button_t* button ) { + if ( cgs.ccFilter & ( 1 << button->data[0] ) ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.ccFilterBackOff ); + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.ccFilterBackOn ); + } + +// CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.ccFilterPics[button->data[0]] ); + CG_DrawPic( button->rect.x + 1, button->rect.y + 1, button->rect.w - 2, button->rect.h - 2, cgs.media.ccFilterPics[button->data[0]] ); +} + + +void CG_LimboPanel_RenderSkillIcon( panel_button_t* button ) { + qhandle_t shader; + if ( cg_gameType.integer == GT_WOLF_LMS /*|| CG_LimboPanel_GetTeam() == TEAM_SPECTATOR*/ ) { + return; + } + + switch ( button->data[0] ) { + case 0: + shader = cgs.media.limboSkillsBS; + break; + case 1: + shader = cgs.media.limboSkillsLW; + break; + case 2: + shader = cgs.media.limboClassButtons[CG_LimboPanel_GetClass()]; + break; + default: + return; + } + + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, shader ); +} + + +qboolean CG_LimboPanel_WeaponLights_KeyDown( panel_button_t* button, int key ) { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qfalse; + } + + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + cgs.ccSelectedWeaponNumber = button->data[0]; + CG_LimboPanel_RequestWeaponStats(); + return qtrue; + } + + return qfalse; +} + +void CG_LimboPanel_WeaponLights( panel_button_t* button ) { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboWeaponNumber_off ); + } else { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, button->data[0] == cgs.ccSelectedWeaponNumber ? cgs.media.limboWeaponNumber_on : cgs.media.limboWeaponNumber_off ); + } +} + +qboolean CG_LimboPanel_WeaponPanel_KeyDown( panel_button_t* button, int key ) { + button->data[7] = 0; + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qfalse; + } + + if ( key == K_MOUSE1 ) { + SOUND_SELECT; + + BG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + + return qfalse; +} + +qboolean CG_LimboPanel_WeaponPanel_KeyUp( panel_button_t* button, int key ) { + int cnt, i; + rectDef_t rect; + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qfalse; + } + + if ( key == K_MOUSE1 ) { + if ( BG_PanelButtons_GetFocusButton() == button ) { + + memcpy( &rect, &button->rect, sizeof( rect ) ); + rect.y -= rect.h; + + cnt = CG_LimboPanel_WeaponCount(); + for ( i = 1; i < cnt; i++, rect.y -= rect.h ) { + + if ( !BG_CursorInRect( &rect ) ) { + continue; + } + + if ( !CG_LimboPanel_GetSelectedWeaponNum() ) { + CG_LimboPanel_SetSelectedWeaponNum( i ); + CG_LimboPanel_SendSetupMsg( qfalse ); + } else { + if ( i <= CG_LimboPanel_GetSelectedWeaponNum() ) { + CG_LimboPanel_SetSelectedWeaponNum( i - 1 ); + CG_LimboPanel_SendSetupMsg( qfalse ); + } else { + CG_LimboPanel_SetSelectedWeaponNum( i ); + CG_LimboPanel_SendSetupMsg( qfalse ); + } + } + } + + BG_PanelButtons_SetFocusButton( NULL ); + + return qtrue; + } + } + + return qfalse; +} + +void CG_LimboPanel_WeaponPanel_DrawWeapon( rectDef_t* rect, weapon_t weap, qboolean highlight, const char* ofTxt, qboolean disabled ) { + weaponType_t* wt = WM_FindWeaponTypeForWeapon( weap ); + qhandle_t shader = cgs.media.limboWeaponCard; + int width = CG_Text_Width_Ext( ofTxt, 0.2f, 0, &cgs.media.limboFont2 ); + float x = rect->x + rect->w - width - 4; + vec4_t clr; + + if ( !wt ) { + return; + } + + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader ); +// CG_DrawRect( rect->x, rect->y, rect->w, rect->h, 1, colorWhite ); + if ( wt->desc ) { + if ( highlight && BG_CursorInRect( rect ) ) { + Vector4Copy( weaponPanelNameFont.colour, clr ); + clr[3] *= 1.5; + CG_Text_Paint_Ext( rect->x + 4, rect->y + 12, weaponPanelNameFont.scalex, weaponPanelNameFont.scaley, clr, wt->desc, 0, 0, weaponPanelNameFont.style, weaponPanelNameFont.font ); + } else { + CG_Text_Paint_Ext( rect->x + 4, rect->y + 12, weaponPanelNameFont.scalex, weaponPanelNameFont.scaley, weaponPanelNameFont.colour, wt->desc, 0, 0, weaponPanelNameFont.style, weaponPanelNameFont.font ); + } + } + + { + float x2, y2, w, h, s0, s1, t0, t1; + + trap_R_SetColor( NULL ); + + x2 = rect->x; + y2 = rect->y + ( rect->h * 0.25f ); + + CG_LimboPanel_GetWeaponCardIconData( weap, &shader, &w, &h, &s0, &t0, &s1, &t1 ); + + w *= rect->w; + h *= rect->h * 0.75f; + + CG_DrawPicST( x2, y2, w, h, s0, t0, s1, t1, shader ); + + if ( disabled ) { + vec4_t clr = { 1.f, 1.f, 1.f, 0.6f }; + + trap_R_SetColor( clr ); + CG_DrawPic( x2, y2 + 4 + ( h - 16 ) * 0.5f, w, 16, cgs.media.limboWeaponCardOOS ); + trap_R_SetColor( NULL ); + } + } + CG_Text_Paint_Ext( x, rect->y + rect->h - 2, 0.2f, 0.2f, colorBlack, ofTxt, 0, 0, 0, &cgs.media.limboFont2 ); +} + +#define BRDRSIZE 4 +void CG_DrawBorder( float x, float y, float w, float h, qboolean fill, qboolean drawMouseOver ) { + vec4_t clrBack = { 0.1f, 0.1f, 0.1f, 1.f }; + vec4_t clrBack2 = { 0.2f, 0.2f, 0.2f, 1.f }; + + // top / bottom + CG_DrawPic( x, y - BRDRSIZE, w, BRDRSIZE, cgs.media.limboWeaponCardSurroundH ); + CG_DrawPicST( x, y + h, w, BRDRSIZE, 0.f, 1.f, 1.f, 0.f, cgs.media.limboWeaponCardSurroundH ); + + CG_DrawPic( x - BRDRSIZE, y, BRDRSIZE, h, cgs.media.limboWeaponCardSurroundV ); + CG_DrawPicST( x + w, y, BRDRSIZE, h, 1.f, 0.f, 0.f, 1.f, cgs.media.limboWeaponCardSurroundV ); + + CG_DrawPicST( x - BRDRSIZE, y - BRDRSIZE, BRDRSIZE, BRDRSIZE, 0.f, 0.f, 1.f, 1.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( x + w, y - BRDRSIZE, BRDRSIZE, BRDRSIZE, 1.f, 0.f, 0.f, 1.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( x + w, y + h, BRDRSIZE, BRDRSIZE, 1.f, 1.f, 0.f, 0.f, cgs.media.limboWeaponCardSurroundC ); + CG_DrawPicST( x - BRDRSIZE, y + h, BRDRSIZE, BRDRSIZE, 0.f, 1.f, 1.f, 0.f, cgs.media.limboWeaponCardSurroundC ); + + if ( fill ) { + if ( drawMouseOver ) { + rectDef_t rect; + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + if ( BG_CursorInRect( &rect ) ) { + CG_FillRect( x, y, w, h, clrBack2 ); + } else { + CG_FillRect( x, y, w, h, clrBack ); + } + } else { + CG_FillRect( x, y, w, h, clrBack ); + } + } +} + +void CG_LimboPanel_Border_Draw( panel_button_t* button ) { + CG_DrawBorder( button->rect.x, button->rect.y, button->rect.w, button->rect.h, qtrue, qtrue ); +} + + +void CG_LimboPanel_WeaponPanel( panel_button_t* button ) { + weapon_t weap = CG_LimboPanel_GetSelectedWeapon(); + int cnt = CG_LimboPanel_WeaponCount(); + + if ( cgs.ccSelectedWeapon2 >= CG_LimboPanel_WeaponCount_ForSlot( 0 ) ) { + cgs.ccSelectedWeapon2 = CG_LimboPanel_WeaponCount_ForSlot( 0 ) - 1; + } + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + vec4_t clr = { 0.f, 0.f, 0.f, 0.4f }; + + + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboWeaponCard ); + + trap_R_SetColor( clr ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboWeaponBlendThingy ); + trap_R_SetColor( NULL ); + + CG_Text_Paint_Ext( button->rect.x + 4, button->rect.y + 12, weaponPanelNameFont.scalex, weaponPanelNameFont.scaley, weaponPanelNameFont.colour, "SPECTATOR", 0, 0, weaponPanelNameFont.style, weaponPanelNameFont.font ); + + return; + } + + if ( BG_PanelButtons_GetFocusButton() == button && cnt > 1 ) { + int i, x; + rectDef_t rect; + memcpy( &rect, &button->rect, sizeof( rect ) ); + + CG_LimboPanel_WeaponPanel_DrawWeapon( &rect, weap, qtrue, va( "%iof%i", CG_LimboPanel_GetSelectedWeaponNum() + 1, cnt ), CG_LimboPanel_RealWeaponIsDisabled( weap ) ); + if ( BG_CursorInRect( &rect ) ) { + if ( button->data[7] != 0 ) { + SOUND_FOCUS; + + button->data[7] = 0; + } + } + rect.y -= rect.h; + + // render in expanded mode ^ + for ( i = 0, x = 1; i < cnt; i++ ) { + weapon_t cycleWeap = CG_LimboPanel_GetWeaponForNumber( i, cgs.ccSelectedWeaponNumber, qtrue ); + if ( cycleWeap != weap ) { + CG_LimboPanel_WeaponPanel_DrawWeapon( &rect, cycleWeap, qtrue, va( "%iof%i", i + 1, cnt ), CG_LimboPanel_RealWeaponIsDisabled( cycleWeap ) ); + + if ( BG_CursorInRect( &rect ) ) { + if ( button->data[7] != x ) { + SOUND_FOCUS; + + button->data[7] = x; + } + } + + rect.y -= rect.h; + x++; + } + } + + CG_DrawBorder( button->rect.x, button->rect.y - ( ( cnt - 1 ) * button->rect.h ), button->rect.w, button->rect.h * cnt, qfalse, qfalse ); + } else { + vec4_t clr = { 0.f, 0.f, 0.f, 0.4f }; + vec4_t clr2 = { 1.f, 1.f, 1.f, 0.4f }; + + // render in normal mode + CG_LimboPanel_WeaponPanel_DrawWeapon( &button->rect, weap, cnt > 1 ? qtrue : qfalse, va( "%iof%i", CG_LimboPanel_GetSelectedWeaponNum() + 1, cnt ), CG_LimboPanel_RealWeaponIsDisabled( weap ) ); + + if ( cnt <= 1 || !BG_CursorInRect( &button->rect ) ) { + trap_R_SetColor( clr2 ); + } + CG_DrawPic( button->rect.x + button->rect.w - 20, button->rect.y + 4, 16, 12, cgs.media.limboWeaponCardArrow ); + + + trap_R_SetColor( clr ); + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.limboWeaponBlendThingy ); + trap_R_SetColor( NULL ); + } +} + +void CG_LimboPanel_RenderCounterNumber( float x, float y, float w, float h, float number, qhandle_t shaderBack, qhandle_t shaderRoll, int numbuttons ) { + float numberS = ( ( ( numbuttons - 1 ) - number ) + 0 ) * ( 1.f / numbuttons ); + float numberE = ( ( ( numbuttons - 1 ) - number ) + 1 ) * ( 1.f / numbuttons ); + + CG_AdjustFrom640( &x, &y, &w, &h ); + trap_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, shaderBack ); + trap_R_DrawStretchPic( x, y, w, h, 0, numberS, 1, numberE, shaderRoll ); +} + +int CG_LimboPanel_RenderCounter_ValueForButton( panel_button_t* button ) { + int i, count = 0; + + switch ( button->data[0] ) { + case 0: // class counts + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR || CG_LimboPanel_GetRealTeam() != CG_LimboPanel_GetTeam() ) { + return 0; // dont give class counts unless we are on that team (or spec) + } + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + if ( cgs.clientinfo[i].team != CG_LimboPanel_GetTeam() || cgs.clientinfo[i].cls != button->data[1] ) { + continue; + } + + count++; + } + return count; + case 1: // team counts + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != teamOrder[button->data[1]] ) { + continue; + } + + count++; + } + return count; + case 2: // xp + return cg.xp; + case 3: // respawn time + return CG_CalculateReinfTime_Float( qtrue ); + case 4: // skills + switch ( button->data[1] ) { + case 0: + count = cgs.clientinfo[cg.clientNum].skill[SK_BATTLE_SENSE]; + break; + case 1: + count = cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS]; + break; + case 2: + count = cgs.clientinfo[cg.clientNum].skill[BG_ClassSkillForClass( CG_LimboPanel_GetClass() )]; + break; + } + + return ( 1 << count ) - 1; + case 5: // clock + if ( !cgs.timelimit ) { + return 0; + } + count = ( ( cgs.timelimit * 60 * 1000 ) - ( cg.time - cgs.levelStartTime ) ) / 1000; + switch ( button->data[1] ) { + case 0: // secs + return count % 60; + case 1: // mins + return count / 60; + } + return 0; + case 6: // stats + switch ( button->data[1] ) { + case 0: + return cgs.ccWeaponShots; + case 1: + return cgs.ccWeaponHits; + case 2: + return cgs.ccWeaponShots != 0 ? 100 * cgs.ccWeaponHits / cgs.ccWeaponShots : 0; + } + } + + return 0; +} + +int CG_LimboPanel_RenderCounter_RollTimeForButton( panel_button_t* button ) { + float diff; + switch ( button->data[0] ) { + case 0: // class counts + case 1: // team counts + return 100.f; + + case 4: // skills + return 1000.f; + + case 6: // stats + diff = Q_fabs( button->data[3] - CG_LimboPanel_RenderCounter_ValueForButton( button ) ); + if ( diff < 5 ) { + return 200.f / diff; + } else { + return 50.f; + } + + case 5: // clock + case 3: // respawn time + case 2: // xp + return 50.f; + } + + return 1000.f; +} + +int CG_LimboPanel_RenderCounter_MaxChangeForButton( panel_button_t* button ) { + switch ( button->data[0] ) { + case 2: // xp + case 6: // stats + return 5; + } + + return 1; +} +int CG_LimboPanel_RenderCounter_NumRollers( panel_button_t* button ) { + switch ( button->data[0] ) { + case 0: // class counts + case 1: // team counts + case 5: // clock + case 3: // respawn time + return 2; + + case 4: // skills + if ( cg_gameType.integer == GT_WOLF_LMS /*|| CG_LimboPanel_GetTeam() == TEAM_SPECTATOR*/ ) { + return 0; + } + return 4; + + case 6: // stats + switch ( button->data[1] ) { + case 0: + case 1: + return 4; + case 2: + return 3; + } + + case 2: // xp + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return 0; + } + return 6; + } + + return 0; +} + +qboolean CG_LimboPanel_RenderCounter_CountsDown( panel_button_t* button ) { + switch ( button->data[0] ) { + case 4: // skill + case 2: // xp + return qfalse; + + default: + break; + } + + return qtrue; +} + +qboolean CG_LimboPanel_RenderCounter_CountsUp( panel_button_t* button ) { + switch ( button->data[0] ) { + case 4: // skill + case 3: // respawn time + case 5: // clock + return qfalse; + + default: + break; + } + + return qtrue; +} + +qboolean CG_LimboPanel_RenderCounter_StartSet( panel_button_t* button ) { + switch ( button->data[0] ) { + case 3: // respawn time + case 5: // clock + return qtrue; + + default: + break; + } + + return qfalse; +} + + + +void CG_LimboPanel_RenderMedal( panel_button_t* button ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, cgs.media.medal_back ); + if ( cgs.clientinfo[cg.clientNum].medals[button->data[0]] ) { + CG_DrawPic( button->rect.x - 2, button->rect.y, button->rect.w + 4, button->rect.h, cgs.media.medals[button->data[0]] ); + } +} + +qboolean CG_LimboPanel_RenderCounter_IsReversed( panel_button_t* button ) { + switch ( button->data[0] ) { + case 4: // skill + return qtrue; + + default: + break; + } + + return qfalse; +} + +void CG_LimboPanel_RenderCounter_GetShaders( panel_button_t* button, qhandle_t* shaderBack, qhandle_t* shaderRoll, int* numimages ) { + switch ( button->data[0] ) { + case 4: // skills + *shaderBack = cgs.media.limboStar_back; + *shaderRoll = cgs.media.limboStar_roll; + *numimages = 2; + return; + default: + *shaderBack = cgs.media.limboNumber_back; + *shaderRoll = cgs.media.limboNumber_roll; + *numimages = 10; + return; + } +} + +void CG_LimboPanelRenderText_NoLMS( panel_button_t* button ) { + if ( cg_gameType.integer == GT_WOLF_LMS ) { + return; + } + + BG_PanelButtonsRender_Text( button ); +} + +void CG_LimboPanelRenderText_SkillsText( panel_button_t* button ) { + if ( cg_gameType.integer == GT_WOLF_LMS /*|| CG_LimboPanel_GetTeam() == TEAM_SPECTATOR*/ ) { + return; + } + + BG_PanelButtonsRender_Text( button ); +} + +#define MAX_ROLLERS 8 +#define COUNTER_ROLLTOTAL ( cg.time - button->data[4] ) +// Gordon: this function is mental, i love it :) +void CG_LimboPanel_RenderCounter( panel_button_t* button ) { + float x, w; + float count[MAX_ROLLERS]; + int i, j; + qhandle_t shaderBack; + qhandle_t shaderRoll; + int numimages; + + float counter_rolltime = CG_LimboPanel_RenderCounter_RollTimeForButton( button ); + int num = CG_LimboPanel_RenderCounter_NumRollers( button ); + int value = CG_LimboPanel_RenderCounter_ValueForButton( button ); + if ( num > MAX_ROLLERS ) { + num = MAX_ROLLERS; + } + + CG_LimboPanel_RenderCounter_GetShaders( button, &shaderBack, &shaderRoll, &numimages ); + + if ( COUNTER_ROLLTOTAL < counter_rolltime ) { + // we're rolling + float frac = ( COUNTER_ROLLTOTAL / counter_rolltime ); + + for ( i = 0, j = 1; i < num; i++, j *= numimages ) { + int valueOld = ( button->data[3] / j ) % numimages; + int valueNew = ( button->data[5] / j ) % numimages; + + if ( valueNew == valueOld ) { + count[i] = valueOld; + } else if ( ( valueNew > valueOld ) != ( button->data[5] > button->data[3] ) ) { + // we're flipping around so.... + if ( button->data[5] > button->data[3] ) { + count[i] = valueOld + frac; + } else { + count[i] = valueOld - frac; + } + } else { + // normal flip + count[i] = valueOld + ( ( valueNew - valueOld ) * frac ); + } + } + } else { + if ( button->data[3] != button->data[5] ) { + button->data[3] = button->data[5]; + } else if ( value != button->data[3] ) { + int maxchange = abs( value - button->data[3] ); + if ( maxchange > CG_LimboPanel_RenderCounter_MaxChangeForButton( button ) ) { + maxchange = CG_LimboPanel_RenderCounter_MaxChangeForButton( button ); + } + + if ( value > button->data[3] ) { + if ( CG_LimboPanel_RenderCounter_CountsUp( button ) ) { + button->data[5] = button->data[3] + maxchange; + } else { +// button->data[3] = + button->data[5] = value; + } + } else { + if ( CG_LimboPanel_RenderCounter_CountsDown( button ) ) { + button->data[5] = button->data[3] - maxchange; + } else { +// button->data[3] = + button->data[5] = value; + } + } + button->data[4] = cg.time; + } + + for ( i = 0, j = 1; i < num; i++, j *= numimages ) { + count[i] = (int)( button->data[3] / j ); + } + } + + x = button->rect.x; + w = button->rect.w / (float)num; + + if ( CG_LimboPanel_RenderCounter_IsReversed( button ) ) { + for ( i = 0; i < num; i++ ) { + CG_LimboPanel_RenderCounterNumber( x, button->rect.y, w, button->rect.h, count[i], shaderBack, shaderRoll, numimages ); + + x += w + button->data[6]; + } + } else { + for ( i = num - 1; i >= 0; i-- ) { + CG_LimboPanel_RenderCounterNumber( x, button->rect.y, w, button->rect.h, count[i], shaderBack, shaderRoll, numimages ); + + x += w + button->data[6]; + } + } + + if ( button->data[0] == 0 || button->data[0] == 1 ) { + CG_DrawPic( button->rect.x - 2, button->rect.y - 2, button->rect.w * 1.4f, button->rect.h + 7, cgs.media.limboCounterBorder ); + } +} + +void CG_LimboPanel_Setup( void ) { + panel_button_t* button; + panel_button_t** buttons = limboPanelButtons; + clientInfo_t* ci = &cgs.clientinfo[cg.clientNum]; + bg_playerclass_t *classinfo; + int i; + char buffer[256]; + + cgs.limboLoadoutModified = qfalse; + +// if( !cgs.playedLimboMusic ) { +// trap_S_StartBackgroundTrack( "sound/music/menu_briefing.wav", "", 0 ); +// cgs.playedLimboMusic = qtrue; +// } + + trap_Cvar_VariableStringBuffer( "name", buffer, 256 ); + trap_Cvar_Set( "limboname", buffer ); + + if ( cgs.ccLayers ) { + cgs.ccSelectedLayer = CG_CurLayerForZ( (int)cg.predictedPlayerEntity.lerpOrigin[2] ); + } + + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button->onDraw == CG_LimboPanel_RenderCounter ) { + if ( CG_LimboPanel_RenderCounter_StartSet( button ) ) { + button->data[3] = button->data[5] = CG_LimboPanel_RenderCounter_ValueForButton( button ); + button->data[4] = 0; + } + } + } + + if ( !cgs.limboLoadoutSelected ) { + bg_playerclass_t* classInfo = CG_LimboPanel_GetPlayerClass(); + + for ( i = 0; i < MAX_WEAPS_PER_CLASS; i++ ) { + if ( !classInfo->classWeapons[i] ) { + cgs.ccSelectedWeapon = 0; + break; + } + + if ( classInfo->classWeapons[i] == cgs.clientinfo[cg.clientNum].latchedweapon ) { + cgs.ccSelectedWeapon = i; + break; + } + } + + if ( cgs.ccSelectedWeapon2 >= CG_LimboPanel_WeaponCount_ForSlot( 0 ) ) { + cgs.ccSelectedWeapon2 = CG_LimboPanel_WeaponCount_ForSlot( 0 ) - 1; + } + + for ( i = 0; i < 3; i++ ) { + if ( teamOrder[i] == ci->team ) { + cgs.ccSelectedTeam = i; + } + } + + if ( ci->team != TEAM_SPECTATOR ) { + cgs.ccSelectedClass = ci->cls; + } + } + + CG_LimboPanel_RequestWeaponStats(); + cgs.ccRequestedObjective = cgs.ccSelectedObjective = CG_LimboPanel_GetMaxObjectives(); + CG_LimboPanel_RequestObjective(); + + cgs.ccSelectedObjective = CG_LimboPanel_GetMaxObjectives(); + cgs.ccSelectedWeaponNumber = 1; + + classinfo = CG_LimboPanel_GetPlayerClass(); + if ( CG_LimboPanel_WeaponIsDisabled( cgs.ccSelectedWeapon ) ) { + // set weapon to default if disabled + // NOTE classWeapons[0] must NEVER be disabled + cgs.ccSelectedWeapon = 0; //classinfo->classWeapons[0]; + } +} + + +void CG_LimboPanel_Init( void ) { + BG_PanelButtonsSetup( limboPanelButtons ); +} + +qboolean CG_LimboPanel_Draw( void ) { + static panel_button_t* lastHighlight; + panel_button_t* hilight; +// panel_button_t** buttons = limboPanelButtons; + + hilight = BG_PanelButtonsGetHighlightButton( limboPanelButtons ); + if ( hilight && hilight != lastHighlight ) { + lastHighlight = hilight; +// SOUND_FOCUS; + } + + if ( cg.limboEndCinematicTime > cg.time ) { + //% CG_DrawPic( LIMBO_3D_X, LIMBO_3D_Y, LIMBO_3D_W, LIMBO_3D_H, cgs.media.limboRadioBroadcast ); + CG_DrawPic( LIMBO_3D_X + 4, LIMBO_3D_Y - 8, LIMBO_3D_W - 8, LIMBO_3D_W - 8, cgs.media.limboRadioBroadcast ); + } + + BG_PanelButtonsRender( limboPanelButtons ); + + trap_R_SetColor( NULL ); + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + + if ( cgs.ccRequestedObjective != -1 ) { + if ( cg.time - cgs.ccLastObjectiveRequestTime > 1000 ) { + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + if ( cgs.ccCurrentCamObjective != -1 || cgs.ccPortalEnt != -1 ) { + CG_LimboPanel_RequestObjective(); + } + } else { + if ( ( cgs.ccRequestedObjective == cgs.ccSelectedObjective && ( cgs.ccCurrentCamObjective != cgs.ccRequestedObjective || cgs.ccPortalEnt != -1 ) ) ) { + if ( !( cgs.ccRequestedObjective == CG_LimboPanel_GetMaxObjectives() && cgs.ccCurrentCamObjective == -1 && cgs.ccPortalEnt == -1 ) ) { + CG_LimboPanel_RequestObjective(); + } + } + } + } + } + + return qtrue; +} + +void CG_LimboPanel_KeyHandling( int key, qboolean down ) { + int b1, b2; + if ( BG_PanelButtonsKeyEvent( key, down, limboPanelButtons ) ) { + return; + } + + if ( down ) { + cgDC.getKeysForBinding( "openlimbomenu", &b1, &b2 ); + if ( ( b1 != -1 && b1 == key ) || ( b2 != -1 && b2 == key ) ) { + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + return; + } + } + + if ( down && key ) { + if ( CG_CommandCentreSpawnPointClick() ) { + return; + } + } +} + +void CG_LimboPanel_GetWeaponCardIconData( weapon_t weap, qhandle_t* shader, float* w, float* h, float* s0, float* t0, float* s1, float* t1 ) { + // setup the shader + switch ( weap ) { + case WP_MORTAR: + case WP_PANZERFAUST: + case WP_FLAMETHROWER: + case WP_FG42: + case WP_MOBILE_MG42: + case WP_MP40: + case WP_STEN: + case WP_THOMPSON: + *shader = cgs.media.limboWeaponCard1; + break; + + case WP_COLT: + case WP_LUGER: + case WP_AKIMBO_COLT: + case WP_AKIMBO_LUGER: + case WP_AKIMBO_SILENCEDCOLT: + case WP_AKIMBO_SILENCEDLUGER: + case WP_SILENCED_COLT: + case WP_SILENCER: + case WP_CARBINE: + case WP_GARAND: + case WP_KAR98: + case WP_K43: + *shader = cgs.media.limboWeaponCard2; + break; + + default: // shouldn't happen + *shader = 0; + } + + // setup s co-ords + switch ( weap ) { + case WP_SILENCED_COLT: + case WP_SILENCER: + case WP_LUGER: + case WP_COLT: + *s0 = 0; + *s1 = 0.5f; + break; + default: + *s0 = 0; + *s1 = 1; + break; + } + + // setup t co-ords + switch ( weap ) { + case WP_AKIMBO_SILENCEDLUGER: + case WP_SILENCER: + case WP_MORTAR: + *t0 = 0 / 8.f; + *t1 = 1 / 8.f; + break; + case WP_AKIMBO_SILENCEDCOLT: + case WP_SILENCED_COLT: + case WP_PANZERFAUST: + *t0 = 1 / 8.f; + *t1 = 2 / 8.f; + break; + case WP_LUGER: + case WP_AKIMBO_LUGER: + case WP_FLAMETHROWER: + *t0 = 2 / 8.f; + *t1 = 3 / 8.f; + break; + case WP_AKIMBO_COLT: + case WP_COLT: + case WP_FG42: + *t0 = 3 / 8.f; + *t1 = 4 / 8.f; + break; + case WP_CARBINE: + case WP_MOBILE_MG42: + *t0 = 4 / 8.f; + *t1 = 5 / 8.f; + break; + case WP_KAR98: + case WP_MP40: + *t0 = 5 / 8.f; + *t1 = 6 / 8.f; + break; + case WP_K43: + case WP_STEN: + *t0 = 6 / 8.f; + *t1 = 7 / 8.f; + break; + case WP_GARAND: + case WP_THOMPSON: + *t0 = 7 / 8.f; + *t1 = 8 / 8.f; + break; + default: // shouldn't happen + *t0 = 0.0; + *t1 = 1.0; + break; + } + + *h = 1.f; + switch ( weap ) { + case WP_SILENCED_COLT: + case WP_SILENCER: + case WP_COLT: + case WP_LUGER: + *w = 0.5f; + break; + default: + *w = 1.f; + break; + } +} + +// Gordon: Utility funcs +team_t CG_LimboPanel_GetTeam( void ) { + return teamOrder[cgs.ccSelectedTeam]; +} + +team_t CG_LimboPanel_GetRealTeam( void ) { + return cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ? CG_LimboPanel_GetTeam() : cgs.clientinfo[cg.clientNum].team; +} + +int CG_LimboPanel_GetClass( void ) { + return cgs.ccSelectedClass; +} + +bg_character_t* CG_LimboPanel_GetCharacter( void ) { + return BG_GetCharacter( CG_LimboPanel_GetTeam(), CG_LimboPanel_GetClass() ); +} + +bg_playerclass_t* CG_LimboPanel_GetPlayerClass( void ) { + return BG_GetPlayerClassInfo( CG_LimboPanel_GetTeam(), CG_LimboPanel_GetClass() ); +} + +int CG_LimboPanel_WeaponCount( void ) { + return CG_LimboPanel_WeaponCount_ForSlot( cgs.ccSelectedWeaponNumber ); +} + +int CG_LimboPanel_WeaponCount_ForSlot( int number ) { + if ( number == 1 ) { + bg_playerclass_t* classInfo = CG_LimboPanel_GetPlayerClass(); + int cnt = 0, i; + + for ( i = 0; i < MAX_WEAPS_PER_CLASS; i++ ) { + if ( !classInfo->classWeapons[i] ) { + break; + } + + cnt++; + } + return cnt; + } else { + if ( cgs.clientinfo[cg.clientNum].skill[SK_HEAVY_WEAPONS] >= 4 && CG_LimboPanel_GetClass() == PC_SOLDIER ) { + if ( cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS] >= 4 ) { + return 3; + } else { + return 2; + } + } else { + if ( cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS] >= 4 ) { + return 2; + } else { + return 1; + } + } + } +} + +int CG_LimboPanel_GetWeaponNumberForPos( int pos ) { + int i, cnt = 0; + + if ( cgs.ccSelectedWeaponNumber == 0 ) { + return pos; + } + + if ( pos < 0 || pos > CG_LimboPanel_WeaponCount() ) { + return 0; + } + + for ( i = 0; i <= pos; i++ ) { + while ( CG_LimboPanel_WeaponIsDisabled( i + cnt ) ) { + cnt++; + } + } + + return pos + cnt; +} + +weapon_t CG_LimboPanel_GetWeaponForNumber( int number, int slot, qboolean ignoreDisabled ) { + bg_playerclass_t* classInfo; + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return WP_NONE; + } + + classInfo = CG_LimboPanel_GetPlayerClass(); + if ( !classInfo ) { + return WP_NONE; + } + + if ( slot == 1 ) { + if ( !ignoreDisabled && CG_LimboPanel_WeaponIsDisabled( number ) ) { + if ( !number ) { + CG_Error( "ERROR: Class weapon 0 disabled\n" ); + return WP_NONE; + } else { + return classInfo->classWeapons[ 0 ]; + } + } + + return classInfo->classWeapons[ number ]; + } else { + if ( cgs.clientinfo[cg.clientNum].skill[SK_HEAVY_WEAPONS] >= 4 && CG_LimboPanel_GetClass() == PC_SOLDIER ) { + if ( cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS] >= 4 ) { + if ( number == 2 ) { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_MP40 : WP_THOMPSON; + } + } else { + if ( number == 1 ) { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_MP40 : WP_THOMPSON; + } + } + } + + if ( cgs.clientinfo[cg.clientNum].skill[SK_LIGHT_WEAPONS] >= 4 ) { + if ( number >= 1 ) { + if ( CG_LimboPanel_GetClass() == PC_COVERTOPS ) { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_AKIMBO_SILENCEDLUGER : WP_AKIMBO_SILENCEDCOLT; + } else { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_AKIMBO_LUGER : WP_AKIMBO_COLT; + } + } + } + + if ( number == 0 ) { + if ( CG_LimboPanel_GetClass() == PC_COVERTOPS ) { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_SILENCER : WP_SILENCED_COLT; + } else { + return CG_LimboPanel_GetTeam() == TEAM_AXIS ? WP_LUGER : WP_COLT; + } + } + + return 0; + } +} + +weapon_t CG_LimboPanel_GetSelectedWeaponForSlot( int index ) { + return CG_LimboPanel_GetWeaponForNumber( index == 1 ? cgs.ccSelectedWeapon : cgs.ccSelectedWeapon2, index, qfalse ); +} + +void CG_LimboPanel_SetSelectedWeaponNumForSlot( int index, int number ) { + if ( index == 0 ) { + cgs.ccSelectedWeapon = number; + } else { + cgs.ccSelectedWeapon2 = number; + } +} + +weapon_t CG_LimboPanel_GetSelectedWeapon( void ) { + return CG_LimboPanel_GetWeaponForNumber( CG_LimboPanel_GetSelectedWeaponNum(), cgs.ccSelectedWeaponNumber, qfalse ); +} + +int CG_LimboPanel_GetSelectedWeaponNum( void ) { + if ( !cgs.ccSelectedWeaponNumber ) { + return cgs.ccSelectedWeapon2; + } + + if ( CG_LimboPanel_WeaponIsDisabled( cgs.ccSelectedWeapon ) ) { + CG_LimboPanel_SetSelectedWeaponNumForSlot( 0, 0 ); + } + + return cgs.ccSelectedWeapon; +} + +void CG_LimboPanel_RequestWeaponStats( void ) { + extWeaponStats_t weapStat = CG_LimboPanel_GetSelectedWeaponStat(); + if ( weapStat == WS_MAX ) { + // Bleh? + return; + } + + trap_SendClientCommand( va( "ws %i", weapStat ) ); +} + +void CG_LimboPanel_RequestObjective( void ) { + int max = CG_LimboPanel_GetMaxObjectives(); + + if ( cgs.ccSelectedObjective != max && CG_LimboPanel_GetTeam() != TEAM_SPECTATOR ) { + trap_SendClientCommand( va( "obj %i", cgs.ccSelectedObjective ) ); + } else { + trap_SendClientCommand( va( "obj %i", -1 ) ); + } + cgs.ccRequestedObjective = cgs.ccSelectedObjective; + cgs.ccLastObjectiveRequestTime = cg.time; +} + + +void CG_LimboPanel_SetSelectedWeaponNum( int number ) { + if ( cgs.ccSelectedWeaponNumber == 1 ) { + if ( !CG_LimboPanel_WeaponIsDisabled( number ) ) { + cgs.ccSelectedWeapon = number; + } + } else { + cgs.ccSelectedWeapon2 = number; + } + + CG_LimboPanel_RequestWeaponStats(); +} + +extWeaponStats_t CG_LimboPanel_GetSelectedWeaponStat( void ) { + return BG_WeapStatForWeapon( CG_LimboPanel_GetSelectedWeapon() ); +} + +int CG_LimboPanel_TeamCount( weapon_t weap ) { + int i, cnt; + + if ( weap == -1 ) { // we aint checking for a weapon, so always include ourselves + cnt = 1; + } else { // we ARE checking for a weapon, so ignore ourselves + cnt = 0; + } + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + if ( i == cg.clientNum ) { + continue; + } + + if ( !cgs.clientinfo[i].infoValid ) { + continue; + } + + if ( cgs.clientinfo[i].team != CG_LimboPanel_GetTeam() ) { + continue; + } + + if ( weap != -1 ) { + if ( cgs.clientinfo[i].weapon != weap && cgs.clientinfo[i].latchedweapon != weap ) { + continue; + } + } + + cnt++; + } + + return cnt; +} + +qboolean CG_IsHeavyWeapon( weapon_t weap ) { + int i; + + for ( i = 0; i < NUM_HEAVY_WEAPONS; i++ ) { + if ( bg_heavyWeapons[i] == weap ) { + return qtrue; + } + } + + return qfalse; +} + +qboolean CG_LimboPanel_WeaponIsDisabled( int index ) { + bg_playerclass_t *classinfo; + int count, wcount; + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qtrue; + } + + classinfo = CG_LimboPanel_GetPlayerClass(); + + if ( !CG_IsHeavyWeapon( classinfo->classWeapons[index] ) ) { + return qfalse; + } + + count = CG_LimboPanel_TeamCount( -1 ); + wcount = CG_LimboPanel_TeamCount( classinfo->classWeapons[index] ); + + if ( wcount >= ceil( count * cgs.weaponRestrictions ) ) { + return qtrue; + } + + return qfalse; +} + +qboolean CG_LimboPanel_RealWeaponIsDisabled( weapon_t weap ) { + int count, wcount; + + if ( CG_LimboPanel_GetTeam() == TEAM_SPECTATOR ) { + return qtrue; + } + + if ( !CG_IsHeavyWeapon( weap ) ) { + return qfalse; + } + + count = CG_LimboPanel_TeamCount( -1 ); + wcount = CG_LimboPanel_TeamCount( weap ); + + if ( wcount >= ceil( count * cgs.weaponRestrictions ) ) { + return qtrue; + } + + return qfalse; +} diff --git a/src/cgame/cg_loadpanel.c b/src/cgame/cg_loadpanel.c new file mode 100644 index 0000000..a8ab655 --- /dev/null +++ b/src/cgame/cg_loadpanel.c @@ -0,0 +1,635 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" +#include "../ui/ui_shared.h" + +extern displayContextDef_t *DC; + +qboolean bg_loadscreeninited = qfalse; +qboolean bg_loadscreeninteractive; +fontInfo_t bg_loadscreenfont1; +fontInfo_t bg_loadscreenfont2; +qhandle_t bg_axispin; +qhandle_t bg_alliedpin; +qhandle_t bg_neutralpin; +qhandle_t bg_pin; + +qhandle_t bg_filter_pb; +qhandle_t bg_filter_ff; +qhandle_t bg_filter_hw; +qhandle_t bg_filter_lv; +qhandle_t bg_filter_al; +qhandle_t bg_filter_bt; + +qhandle_t bg_mappic; + +// panel_button_text_t FONTNAME = { SCALEX, SCALEY, COLOUR, STYLE, FONT }; + +panel_button_text_t missiondescriptionTxt = { + 0.2f, 0.2f, + { 0.0f, 0.0f, 0.0f, 1.f }, + 0, 0, + &bg_loadscreenfont2, +}; + +panel_button_text_t missiondescriptionHeaderTxt = { + 0.2f, 0.2f, + { 0.0f, 0.0f, 0.0f, 0.8f }, + 0,ITEM_ALIGN_CENTER, + &bg_loadscreenfont2, +}; + +panel_button_text_t campaignpheaderTxt = { + 0.2f, 0.2f, + { 1.0f, 1.0f, 1.0f, 0.6f }, + 0, 0, + &bg_loadscreenfont2, +}; + +panel_button_text_t campaignpTxt = { + 0.30f, 0.30f, + { 1.0f, 1.0f, 1.0f, 0.6f }, + 0, 0, + &bg_loadscreenfont2, +}; + +panel_button_text_t loadScreenMeterBackTxt = { + 0.22f, 0.22f, + { 0.1f, 0.1f, 0.1f, 0.8f }, + 0, ITEM_ALIGN_CENTER, + &bg_loadscreenfont2, +}; + +panel_button_t loadScreenMap = { + "gfx/loading/camp_map", + NULL, + { 0, 0, 440, 480 }, // shouldn't this be square?? // Gordon: no, the map is actually WIDER that tall, which makes it even worse... + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t loadScreenBack = { + "gfx/loading/camp_side", + NULL, + { 440, 0, 200, 480 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t loadScreenPins = { + NULL, + NULL, + { 0, 0, 640, 480 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LoadPanel_RenderCampaignPins, + NULL, +}; + +panel_button_t missiondescriptionPanelHeaderText = { + NULL, + "***TOP SECRET***", + { 440, 72, 200, 32 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &missiondescriptionHeaderTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t missiondescriptionPanelText = { + NULL, + NULL, + { 460, 84, 160, 232 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &missiondescriptionTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LoadPanel_RenderMissionDescriptionText, + NULL, +}; + +panel_button_t campaignheaderPanelText = { + NULL, + NULL, + { 456, 24, 152, 232 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &campaignpheaderTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LoadPanel_RenderCampaignTypeText, + NULL, +}; + +panel_button_t campaignPanelText = { + NULL, + NULL, + { 464, 40, 152, 232 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &campaignpTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LoadPanel_RenderCampaignNameText, + NULL, +}; + +panel_button_t loadScreenMeterBack = { + "gfx/loading/progressbar_back", + NULL, + { 440 + 26, 480 - 30 + 1, 200 - 56, 20 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Img, + NULL, +}; + +panel_button_t loadScreenMeterBack2 = { + "gfx/loading/progressbar", + NULL, + { 440 + 26, 480 - 30 + 1, 200 - 56, 20 }, + { 1, 255, 0, 0, 255, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_LoadPanel_RenderLoadingBar, + NULL, +}; + +panel_button_t loadScreenMeterBackText = { + NULL, + "LOADING", + { 440 + 28, 480 - 28 + 12 + 1, 200 - 56 - 2, 16 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &loadScreenMeterBackTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t* loadpanelButtons[] = { + &loadScreenMap, &loadScreenBack, + + + &missiondescriptionPanelText, &missiondescriptionPanelHeaderText, + + &campaignheaderPanelText, &campaignPanelText, + + &loadScreenMeterBack, &loadScreenMeterBack2, &loadScreenMeterBackText, + + &loadScreenPins, + + NULL, +}; + +/* +================ +CG_DrawConnectScreen +================ +*/ + +const char* CG_LoadPanel_GameTypeName( gametype_t gt ) { + switch ( gt ) { + case GT_SINGLE_PLAYER: + return "Single Player"; + case GT_COOP: + return "Co-op"; + case GT_WOLF: + return "Objective"; + case GT_WOLF_STOPWATCH: + return "Stopwatch"; + case GT_WOLF_CAMPAIGN: + return "Campaign"; + case GT_WOLF_LMS: + return "Last Man Standing"; + default: + break; + } + + return "Invalid"; +} + +void CG_DrawConnectScreen( qboolean interactive, qboolean forcerefresh ) { + static qboolean inside = qfalse; + char buffer[1024]; + + bg_loadscreeninteractive = interactive; + + if ( !DC ) { + return; + } + + if ( inside ) { + return; + } + + inside = qtrue; + + if ( !bg_loadscreeninited ) { + trap_Cvar_Set( "ui_connecting", "0" ); + + DC->registerFont( "ariblk", 27, &bg_loadscreenfont1 ); + DC->registerFont( "courbd", 30, &bg_loadscreenfont2 ); + + bg_axispin = DC->registerShaderNoMip( "gfx/loading/pin_axis" ); + bg_alliedpin = DC->registerShaderNoMip( "gfx/loading/pin_allied" ); + bg_neutralpin = DC->registerShaderNoMip( "gfx/loading/pin_neutral" ); + bg_pin = DC->registerShaderNoMip( "gfx/loading/pin_shot" ); + + + bg_filter_pb = DC->registerShaderNoMip( "ui/assets/filter_pb" ); + bg_filter_ff = DC->registerShaderNoMip( "ui/assets/filter_ff" ); + bg_filter_hw = DC->registerShaderNoMip( "ui/assets/filter_weap" ); + bg_filter_lv = DC->registerShaderNoMip( "ui/assets/filter_lives" ); + bg_filter_al = DC->registerShaderNoMip( "ui/assets/filter_antilag" ); + bg_filter_bt = DC->registerShaderNoMip( "ui/assets/filter_balance" ); + + + bg_mappic = 0; + + BG_PanelButtonsSetup( loadpanelButtons ); + + bg_loadscreeninited = qtrue; + } + + BG_PanelButtonsRender( loadpanelButtons ); + + if ( interactive ) { + DC->drawHandlePic( DC->cursorx, DC->cursory, 32, 32, DC->Assets.cursor ); + } + + DC->getConfigString( CS_SERVERINFO, buffer, sizeof( buffer ) ); + if ( *buffer ) { + const char *str; + qboolean enabled = qfalse; + float x, y; + int i; +// vec4_t clr1 = { 41/255.f, 51/255.f, 43/255.f, 204/255.f }; +// vec4_t clr2 = { 0.f, 0.f, 0.f, 225/255.f }; + vec4_t clr3 = { 1.f, 1.f, 1.f, .6f }; + +/* CG_FillRect( 8, 8, 230, 16, clr1 ); + CG_DrawRect_FixedBorder( 8, 8, 230, 16, 1, colorMdGrey ); + + CG_FillRect( 8, 23, 230, 210, clr2 ); + CG_DrawRect_FixedBorder( 8, 23, 230, 216, 1, colorMdGrey );*/ + + y = 322; + CG_Text_Paint_Centred_Ext( 540, y, 0.22f, 0.22f, clr3, "SERVER INFO", 0, 0, 0, &bg_loadscreenfont1 ); + + y = 340; + str = Info_ValueForKey( buffer, "sv_hostname" ); + CG_Text_Paint_Centred_Ext( 540, y, 0.2f, 0.2f, colorWhite, str && *str ? str : "ETHost", 0, 26, 0, &bg_loadscreenfont2 ); + + + y += 14; + for ( i = 0; i < MAX_MOTDLINES; i++ ) { + str = CG_ConfigString( CS_CUSTMOTD + i ); + if ( !str || !*str ) { + break; + } + + CG_Text_Paint_Centred_Ext( 540, y, 0.2f, 0.2f, colorWhite, str, 0, 26, 0, &bg_loadscreenfont2 ); + + y += 10; + } + + y = 417; + + str = Info_ValueForKey( buffer, "g_friendlyfire" ); + if ( str && *str && atoi( str ) ) { + x = 461; + CG_DrawPic( x, y, 16, 16, bg_filter_ff ); + } + + if ( atoi( Info_ValueForKey( buffer, "g_gametype" ) ) != GT_WOLF_LMS ) { + str = Info_ValueForKey( buffer, "g_alliedmaxlives" ); + if ( str && *str && atoi( str ) ) { + enabled = qtrue; + } else { + str = Info_ValueForKey( buffer, "g_axismaxlives" ); + if ( str && *str && atoi( str ) ) { + enabled = qtrue; + } else { + str = Info_ValueForKey( buffer, "g_maxlives" ); + if ( str && *str && atoi( str ) ) { + enabled = qtrue; + } + } + } + } + + if ( enabled ) { + x = 489; + CG_DrawPic( x, y, 16, 16, bg_filter_lv ); + } + + str = Info_ValueForKey( buffer, "sv_punkbuster" ); + if ( str && *str && atoi( str ) ) { + x = 518; + CG_DrawPic( x, y, 16, 16, bg_filter_pb ); + } + + str = Info_ValueForKey( buffer, "g_heavyWeaponRestriction" ); + if ( str && *str && atoi( str ) != 100 ) { + x = 546; + CG_DrawPic( x, y, 16, 16, bg_filter_hw ); + } + + str = Info_ValueForKey( buffer, "g_antilag" ); + if ( str && *str && atoi( str ) ) { + x = 575; + CG_DrawPic( x, y, 16, 16, bg_filter_al ); + } + + str = Info_ValueForKey( buffer, "g_balancedteams" ); + if ( str && *str && atoi( str ) ) { + x = 604; + CG_DrawPic( x, y, 16, 16, bg_filter_bt ); + } + } + + if ( *cgs.rawmapname ) { + if ( !bg_mappic ) { + bg_mappic = DC->registerShaderNoMip( va( "levelshots/%s", cgs.rawmapname ) ); + + if ( !bg_mappic ) { + bg_mappic = DC->registerShaderNoMip( "levelshots/unknownmap" ); + } + } + + trap_R_SetColor( colorBlack ); + CG_DrawPic( 16 + 1, 2 + 1, 192, 144, bg_mappic ); + + trap_R_SetColor( NULL ); + CG_DrawPic( 16, 2, 192, 144, bg_mappic ); + + CG_DrawPic( 16 + 80, 2 + 6, 20, 20, bg_pin ); + } + + if ( forcerefresh ) { + DC->updateScreen(); + } + + inside = qfalse; +} + +void CG_LoadPanel_RenderLoadingBar( panel_button_t* button ) { + int hunkused, hunkexpected; + float frac; + + trap_GetHunkData( &hunkused, &hunkexpected ); + + if ( hunkexpected <= 0 ) { + return; + } + + frac = hunkused / (float)hunkexpected; + if ( frac < 0.f ) { + frac = 0.f; + } + if ( frac > 1.f ) { + frac = 1.f; + } + + CG_DrawPicST( button->rect.x, button->rect.y, button->rect.w * frac, button->rect.h, 0, 0, frac, 1, button->hShaderNormal ); +} + +void CG_LoadPanel_RenderCampaignTypeText( panel_button_t* button ) { +/* char buffer[1024]; + const char* str; + DC->getConfigString( CS_SERVERINFO, buffer, sizeof( buffer ) ); + if( !*buffer ) { + return; + } + + str = Info_ValueForKey( buffer, "g_gametype" ); +*/ + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, va( "%s:", CG_LoadPanel_GameTypeName( cgs.gametype ) ), 0, 0, button->font->style, button->font->font ); +} + + +void CG_LoadPanel_RenderCampaignNameText( panel_button_t* button ) { + const char* cs; + float w; + //char buffer[1024]; + //int gametype; + + //DC->getConfigString( CS_SERVERINFO, buffer, sizeof( buffer ) ); + //cs = Info_ValueForKey( buffer, "g_gametype" ); + //gametype = atoi(cs); + + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + + cs = DC->nameForCampaign(); + if ( !cs ) { + return; + } + + cs = va( "%s %iof%i", cs, cgs.currentCampaignMap + 1, cgs.campaignData.mapCount ); + + w = CG_Text_Width_Ext( cs, button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x + ( button->rect.w - w ) * 0.5f, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, cs, 0, 0, 0, button->font->font ); + + } else { + + if ( !cgs.arenaInfoLoaded ) { + return; + } + + w = CG_Text_Width_Ext( cgs.arenaData.longname, button->font->scalex, 0, button->font->font ); + CG_Text_Paint_Ext( button->rect.x + ( button->rect.w - w ) * 0.5f, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, cgs.arenaData.longname, 0, 0, 0, button->font->font ); + } +} + +void CG_LoadPanel_RenderMissionDescriptionText( panel_button_t* button ) { + const char* cs; + char *s, *p; + char buffer[1024]; + float y; + //int gametype; + + //DC->getConfigString( CS_SERVERINFO, buffer, sizeof( buffer ) ); + //cs = Info_ValueForKey( buffer, "g_gametype" ); + //gametype = atoi(cs); + +// DC->fillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + + cs = DC->descriptionForCampaign(); + if ( !cs ) { + return; + } + + } else if ( cgs.gametype == GT_WOLF_LMS ) { + + //cs = CG_ConfigString( CS_MULTI_MAPDESC3 ); + + if ( !cgs.arenaInfoLoaded ) { + return; + } + + cs = cgs.arenaData.lmsdescription; + + } else { + + if ( !cgs.arenaInfoLoaded ) { + return; + } + + cs = cgs.arenaData.description; + } + + Q_strncpyz( buffer, cs, sizeof( buffer ) ); + while ( ( s = strchr( buffer, '*' ) ) ) { + *s = '\n'; + } + + BG_FitTextToWidth_Ext( buffer, button->font->scalex, button->rect.w - 16, sizeof( buffer ), button->font->font ); + + y = button->rect.y + 12; + + s = p = buffer; + while ( *p ) { + if ( *p == '\n' ) { + *p++ = '\0'; + DC->drawTextExt( button->rect.x + 4, y, button->font->scalex, button->font->scaley, button->font->colour, s, 0, 0, 0, button->font->font ); + y += 8; + s = p; + } else { + p++; + } + } +} + +void CG_LoadPanel_KeyHandling( int key, qboolean down ) { + if ( BG_PanelButtonsKeyEvent( key, down, loadpanelButtons ) ) { + return; + } +} + +qboolean CG_LoadPanel_ContinueButtonKeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + CG_EventHandling( CGAME_EVENT_GAMEVIEW, qfalse ); + return qtrue; + } + + return qfalse; +} + + +void CG_LoadPanel_DrawPin( const char* text, float px, float py, float sx, float sy, qhandle_t shader, float pinsize, float backheight ) { + float x, y, w, h; + vec4_t colourFadedBlack = { 0.f, 0.f, 0.f, 0.4f }; + + w = DC->textWidthExt( text, sx, 0, &bg_loadscreenfont2 ); + if ( px + 30 + w > 440 ) { + DC->fillRect( px - w - 28 + 2, py - ( backheight / 2.f ) + 2, 28 + w, backheight, colourFadedBlack ); + DC->fillRect( px - w - 28, py - ( backheight / 2.f ), 28 + w, backheight, colorBlack ); + } else { + DC->fillRect( px + 2, py - ( backheight / 2.f ) + 2, 28 + w, backheight, colourFadedBlack ); + DC->fillRect( px, py - ( backheight / 2.f ), 28 + w, backheight, colorBlack ); + } + + x = px - pinsize; + y = py - pinsize; + w = pinsize * 2.f; + h = pinsize * 2.f; + + DC->drawHandlePic( x, y, w, h, shader ); + + if ( px + 30 + w > 440 ) { + DC->drawTextExt( px - 12 - w - 28, py + 4, sx, sy, colorWhite, text, 0, 0, 0, &bg_loadscreenfont2 ); + } else { + DC->drawTextExt( px + 16, py + 4, sx, sy, colorWhite, text, 0, 0, 0, &bg_loadscreenfont2 ); + } +} + +void CG_LoadPanel_RenderCampaignPins( panel_button_t* button ) { + int i; + qhandle_t shader; + /*char buffer[1024]; + char *s; + int gametype; + + DC->getConfigString( CS_SERVERINFO, buffer, sizeof( buffer ) ); + s = Info_ValueForKey( buffer, "g_gametype" ); + gametype = atoi(s);*/ + + if ( cgs.gametype == GT_WOLF_STOPWATCH || cgs.gametype == GT_WOLF_LMS || cgs.gametype == GT_WOLF ) { + float px, py; + + if ( !cgs.arenaInfoLoaded ) { + return; + } + + px = ( cgs.arenaData.mappos[0] / 1024.f ) * 440.f; + py = ( cgs.arenaData.mappos[1] / 1024.f ) * 480.f; + + CG_LoadPanel_DrawPin( cgs.arenaData.longname, px, py, 0.22f, 0.25f, bg_neutralpin, 16.f, 16.f ); + } else { + if ( !cgs.campaignInfoLoaded ) { + return; + } + + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + float px, py; + + cg.teamWonRounds[1] = atoi( CG_ConfigString( CS_ROUNDSCORES1 ) ); + cg.teamWonRounds[0] = atoi( CG_ConfigString( CS_ROUNDSCORES2 ) ); + + if ( cg.teamWonRounds[1] & ( 1 << i ) ) { + shader = bg_axispin; + } else if ( cg.teamWonRounds[0] & ( 1 << i ) ) { + shader = bg_alliedpin; + } else { + shader = bg_neutralpin; + } + + px = ( cgs.campaignData.arenas[i].mappos[0] / 1024.f ) * 440.f; + py = ( cgs.campaignData.arenas[i].mappos[1] / 1024.f ) * 480.f; + + CG_LoadPanel_DrawPin( cgs.campaignData.arenas[i].longname, px, py, 0.22f, 0.25f, shader, 16.f, 16.f ); + } + } +} diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h new file mode 100644 index 0000000..0510d60 --- /dev/null +++ b/src/cgame/cg_local.h @@ -0,0 +1,3501 @@ +/* +=========================================================================== + +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: cg_local.h + * + * desc: The entire cgame module is unloaded and reloaded on each level change, + * so there is NO persistant data between levels on the client side. + * If you absolutely need something stored, it can either be kept + * by the server in the server stored userinfos, or stashed in a cvar. + + * +*/ + +#include "../game/q_shared.h" +#include "tr_types.h" +#include "../game/bg_public.h" +#include "cg_public.h" +#include "../ui/ui_shared.h" + + +#define MAX_LOCATIONS 256 +#define POWERUP_BLINKS 5 + +#define STATS_FADE_TIME 200.0f +#define POWERUP_BLINK_TIME 1000 +#define FADE_TIME 200 +#define DAMAGE_DEFLECT_TIME 100 +#define DAMAGE_RETURN_TIME 400 +#define DAMAGE_TIME 500 +#define LAND_DEFLECT_TIME 150 +#define LAND_RETURN_TIME 300 +#define STEP_TIME 200 +#define DUCK_TIME 100 +#define PAIN_TWITCH_TIME 200 +#define ZOOM_TIME 150 +#define MUZZLE_FLASH_TIME 30 +#define SINK_TIME 1000 // time for fragments to sink into ground before going away +#define REWARD_TIME 3000 + +#define PRONE_TIME 500 + +#define PULSE_SCALE 1.5 // amount to scale up the icons when activating + +#define MAX_STEP_CHANGE 32 + +#define MAX_VERTS_ON_POLY 10 +#define MAX_MARK_POLYS 256 // JPW NERVE was 1024 + +#define STAT_MINUS 10 // num frame for '-' stats digit + +#define ICON_SIZE 48 +#define CHAR_WIDTH 32 +#define CHAR_HEIGHT 48 +#define TEXT_ICON_SPACE 4 + +#define TEAMCHAT_WIDTH 70 +#define TEAMCHAT_HEIGHT 8 + +#define NOTIFY_WIDTH 80 +#define NOTIFY_HEIGHT 5 + +// very large characters +#define GIANT_WIDTH 32 +#define GIANT_HEIGHT 48 + +#define NUM_CROSSHAIRS 10 + +// Ridah, trails +#define STYPE_STRETCH 0 +#define STYPE_REPEAT 1 + +#define TJFL_FADEIN ( 1 << 0 ) +#define TJFL_CROSSOVER ( 1 << 1 ) +#define TJFL_NOCULL ( 1 << 2 ) +#define TJFL_FIXDISTORT ( 1 << 3 ) +#define TJFL_SPARKHEADFLARE ( 1 << 4 ) +#define TJFL_NOPOLYMERGE ( 1 << 5 ) +// done. + +// OSP +// Autoaction values +#define AA_DEMORECORD 0x01 +#define AA_SCREENSHOT 0x02 +#define AA_STATSDUMP 0x04 + +// Cursor +#define CURSOR_OFFSETX 13 +#define CURSOR_OFFSETY 12 + +// Demo controls +#define DEMO_THIRDPERSONUPDATE 0 +#define DEMO_RANGEDELTA 6 +#define DEMO_ANGLEDELTA 4 + +// MV overlay +#define MVINFO_TEXTSIZE 10 +#define MVINFO_RIGHT 640 - 3 +#define MVINFO_TOP 100 + +#define MAX_WINDOW_COUNT 10 +#define MAX_WINDOW_LINES 64 + +#define MAX_STRINGS 80 +#define MAX_STRING_POOL_LENGTH 128 + +#define WINDOW_FONTWIDTH 8 // For non-true-type: width to scale from +#define WINDOW_FONTHEIGHT 8 // For non-true-type: height to scale from + +#define WID_NONE 0x00 // General window +#define WID_STATS 0x01 // Stats (reusable due to scroll effect) +#define WID_TOPSHOTS 0x02 // Top/Bottom-shots +#define WID_MOTD 0x04 // MOTD +//#define WID_DEMOHELP 0x08 // Demo key control info +//#define WID_SPECHELP 0x10 // MV spectator key control info + +#define WFX_TEXTSIZING 0x01 // Size the window based on text/font setting +#define WFX_FLASH 0x02 // Alternate between bg and b2 every half second +#define WFX_TRUETYPE 0x04 // Use truetype fonts for text +#define WFX_MULTIVIEW 0x08 // Multiview window +// These need to be last +#define WFX_FADEIN 0x10 // Fade the window in (and back out when closing) +#define WFX_SCROLLUP 0x20 // Scroll window up from the bottom (and back down when closing) +#define WFX_SCROLLDOWN 0x40 // Scroll window down from the top (and back up when closing) +#define WFX_SCROLLLEFT 0x80 // Scroll window in from the left (and back right when closing) +#define WFX_SCROLLRIGHT 0x100 // Scroll window in from the right (and back left when closing) + +#define WSTATE_COMPLETE 0x00 // Window is up with startup effects complete +#define WSTATE_START 0x01 // Window is "initializing" w/effects +#define WSTATE_SHUTDOWN 0x02 // Window is shutting down with effects +#define WSTATE_OFF 0x04 // Window is completely shutdown + +#define MV_PID 0x00FF // Bits available for player IDs for MultiView windows +#define MV_SELECTED 0x0100 // MultiView selected window flag is the 9th bit + +typedef struct { + vec4_t colorBorder; // Window border color + vec4_t colorBackground; // Window fill color + vec4_t colorBackground2; // Window fill color2 (for flashing) + int curX; // Scrolling X position + int curY; // Scrolling Y position + int effects; // Window effects + int flashMidpoint; // Flashing transition point (in ms) + int flashPeriod; // Background flashing period (in ms) + int fontHeight; // For non-truetype font drawing + float fontScaleX; // Font scale factor + float fontScaleY; // Font scale factor + int fontWidth; // For non-truetype font drawing + float h; // Height + int id; // Window ID for special handling (i.e. stats, motd, etc.) + qboolean inuse; // Activity flag + int lineCount; // Number of lines to display + int lineHeight[MAX_WINDOW_LINES]; // Height property for each line + char *lineText[MAX_WINDOW_LINES]; // Text info + float m_x; // Mouse X position + float m_y; // Mouse Y position + int mvInfo; // lower 8 = player id, 9 = is_selected + int targetTime; // Time to complete any defined effect + int state; // Current state of the window + int time; // Current window time + float w; // Width + float x; // Target x-coordinate + // negative values will align the window from the right minus the (window width + offset(x)) + float y; // Target y-coordinate + // negative values will align the window from the bottom minus the (window height + offset(y)) +} cg_window_t; + +typedef struct { + qboolean fActive; + char str[MAX_STRING_POOL_LENGTH]; +} cg_string_t; + +typedef struct { + int activeWindows[MAX_WINDOW_COUNT]; // List of active windows + int numActiveWindows; // Number of active windows in use + cg_window_t window[MAX_WINDOW_COUNT]; // Static allocation of all windows +} cg_windowHandler_t; + +typedef struct { + int pID; // Player ID + int classID; // Player's current class + int width; // Width of text box + char info[8]; // On-screen info (w/color coding) + qboolean fActive; // Overlay element is active + cg_window_t *w; // Window handle (may be NULL) +} cg_mvinfo_t; +// OSP + + + + +// START Mad Doc - TDF +#define NUM_OVERLAY_FACES 1 +// END Mad Doc - TDF + +//================================================= + +// player entities need to track more information +// than any other type of entity. + +// note that not every player entity is a client entity, +// because corpses after respawn are outside the normal +// client numbering range + +// when changing animation, set animationTime to frameTime + lerping time +// The current lerp will finish out, then it will lerp to the new animation +typedef struct { + int oldFrame; + int oldFrameTime; // time when ->oldFrame was exactly on + qhandle_t oldFrameModel; + + int frame; + int frameTime; // time when ->frame will be exactly on + qhandle_t frameModel; + + float backlerp; + + float yawAngle; + qboolean yawing; + float pitchAngle; + qboolean pitching; + + int animationNumber; // may include ANIM_TOGGLEBIT + int oldAnimationNumber; // may include ANIM_TOGGLEBIT + animation_t *animation; + int animationTime; // time when the first frame of the animation will be exact + + + // Ridah, variable speed anims + vec3_t oldFramePos; + float animSpeedScale; + int oldFrameSnapshotTime; + headAnimation_t *headAnim; + // done. + +} lerpFrame_t; + +typedef struct { + lerpFrame_t legs, torso; + lerpFrame_t head; + lerpFrame_t weap; //----(SA) autonomous weapon animations + lerpFrame_t hudhead; + + int painTime; + int painDuration; + int painDirection; // flip from 0 to 1 + int painAnimTorso; + int painAnimLegs; + int lightningFiring; + + // Ridah, so we can do fast tag grabbing + refEntity_t bodyRefEnt, headRefEnt, gunRefEnt; + int gunRefEntFrame; + + float animSpeed; // for manual adjustment + + int lastFiredWeaponTime; + int weaponFireTime; +} playerEntity_t; + +//================================================= + +typedef struct tag_s { + vec3_t origin; + vec3_t axis[3]; +} tag_t; + + +// centity_t have a direct corespondence with gentity_t in the game, but +// only the entityState_t is directly communicated to the cgame +typedef struct centity_s { + entityState_t currentState; // from cg.frame + entityState_t nextState; // from cg.nextFrame, if available + qboolean interpolate; // true if next is valid to interpolate to + qboolean currentValid; // true if cg.frame holds this entity + + int muzzleFlashTime; // move to playerEntity? + int overheatTime; + int previousEvent; + int previousEventSequence; // Ridah + int teleportFlag; + + int trailTime; // so missile trails can handle dropped initial packets + int miscTime; + int soundTime; // ydnar: so looping sounds can start when triggered + + playerEntity_t pe; + +// int errorTime; // decay the error from this time +// vec3_t errorOrigin; +// vec3_t errorAngles; + +// qboolean extrapolated; // false if origin / angles is an interpolation + vec3_t rawOrigin; + vec3_t rawAngles; + + // exact interpolated position of entity on this frame + vec3_t lerpOrigin; + vec3_t lerpAngles; + + vec3_t lastLerpAngles; // (SA) for remembering the last position when a state changes + vec3_t lastLerpOrigin; // Gordon: Added for linked trains player adjust prediction + + // Ridah, trail effects + int headJuncIndex, headJuncIndex2; + int lastTrailTime; + // done. + + // Ridah + vec3_t fireRiseDir; // if standing still this will be up, otherwise it'll point away from movement dir + int lastFuseSparkTime; + + // client side dlights + int dl_frame; + int dl_oldframe; + float dl_backlerp; + int dl_time; + char dl_stylestring[64]; + int dl_sound; + int dl_atten; + + lerpFrame_t lerpFrame; //----(SA) added + vec3_t highlightOrigin; // center of the geometry. for things like corona placement on treasure + qboolean usehighlightOrigin; + + refEntity_t refEnt; + int processedFrame; // frame we were last added to the scene + + int voiceChatSprite; // DHM - Nerve + int voiceChatSpriteTime; // DHM - Nerve + + // item highlighting + int highlightTime; + qboolean highlighted; + + // spline stuff + vec3_t origin2; + splinePath_t* backspline; + float backdelta; + qboolean back; + qboolean moving; + + + int tankframe; + int tankparent; + tag_t mountedMG42Base; + tag_t mountedMG42Nest; + tag_t mountedMG42; + tag_t mountedMG42Player; + tag_t mountedMG42Flash; + + qboolean akimboFire; + + // Gordon: tagconnect cleanup.. + int tagParent; + char tagName[MAX_QPATH]; +} centity_t; + + +//====================================================================== + +// local entities are created as a result of events or predicted actions, +// and live independantly from all server transmitted entities + +typedef struct markPoly_s { + struct markPoly_s *prevMark, *nextMark; + int time; + qhandle_t markShader; + qboolean alphaFade; // fade alpha instead of rgb + float color[4]; + poly_t poly; + polyVert_t verts[MAX_VERTS_ON_POLY]; + + int duration; // Ridah +} markPoly_t; + +//----(SA) moved in from cg_view.c +typedef enum { + ZOOM_NONE, + ZOOM_BINOC, + ZOOM_SNIPER, + ZOOM_SNOOPER, + ZOOM_FG42SCOPE, + ZOOM_MG42, + ZOOM_MAX_ZOOMS +} EZoom_t; + +typedef enum { + ZOOM_OUT, // widest angle + ZOOM_IN // tightest angle (approaching 0) +} EZoomInOut_t; + +extern float zoomTable[ZOOM_MAX_ZOOMS][2]; + +//----(SA) end + +typedef enum { + LE_MARK, + LE_EXPLOSION, + LE_SPRITE_EXPLOSION, + LE_FRAGMENT, + LE_MOVE_SCALE_FADE, + LE_FALL_SCALE_FADE, + LE_FADE_RGB, + LE_SCALE_FADE, + LE_SPARK, + LE_DEBRIS, + LE_BLOOD, + LE_FUSE_SPARK, +// LE_ZOMBIE_SPIRIT, +// LE_ZOMBIE_BAT, + LE_MOVING_TRACER, + LE_EMITTER +} leType_t; + +typedef enum { + LEF_PUFF_DONT_SCALE = 0x0001 // do not scale size over time + ,LEF_TUMBLE = 0x0002 // tumble over time, used for ejecting shells + ,LEF_NOFADEALPHA = 0x0004 // Ridah, sparks + ,LEF_SMOKING = 0x0008 // (SA) smoking + ,LEF_TUMBLE_SLOW = 0x0010 // slow down tumble on hitting ground +} leFlag_t; + +typedef enum { + LEMT_NONE, + LEMT_BLOOD +} leMarkType_t; // fragment local entities can leave marks on walls + +typedef enum { + LEBS_NONE, + LEBS_BLOOD, + LEBS_ROCK, + LEBS_WOOD, + LEBS_BRASS, + LEBS_METAL, + LEBS_BONE +} leBounceSoundType_t; // fragment local entities can make sounds on impacts + +typedef struct localEntity_s { + struct localEntity_s *prev, *next; + leType_t leType; + int leFlags; + + int startTime; + int endTime; + int fadeInTime; + + float lifeRate; // 1.0 / (endTime - startTime) + + trajectory_t pos; + trajectory_t angles; + + float bounceFactor; // 0.0 = no bounce, 1.0 = perfect + + float color[4]; + + float radius; + + float light; + vec3_t lightColor; + + leMarkType_t leMarkType; // mark to leave on fragment impact + leBounceSoundType_t leBounceSoundType; + + refEntity_t refEntity; + + // Ridah + int lightOverdraw; + int lastTrailTime; + int headJuncIndex, headJuncIndex2; + float effectWidth; + int effectFlags; + struct localEntity_s *chain; // used for grouping entities (like for flamethrower junctions) + int onFireStart, onFireEnd; + int ownerNum; + int lastSpiritDmgTime; + + int loopingSound; + + int breakCount; // break-up this many times before we can break no more + float sizeScale; + // done. + +} localEntity_t; + +//====================================================================== + + +typedef struct { + int client; + int score; + int ping; + int time; + int powerUps; + int team; + int playerClass; // NERVE - SMF + int respawnsLeft; // NERVE - SMF +} score_t; + +// each client has an associated clientInfo_t +// that contains media references necessary to present the +// client model and other color coded effects +// this is regenerated each time a client's configstring changes, +// usually as a result of a userinfo (name, model, etc) change +#define MAX_CUSTOM_SOUNDS 32 +typedef struct clientInfo_s { + qboolean infoValid; + + int clientNum; + + char name[MAX_QPATH]; + char cleanname[MAX_QPATH]; + team_t team; + + int botSkill; // 0 = not bot, 1-5 = bot + int score; // updated by score servercmds + int location[2]; // location in 2d for team mode + int health; // you only get this info about your teammates + int curWeapon; + int powerups; // so can display quad/flag status + int breathPuffTime; + int cls; + int blinkTime; //----(SA) + + int handshake; + int rank; + qboolean ccSelected; + int fireteam; + int medals[SK_NUM_SKILLS]; + int skill[SK_NUM_SKILLS]; + int skillpoints[SK_NUM_SKILLS]; // filled OOB by +wstats + + char disguiseName[MAX_QPATH]; + int disguiseRank; + + int weapon; + int secondaryweapon; + int latchedweapon; + + int refStatus; + + bg_character_t *character; + + // Gordon: caching fireteam pointer here, better than trying to work it out all the time + fireteamData_t* fireteamData; + + // Gordon: for fireteams, has been selected + qboolean selected; + + // Gordon: Intermission stats + int totalWeapAcc; + int kills; + int deaths; + + // OSP - per client MV ps info + int ammo; + int ammoclip; + int chargeTime; + qboolean fCrewgun; + int cursorHint; + int grenadeTimeLeft; // Actual time remaining + int grenadeTimeStart; // Time trigger base to compute TimeLeft + int hintTime; + int sprintTime; + int weapHeat; + int weaponState; + int weaponState_last; +} clientInfo_t; + +typedef enum { + W_PART_1, + W_PART_2, + W_PART_3, + W_PART_4, + W_PART_5, + W_PART_6, + W_PART_7, + W_MAX_PARTS +} barrelType_t; + +typedef enum { + W_TP_MODEL, // third person model + W_FP_MODEL, // first person model + W_PU_MODEL, // pickup model + W_NUM_TYPES +} modelViewType_t; + +typedef struct partModel_s { + char tagName[MAX_QPATH]; + qhandle_t model; + qhandle_t skin[3]; // 0: neutral, 1: axis, 2: allied +} partModel_t; + +typedef struct weaponModel_s { + qhandle_t model; + qhandle_t skin[3]; // 0: neutral, 1: axis, 2: allied +} weaponModel_t; + +// each WP_* weapon enum has an associated weaponInfo_t +// that contains media references necessary to present the +// weapon and its effects +typedef struct weaponInfo_s { + qboolean registered; + + animation_t weapAnimations[MAX_WP_ANIMATIONS]; + + qhandle_t handsModel; // the hands don't actually draw, they just position the weapon + + qhandle_t standModel; // not drawn. tags used for positioning weapons for pickup + qboolean droppedAnglesHack; + + weaponModel_t weaponModel[W_NUM_TYPES]; + partModel_t partModels[W_NUM_TYPES][W_MAX_PARTS]; + qhandle_t flashModel[W_NUM_TYPES]; + qhandle_t modModels[6]; // like the scope for the rifles + + vec3_t flashDlightColor; + sfxHandle_t flashSound[4]; // fast firing weapons randomly choose + sfxHandle_t flashEchoSound[4]; //----(SA) added - distant gun firing sound + sfxHandle_t lastShotSound[4]; // sound of the last shot can be different (mauser doesn't have bolt action on last shot for example) + + qhandle_t weaponIcon[2]; //----(SA) [0] is weap icon, [1] is highlight icon + qhandle_t ammoIcon; + + qhandle_t missileModel; + qhandle_t missileAlliedSkin; + qhandle_t missileAxisSkin; + sfxHandle_t missileSound; + void ( *missileTrailFunc )( centity_t *, const struct weaponInfo_s *wi ); + float missileDlight; + vec3_t missileDlightColor; + int missileRenderfx; + + void ( *ejectBrassFunc )( centity_t * ); + + sfxHandle_t readySound; // an amibient sound the weapon makes when it's /not/ firing + sfxHandle_t firingSound; + sfxHandle_t overheatSound; + sfxHandle_t reloadSound; + sfxHandle_t reloadFastSound; + + sfxHandle_t spinupSound; //----(SA) added // sound started when fire button goes down, and stepped on when the first fire event happens + sfxHandle_t spindownSound; //----(SA) added // sound called if the above is running but player doesn't follow through and fire + + sfxHandle_t switchSound; +} weaponInfo_t; + + +// each IT_* item has an associated itemInfo_t +// that constains media references necessary to present the +// item and its effects +typedef struct { + qboolean registered; + qhandle_t models[MAX_ITEM_MODELS]; + qhandle_t icons[MAX_ITEM_ICONS]; +} itemInfo_t; + + +typedef struct { + int itemNum; +} powerupInfo_t; + +#define MAX_VIEWDAMAGE 8 +typedef struct { + int damageTime, damageDuration; + float damageX, damageY, damageValue; +} viewDamage_t; + +#define MAX_REWARDSTACK 5 + +//====================================================================== + +// all cg.stepTime, cg.duckTime, cg.landTime, etc are set to cg.time when the action +// occurs, and they will have visible effects for #define STEP_TIME or whatever msec after + +#define MAX_PREDICTED_EVENTS 16 + +#define MAX_SPAWN_VARS 64 +#define MAX_SPAWN_VARS_CHARS 2048 + + +#define MAX_SPAWNPOINTS 32 +#define MAX_SPAWNDESC 128 + +#define MAX_BUFFERED_SOUNDSCRIPTS 16 + +#define MAX_SOUNDSCRIPT_SOUNDS 16 + +typedef struct soundScriptHandle_s { + char filename[MAX_QPATH]; + sfxHandle_t sfxHandle; +} soundScriptHandle_t; + +typedef struct soundScriptSound_s { + soundScriptHandle_t sounds[MAX_SOUNDSCRIPT_SOUNDS]; + + int numsounds; + int lastPlayed; + + struct soundScriptSound_s *next; +} soundScriptSound_t; + +typedef struct soundScript_s +{ + int index; + char name[MAX_QPATH]; + int channel; + int attenuation; + qboolean streaming; + qboolean looping; + qboolean random; // TODO + int numSounds; + soundScriptSound_t *soundList; // pointer into the global list of soundScriptSounds (defined below) + + struct soundScript_s *nextHash; // next soundScript in our hashTable list position +} soundScript_t; + +typedef struct { + int x, y, z; + int yaw; + int data; + char type; +// int status; + +// qboolean selected; + + vec2_t transformed; + vec2_t automapTransformed; + + team_t team; +} mapEntityData_t; + +// START xkan, 8/29/2002 +// the most buddies we can have +#define MAX_NUM_BUDDY 6 +// END xkan, 8/29/2002 + +typedef enum { + SHOW_OFF, + SHOW_SHUTDOWN, + SHOW_ON +} showView_t; + +void CG_ParseMapEntityInfo( int axis_number, int allied_number ); + +#define MAX_BACKUP_STATES ( CMD_BACKUP + 2 ) + +typedef struct { + int clientFrame; // incremented each frame + + int clientNum; + int xp; + int xpChangeTime; + + qboolean demoPlayback; + qboolean loading; // don't defer players at initial startup + qboolean intermissionStarted; // don't play voice rewards, because game will end shortly + + // there are only one or two snapshot_t that are relevent at a time + int latestSnapshotNum; // the number of snapshots the client system has received + int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet + + snapshot_t *snap; // cg.snap->serverTime <= cg.time + snapshot_t *nextSnap; // cg.nextSnap->serverTime > cg.time, or NULL + snapshot_t activeSnapshots[2]; + + float frameInterpolation; // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime) + + qboolean thisFrameTeleport; + qboolean nextFrameTeleport; + + int frametime; // cg.time - cg.oldTime + + int time; // this is the time value that the client + // is rendering at. + int oldTime; // time at last frame, used for missile trails and prediction checking + + int physicsTime; // either cg.snap->time or cg.nextSnap->time + + int timelimitWarnings; // 5 min, 1 min, overtime + + qboolean mapRestart; // set on a map restart to set back the weapon + + qboolean renderingThirdPerson; // during deaths, chasecams, etc + + // prediction state + qboolean hyperspace; // true if prediction has hit a trigger_teleport + playerState_t predictedPlayerState; + centity_t predictedPlayerEntity; + qboolean validPPS; // clear until the first call to CG_PredictPlayerState + int predictedErrorTime; + vec3_t predictedError; + + int eventSequence; + int predictableEvents[MAX_PREDICTED_EVENTS]; + + float stepChange; // for stair up smoothing + int stepTime; + + float duckChange; // for duck viewheight smoothing + int duckTime; + + float landChange; // for landing hard + int landTime; + + // input state sent to server + int weaponSelect; + + // auto rotating items + vec3_t autoAnglesSlow; + vec3_t autoAxisSlow[3]; + vec3_t autoAngles; + vec3_t autoAxis[3]; + vec3_t autoAnglesFast; + vec3_t autoAxisFast[3]; + + // view rendering + refdef_t refdef; + vec3_t refdefViewAngles; // will be converted to refdef.viewaxis + + // zoom key + qboolean zoomed; + qboolean zoomedBinoc; + int zoomedScope; //----(SA) changed to int + int zoomTime; + float zoomSensitivity; + float zoomval; + + + // information screen text during loading + char infoScreenText[MAX_STRING_CHARS]; + + // scoreboard + int scoresRequestTime; + int numScores; + int selectedScore; + int teamScores[2]; + int teamPlayers[TEAM_NUM_TEAMS]; // JPW NERVE for scoreboard + score_t scores[MAX_CLIENTS]; + qboolean showScores; + qboolean scoreBoardShowing; + int scoreFadeTime; + char killerName[MAX_NAME_LENGTH]; + char spectatorList[MAX_STRING_CHARS]; // list of names + int spectatorLen; // length of list + float spectatorWidth; // width in device units + int spectatorTime; // next time to offset + int spectatorPaintX; // current paint x + int spectatorPaintX2; // current paint x + int spectatorOffset; // current offset from start + int spectatorPaintLen; // current offset from start + + //qboolean showItems; + //int itemFadeTime; + + qboolean lightstylesInited; + + // centerprinting + int centerPrintTime; + int centerPrintCharWidth; + int centerPrintY; + char centerPrint[1024]; + int centerPrintLines; + int centerPrintPriority; // NERVE - SMF + + // fade in/out + int fadeTime; + float fadeRate; + vec4_t fadeColor1; + vec4_t fadeColor2; + + // game stats + int exitStatsTime; + int exitStatsFade; + + // low ammo warning state + int lowAmmoWarning; // 1 = low, 2 = empty + + // kill timers for carnage reward + int lastKillTime; + + // crosshair client ID + int crosshairClientNum; + int crosshairClientTime; + + qboolean crosshairNotLookingAtClient; + int crosshairSPClientTime; + int crosshairVerticalShift; + qboolean crosshairClientNoShoot; + qboolean crosshairTerrain; + + int teamFirstBlood; // 0: allies 1: axis -1: nobody + int teamWonRounds[2]; + + qboolean filtercams; + + int crosshairPowerupNum; + int crosshairPowerupTime; + +// int identifyClientNum; // NERVE - SMF +// int identifyClientHealth; // NERVE - SMF + int identifyClientRequest; // NERVE - SMF + +//----(SA) added + // cursorhints + int cursorHintIcon; + int cursorHintTime; + int cursorHintFade; + int cursorHintValue; +//----(SA) end + + // powerup active flashing + int powerupActive; + int powerupTime; + + // attacking player + int attackerTime; + int voiceTime; + + // reward tmedals + int rewardStack; + int rewardTime; + int rewardCount[MAX_REWARDSTACK]; + qhandle_t rewardShader[MAX_REWARDSTACK]; + qhandle_t rewardSound[MAX_REWARDSTACK]; + + // warmup countdown + int warmup; + int warmupCount; + + //========================== + + int itemPickup; + int itemPickupTime; + int itemPickupBlendTime; // the pulse around the crosshair is timed seperately + + int weaponSelectTime; + int weaponAnimation; + int weaponAnimationTime; + + // blend blobs + viewDamage_t viewDamage[MAX_VIEWDAMAGE]; + float damageTime; // last time any kind of damage was recieved + int damageIndex; // slot that was filled in + float damageX, damageY, damageValue; + + int grenLastTime; + + int switchbackWeapon; + int lastFiredWeapon; + int lastFiredWeaponTime; + int painTime; + int weaponFireTime; + int nextIdleTime; + int lastIdleTimeEnd; + int idleAnim; + int lastWeapSelInBank[MAX_WEAP_BANKS_MP]; // remember which weapon was last selected in a bank for 'weaponbank' commands //----(SA) added +// JPW FIXME NOTE: max_weap_banks > max_weap_banks_mp so this should be OK, but if that changes, change this too + + // status bar head + float headYaw; + float headEndPitch; + float headEndYaw; + int headEndTime; + float headStartPitch; + float headStartYaw; + int headStartTime; + + // view movement + float v_dmg_time; + float v_dmg_pitch; + float v_dmg_roll; + + vec3_t kick_angles; // weapon kicks + vec3_t kick_origin; + + // RF, view flames when getting burnt + int v_fireTime, v_noFireTime; + vec3_t v_fireRiseDir; + + // temp working variables for player view + float bobfracsin; + int bobcycle; + float lastvalidBobfracsin; + int lastvalidBobcycle; + float xyspeed; + int nextOrbitTime; + + // development tool + refEntity_t testModelEntity; + char testModelName[MAX_QPATH]; + qboolean testGun; + + // RF, new kick angles + vec3_t kickAVel; // for damage feedback, weapon recoil, etc + // This is the angular velocity, to give a smooth + // rotational feedback, rather than sudden jerks + vec3_t kickAngles; // for damage feedback, weapon recoil, etc + // NOTE: this is not transmitted through MSG.C stream + // since weapon kicks are client-side, and damage feedback + // is rare enough that we can transmit that as an event + float recoilPitch, recoilPitchAngle; + + // Duffy + qboolean cameraMode; // if rendering from a camera + // Duffy end + + // NERVE - SMF - Objective info display + qboolean limboMenu; + + int oidTeam; + int oidPrintTime; + int oidPrintCharWidth; + int oidPrintY; + char oidPrint[1024]; + int oidPrintLines; + + // for voice chat buffer + int voiceChatTime; + int voiceChatBufferIn; + int voiceChatBufferOut; + + int newCrosshairIndex; + qhandle_t crosshairShaderAlt[NUM_CROSSHAIRS]; + + int cameraShakeTime; + float cameraShakePhase; + float cameraShakeScale; + float cameraShakeLength; + + qboolean latchAutoActions; + qboolean latchVictorySound; + // -NERVE - SMF + + // spawn variables + qboolean spawning; // the CG_Spawn*() functions are valid + int numSpawnVars; + char *spawnVars[MAX_SPAWN_VARS][2]; // key / value pairs + int numSpawnVarChars; + char spawnVarChars[MAX_SPAWN_VARS_CHARS]; + + vec2_t mapcoordsMins; + vec2_t mapcoordsMaxs; + vec2_t mapcoordsScale; + qboolean mapcoordsValid; + + + int numMiscGameModels; + + + qboolean showCampaignBriefing; + qboolean showGameView; + qboolean showFireteamMenu; + + char spawnPoints[MAX_SPAWNPOINTS][MAX_SPAWNDESC]; + vec3_t spawnCoordsUntransformed[MAX_SPAWNPOINTS]; + vec3_t spawnCoords[MAX_SPAWNPOINTS]; + team_t spawnTeams[MAX_SPAWNPOINTS]; + team_t spawnTeams_old[MAX_SPAWNPOINTS]; + int spawnTeams_changeTime[MAX_SPAWNPOINTS]; + int spawnPlayerCounts[MAX_SPAWNPOINTS]; + int spawnCount; + int selectedSpawnPoint; + + cg_string_t aStringPool[MAX_STRINGS]; + int demohelpWindow; + cg_window_t *motdWindow; + cg_window_t *msgWstatsWindow; + cg_window_t *msgWtopshotsWindow; + int mv_cnt; // Number of active MV windows + int mvClientList; // Cached client listing of who is merged + cg_window_t *mvCurrentActive; // Client ID of current active window (-1 = none) + cg_window_t *mvCurrentMainview; // Client ID used in the main display (should always be set if mv_cnt > 0) + cg_mvinfo_t mvOverlay[MAX_MVCLIENTS]; // Cached info for MV overlay + int mvTeamList[TEAM_NUM_TEAMS][MAX_MVCLIENTS]; + int mvTotalClients; // Total # of clients available for MV processing + int mvTotalTeam[TEAM_NUM_TEAMS]; + refdef_t *refdef_current; // Handling of some drawing elements for MV + qboolean showStats; + int spechelpWindow; + int statsRequestTime; + cg_window_t *statsWindow; + int topshotsRequestTime; + cg_window_t *topshotsWindow; + cg_window_t *windowCurrent; // Current window to update.. a bit of a hack :p + cg_windowHandler_t winHandler; + vec4_t xhairColor; + vec4_t xhairColorAlt; + + // Arnout: allow overriding of countdown sounds + char fiveMinuteSound_g[MAX_QPATH]; + char fiveMinuteSound_a[MAX_QPATH]; + char twoMinuteSound_g[MAX_QPATH]; + char twoMinuteSound_a[MAX_QPATH]; + char thirtySecondSound_g[MAX_QPATH]; + char thirtySecondSound_a[MAX_QPATH]; + + pmoveExt_t pmext; + + int numOIDtriggers2; + char oidTriggerInfoAllies[MAX_OID_TRIGGERS][256]; + char oidTriggerInfoAxis[MAX_OID_TRIGGERS][256]; + + int ltChargeTime[2]; + int soldierChargeTime[2]; + int engineerChargeTime[2]; + int medicChargeTime[2]; + int covertopsChargeTime[2]; + // START xkan, 8/29/2002 + // which bots are currently selected + int selectedBotClientNumber[MAX_NUM_BUDDY]; + // END xkan, 8/29/2002 + int binocZoomTime; + int limboEndCinematicTime; + int proneMovingTime; + fireteamData_t fireTeams[32]; + + // TAT 10/23/2002 + // For the bot hud, we keep a bit mask for which bot_action icons to show + int botMenuIcons; + // And we need to know which one is the selected one + int botSelectedCommand; + + // START Mad Doc - TDF + int orderFade; + int orderTime; + // END Mad Doc - TDF + + centity_t *satchelCharge; + + playerState_t backupStates[MAX_BACKUP_STATES]; + int backupStateTop; + int backupStateTail; + int lastPredictedCommand; + int lastPhysicsTime; + + qboolean skyboxEnabled; + vec3_t skyboxViewOrg; + vec_t skyboxViewFov; + + vec3_t tankflashorg; + + qboolean editingSpeakers; + + qboolean serverRespawning; + + // mortar hud + vec2_t mortarFireAngles; + int mortarImpactTime; + vec3_t mortarImpactPos; + qboolean mortarImpactOutOfMap; + + // artillery requests + vec3_t artilleryRequestPos[MAX_CLIENTS]; + int artilleryRequestTime[MAX_CLIENTS]; + + soundScript_t* bufferSoundScripts[MAX_BUFFERED_SOUNDSCRIPTS]; + int bufferedSoundScriptEndTime; + int numbufferedSoundScripts; + + char objMapDescription_Axis[384]; + char objMapDescription_Allied[384]; + char objMapDescription_Neutral[384]; + char objDescription_Axis[MAX_OBJECTIVES][256]; + char objDescription_Allied[MAX_OBJECTIVES][256]; + + int waterundertime; +} cg_t; + +#define NUM_FUNNEL_SPRITES 21 + +#define MAX_LOCKER_DEBRIS 5 + +// all of the model, shader, and sound references that are +// loaded at gamestate time are stored in cgMedia_t +// Other media that can be tied to clients, weapons, or items are +// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t +typedef struct { + qhandle_t charsetShader; + // JOSEPH 4-17-00 + qhandle_t menucharsetShader; + // END JOSEPH + qhandle_t charsetProp; + qhandle_t charsetPropGlow; + qhandle_t charsetPropB; + qhandle_t whiteShader; + + qhandle_t armorModel; + +// JPW NERVE + qhandle_t hudSprintBar; + qhandle_t hudAxisHelmet; + qhandle_t hudAlliedHelmet; + qhandle_t redColorBar; + qhandle_t blueColorBar; +// jpw + qhandle_t teamStatusBar; + + qhandle_t deferShader; + + // gib explosions + qhandle_t gibAbdomen; + qhandle_t gibArm; + qhandle_t gibChest; + qhandle_t gibFist; + qhandle_t gibFoot; + qhandle_t gibForearm; + qhandle_t gibIntestine; + qhandle_t gibLeg; + qhandle_t gibSkull; + qhandle_t gibBrain; + + // debris + qhandle_t debBlock[6]; + qhandle_t debRock[3]; + qhandle_t debFabric[3]; + qhandle_t debWood[6]; + + qhandle_t targetEffectExplosionShader; + + qhandle_t machinegunBrassModel; + qhandle_t panzerfaustBrassModel; //----(SA) added + + // Rafael + qhandle_t smallgunBrassModel; + + qhandle_t shotgunBrassModel; + + qhandle_t railRingsShader; + qhandle_t railCoreShader; + qhandle_t ropeShader; + + qhandle_t lightningShader; + + qhandle_t friendShader; + + qhandle_t spawnInvincibleShader; + qhandle_t scoreEliminatedShader; + + qhandle_t medicReviveShader; + qhandle_t voiceChatShader; + qhandle_t balloonShader; + qhandle_t objectiveShader; + + qhandle_t vehicleShader; + qhandle_t destroyShader; + +// qhandle_t selectShader; + qhandle_t viewBloodShader; + qhandle_t tracerShader; + qhandle_t crosshairShader[NUM_CROSSHAIRS]; + qhandle_t lagometerShader; + qhandle_t backTileShader; + qhandle_t noammoShader; + + qhandle_t reticleShader; + qhandle_t reticleShaderSimple; + qhandle_t snooperShader; +// qhandle_t snooperShaderSimple; + qhandle_t binocShader; + qhandle_t binocShaderSimple; +// JPW NERVE + qhandle_t fleshSmokePuffShader; // JPW NERVE for bullet hit flesh smoke puffs + qhandle_t nerveTestShader; + qhandle_t idTestShader; + qhandle_t hud1Shader; + qhandle_t hud2Shader; + qhandle_t hud3Shader; + qhandle_t hud4Shader; + qhandle_t hud5Shader; +// jpw + qhandle_t smokePuffShader; + qhandle_t smokePuffRageProShader; + qhandle_t shotgunSmokePuffShader; + qhandle_t waterBubbleShader; + qhandle_t bloodTrailShader; + +// qhandle_t nailPuffShader; + +//----(SA) cursor hints + // would be nice to specify these in the menu scripts instead of permanent handles... + qhandle_t usableHintShader; + qhandle_t notUsableHintShader; + qhandle_t doorHintShader; + qhandle_t doorRotateHintShader; + qhandle_t doorLockHintShader; + qhandle_t doorRotateLockHintShader; + qhandle_t mg42HintShader; + qhandle_t breakableHintShader; + qhandle_t chairHintShader; + qhandle_t alarmHintShader; + qhandle_t healthHintShader; + qhandle_t treasureHintShader; + qhandle_t knifeHintShader; + qhandle_t ladderHintShader; + qhandle_t buttonHintShader; + qhandle_t waterHintShader; + qhandle_t cautionHintShader; + qhandle_t dangerHintShader; + qhandle_t secretHintShader; + qhandle_t qeustionHintShader; + qhandle_t exclamationHintShader; + qhandle_t clipboardHintShader; + qhandle_t weaponHintShader; + qhandle_t ammoHintShader; + qhandle_t armorHintShader; + qhandle_t powerupHintShader; + qhandle_t holdableHintShader; + qhandle_t inventoryHintShader; + + qhandle_t hintPlrFriendShader; + qhandle_t hintPlrNeutralShader; + qhandle_t hintPlrEnemyShader; + qhandle_t hintPlrUnknownShader; + + // DHM - Nerve :: Multiplayer hints + qhandle_t buildHintShader; + qhandle_t disarmHintShader; + qhandle_t reviveHintShader; + qhandle_t dynamiteHintShader; + // dhm - end + + + qhandle_t tankHintShader; + qhandle_t satchelchargeHintShader; + qhandle_t uniformHintShader; + qhandle_t waypointAttackShader; + qhandle_t waypointDefendShader; + qhandle_t waypointRegroupShader; + // TAT 8/29/2002 - a shader for the bot indicator + qhandle_t waypointBotShader; + // for a queued bot order (Listen Up/Go Go Go) + qhandle_t waypointBotQueuedShader; + qhandle_t waypointCompassAttackShader; + qhandle_t waypointCompassDefendShader; + qhandle_t waypointCompassRegroupShader; + qhandle_t commandCentreWoodShader; + qhandle_t commandCentreMapShader[MAX_COMMANDMAP_LAYERS]; + qhandle_t commandCentreMapShaderTrans[MAX_COMMANDMAP_LAYERS]; + qhandle_t commandCentreAutomapShader[MAX_COMMANDMAP_LAYERS]; + qhandle_t commandCentreAutomapMaskShader; + qhandle_t commandCentreAutomapBorderShader; + qhandle_t commandCentreAutomapBorder2Shader; + qhandle_t commandCentreAutomapCornerShader; + qhandle_t commandCentreAxisMineShader; + qhandle_t commandCentreAlliedMineShader; + qhandle_t commandCentreSpawnShader[2]; + + // Mad Doc - TDF + qhandle_t ingameAutomapBackground; + + qhandle_t landmineHintShader; + qhandle_t compassConstructShader; + qhandle_t compassDestroyShader; + qhandle_t buddyShader; + qhandle_t hudBorderVert; + qhandle_t hudBorderVert2; + + qhandle_t waypointMarker; + + qhandle_t slashShader; + qhandle_t compassShader; + qhandle_t compass2Shader; + + // Rafael + qhandle_t snowShader; + qhandle_t oilParticle; + qhandle_t oilSlick; + // done. + + // Rafael - cannon + qhandle_t smokePuffShaderdirty; + qhandle_t smokePuffShaderb1; + qhandle_t smokePuffShaderb2; + qhandle_t smokePuffShaderb3; + qhandle_t smokePuffShaderb4; + qhandle_t smokePuffShaderb5; + // done + + // Rafael - blood pool + qhandle_t bloodPool; + + // Ridah, viewscreen blood animation + qhandle_t viewBloodAni[5]; + qhandle_t viewFlashBlood; + qhandle_t viewFlashFire[16]; + // done + + // Rafael shards + qhandle_t shardGlass1; + qhandle_t shardGlass2; + qhandle_t shardWood1; + qhandle_t shardWood2; + qhandle_t shardMetal1; + qhandle_t shardMetal2; +// qhandle_t shardCeramic1; +// qhandle_t shardCeramic2; + // done + + qhandle_t shardRubble1; + qhandle_t shardRubble2; + qhandle_t shardRubble3; + + + qhandle_t shardJunk[MAX_LOCKER_DEBRIS]; + + qhandle_t numberShaders[11]; + + qhandle_t shadowFootShader; + qhandle_t shadowTorsoShader; + + // wall mark shaders + qhandle_t wakeMarkShader; + qhandle_t wakeMarkShaderAnim; + qhandle_t bloodMarkShaders[5]; + qhandle_t bloodDotShaders[5]; + qhandle_t bulletMarkShader; + qhandle_t bulletMarkShaderMetal; + qhandle_t bulletMarkShaderWood; + qhandle_t bulletMarkShaderGlass; + qhandle_t burnMarkShader; + + qhandle_t flamebarrel; + qhandle_t mg42muzzleflash; + + qhandle_t waterSplashModel; + qhandle_t waterSplashShader; + + qhandle_t thirdPersonBinocModel; //----(SA) added + + // weapon effect shaders + qhandle_t railExplosionShader; + qhandle_t bulletExplosionShader; + qhandle_t rocketExplosionShader; + qhandle_t grenadeExplosionShader; + qhandle_t bfgExplosionShader; + qhandle_t bloodExplosionShader; + + // special effects models + qhandle_t teleportEffectModel; + qhandle_t teleportEffectShader; + + // Ridah + qhandle_t bloodCloudShader; + qhandle_t sparkParticleShader; + qhandle_t smokeTrailShader; + qhandle_t fireTrailShader; + //qhandle_t lightningBoltShader; + qhandle_t flamethrowerFireStream; + qhandle_t flamethrowerBlueStream; + qhandle_t flamethrowerFuelStream; + qhandle_t flamethrowerFuelShader; + qhandle_t onFireShader, onFireShader2; + qhandle_t viewFadeBlack; + qhandle_t sparkFlareShader; + qhandle_t funnelFireShader[NUM_FUNNEL_SPRITES]; + qhandle_t spotLightShader; + qhandle_t spotLightBeamShader; + qhandle_t bulletParticleTrailShader; + qhandle_t smokeParticleShader; + + // DHM - Nerve :: bullet hitting dirt + qhandle_t dirtParticle1Shader; + qhandle_t dirtParticle2Shader; + qhandle_t dirtParticle3Shader; + + qhandle_t genericConstructionShader; + //qhandle_t genericConstructionShaderBrush; + //qhandle_t genericConstructionShaderModel; + qhandle_t alliedUniformShader; + qhandle_t axisUniformShader; + + sfxHandle_t sfx_artilleryExp[3]; + sfxHandle_t sfx_artilleryDist; + + sfxHandle_t sfx_airstrikeExp[3]; + sfxHandle_t sfx_airstrikeDist; + + // sounds + sfxHandle_t noFireUnderwater; + sfxHandle_t selectSound; + sfxHandle_t landHurt; + + sfxHandle_t footsteps[FOOTSTEP_TOTAL][4]; + sfxHandle_t sfx_rockexp; + sfxHandle_t sfx_rockexpDist; + sfxHandle_t sfx_rockexpWater; + sfxHandle_t sfx_satchelexp; + sfxHandle_t sfx_satchelexpDist; + sfxHandle_t sfx_landmineexp; + sfxHandle_t sfx_landmineexpDist; + sfxHandle_t sfx_mortarexp[4]; + sfxHandle_t sfx_mortarexpDist; + sfxHandle_t sfx_grenexp; + sfxHandle_t sfx_grenexpDist; + sfxHandle_t sfx_brassSound[BRASSSOUND_MAX][3]; + sfxHandle_t sfx_rubbleBounce[3]; + + sfxHandle_t sfx_bullet_fleshhit[5]; + sfxHandle_t sfx_bullet_metalhit[5]; + sfxHandle_t sfx_bullet_woodhit[5]; + sfxHandle_t sfx_bullet_glasshit[5]; + sfxHandle_t sfx_bullet_stonehit[5]; + sfxHandle_t sfx_bullet_waterhit[5]; + + sfxHandle_t sfx_dynamiteexp; + sfxHandle_t sfx_dynamiteexpDist; + sfxHandle_t sfx_spearhit; + sfxHandle_t sfx_knifehit[5]; + sfxHandle_t gibSound; + sfxHandle_t noAmmoSound; + sfxHandle_t landSound[FOOTSTEP_TOTAL]; + + sfxHandle_t fiveMinuteSound_g, fiveMinuteSound_a; + sfxHandle_t twoMinuteSound_g, twoMinuteSound_a; + sfxHandle_t thirtySecondSound_g, thirtySecondSound_a; + + sfxHandle_t watrInSound; + sfxHandle_t watrOutSound; + sfxHandle_t watrUnSound; + sfxHandle_t watrGaspSound; + + sfxHandle_t underWaterSound; + sfxHandle_t fireSound; + sfxHandle_t waterSound; + + sfxHandle_t grenadePulseSound4; + sfxHandle_t grenadePulseSound3; + sfxHandle_t grenadePulseSound2; + sfxHandle_t grenadePulseSound1; +// sfxHandle_t sparkSounds; + + // Ridah + sfxHandle_t flameSound; + sfxHandle_t flameBlowSound; + sfxHandle_t flameStartSound; + sfxHandle_t flameStreamSound; + sfxHandle_t flameCrackSound; + sfxHandle_t boneBounceSound; + + //sfxHandle_t grenadebounce1; + //sfxHandle_t grenadebounce2; + sfxHandle_t grenadebounce[FOOTSTEP_TOTAL][2]; + + sfxHandle_t dynamitebounce1; //----(SA) added + sfxHandle_t landminebounce1; + + sfxHandle_t fkickwall; + sfxHandle_t fkickflesh; + + sfxHandle_t fkickmiss; + + int bulletHitFleshScript; + + sfxHandle_t satchelbounce1; + + qhandle_t cursor; + qhandle_t selectCursor; + qhandle_t sizeCursor; + + sfxHandle_t uniformPickup; + sfxHandle_t minePrimedSound; + sfxHandle_t buildSound[4]; + sfxHandle_t buildDecayedSound; + + sfxHandle_t sndLimboSelect; + sfxHandle_t sndLimboFocus; + sfxHandle_t sndLimboFilter; + sfxHandle_t sndLimboCancel; + + sfxHandle_t sndRankUp; + sfxHandle_t sndSkillUp; + + sfxHandle_t sndMedicCall[2]; + + qhandle_t ccStamps[2]; + qhandle_t ccFilterPics[10]; + qhandle_t ccFilterBackOn; + qhandle_t ccFilterBackOff; + qhandle_t ccPaper; + qhandle_t ccPaperConsole; + qhandle_t ccBars[3]; + qhandle_t ccFlags[3]; + qhandle_t ccLeather; + //qhandle_t ccArrow; + qhandle_t ccPlayerHighlight; + qhandle_t ccConstructIcon[2]; + qhandle_t ccCmdPost[2]; + qhandle_t ccDestructIcon[3][2]; + qhandle_t ccTankIcon; + qhandle_t skillPics[SK_NUM_SKILLS]; + qhandle_t ccMortarHit; + qhandle_t ccMortarTarget; + qhandle_t ccMortarTargetArrow; + + qhandle_t currentSquadBackground; + qhandle_t SPTeamOverlayUnitBackground; + qhandle_t SPTeamOverlayUnitSelected; + qhandle_t SPTeamOverlayBotOrders[BOT_ACTION_MAX]; + qhandle_t SPTeamOverlayBotOrdersBkg; + qhandle_t SPPlayerInfoSpecialIcon; + qhandle_t SPPlayerInfoHealthIcon; + qhandle_t SPPlayerInfoStaminaIcon; + qhandle_t SPPlayerInfoAmmoIcon; + + // Gordon: for commandmap + qhandle_t medicIcon; + + qhandle_t hWeaponSnd; + qhandle_t hWeaponEchoSnd; + qhandle_t hWeaponHeatSnd; + + qhandle_t hWeaponSnd_2; + qhandle_t hWeaponEchoSnd_2; + qhandle_t hWeaponHeatSnd_2; + +// qhandle_t hflakWeaponSnd; + + qhandle_t hMountedMG42Base; // trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42nestbase.md3" ); + qhandle_t hMountedMG42Nest; // trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42nest.md3" ); + qhandle_t hMountedMG42; // trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42.md3" ); + qhandle_t hMountedBrowning; + qhandle_t hMountedFPMG42; + qhandle_t hMountedFPBrowning; + + // Gordon: medals + qhandle_t medals[SK_NUM_SKILLS]; + qhandle_t medal_back; + + // Gordon: new limbo stuff + fontInfo_t limboFont1; + fontInfo_t limboFont1_lo; + fontInfo_t limboFont2; + qhandle_t limboNumber_roll; + qhandle_t limboNumber_back; + qhandle_t limboStar_roll; + qhandle_t limboStar_back; + qhandle_t limboWeaponNumber_off; + qhandle_t limboWeaponNumber_on; + qhandle_t limboWeaponCard; + qhandle_t limboWeaponCardSurroundH; + qhandle_t limboWeaponCardSurroundV; + qhandle_t limboWeaponCardSurroundC; + qhandle_t limboWeaponCardOOS; + qhandle_t limboLight_on; + qhandle_t limboLight_on2; + qhandle_t limboLight_off; + + qhandle_t limboClassButtons[NUM_PLAYER_CLASSES]; + //qhandle_t limboClassButtonBack; + + qhandle_t limboClassButton2Back_on; + qhandle_t limboClassButton2Back_off; + qhandle_t limboClassButton2Wedge_on; + qhandle_t limboClassButton2Wedge_off; + qhandle_t limboClassButtons2[NUM_PLAYER_CLASSES]; + +// skill_back_on +// skill_back_off +// skill_4pieces +// skill_glass_top_layer +// skill_testicon + + qhandle_t limboTeamButtonBack_on; + qhandle_t limboTeamButtonBack_off; + qhandle_t limboTeamButtonAllies; + qhandle_t limboTeamButtonAxis; + qhandle_t limboTeamButtonSpec; + qhandle_t limboBlendThingy; + qhandle_t limboWeaponBlendThingy; + qhandle_t limboSkillsLW; + qhandle_t limboSkillsBS; + //qhandle_t limboCursor_on; + //qhandle_t limboCursor_off; + qhandle_t limboCounterBorder; + qhandle_t limboWeaponCard1; + qhandle_t limboWeaponCard2; + qhandle_t limboWeaponCardArrow; + qhandle_t limboObjectiveBack[3]; + qhandle_t limboClassBar; + qhandle_t limboBriefingButtonOn; + qhandle_t limboBriefingButtonOff; + qhandle_t limboBriefingButtonStopOn; + qhandle_t limboBriefingButtonStopOff; + + qhandle_t limboSpectator; + qhandle_t limboRadioBroadcast; + + qhandle_t cursorIcon; + + + + qhandle_t hudPowerIcon; + qhandle_t hudSprintIcon; + qhandle_t hudHealthIcon; + + qhandle_t pmImages[PM_NUM_TYPES]; + qhandle_t pmImageAlliesConstruct; + qhandle_t pmImageAxisConstruct; + qhandle_t pmImageAlliesMine; + qhandle_t pmImageAxisMine; + qhandle_t hintKey; + + qhandle_t hudDamagedStates[4]; + + qhandle_t browningIcon; + + qhandle_t axisFlag; + qhandle_t alliedFlag; + + qhandle_t disconnectIcon; + + qhandle_t fireteamicons[6]; +} cgMedia_t; + +typedef struct { + char lmsdescription[1024]; + char description[1024]; + char axiswintext[1024]; + char alliedwintext[1024]; + char longname[128]; + vec2_t mappos; +} arenaInfo_t; + +typedef struct { + char campaignDescription[2048]; + char campaignName[128]; + char mapnames[MAX_MAPS_PER_CAMPAIGN][MAX_QPATH]; + vec2_t mappos[MAX_MAPS_PER_CAMPAIGN]; + arenaInfo_t arenas[MAX_MAPS_PER_CAMPAIGN]; + int mapCount; + int current; + vec2_t mapTC[2]; +} cg_campaignInfo_t; + +#define MAX_COMMAND_INFO MAX_CLIENTS + +#define MAX_STATIC_GAMEMODELS 1024 + +typedef struct cg_gamemodel_s { + qhandle_t model; + vec3_t org; + vec3_t axes[3]; + vec_t radius; +} cg_gamemodel_t; + +typedef struct cg_weaponstats_s { + int numKills; + int numHits; + int numShots; +} cg_weaponstats_t; + +typedef struct { + char strWS[WS_MAX][MAX_STRING_TOKENS]; + char strExtra[2][MAX_STRING_TOKENS]; + char strRank[MAX_STRING_TOKENS]; + char strSkillz[SK_NUM_SKILLS][MAX_STRING_TOKENS]; + int cWeapons; + int cSkills; + qboolean fHasStats; + int nClientID; + int nRounds; + int fadeTime; + int show; + int requestTime; +} gameStats_t; + +typedef struct { + char strWS[WS_MAX * 2][MAX_STRING_TOKENS]; + int cWeapons; + int fadeTime; + int show; + int requestTime; +} topshotStats_t; + +typedef struct oidInfo_s { + int spawnflags; + qhandle_t customimageallies; + qhandle_t customimageaxis; + int entityNum; + int objflags; + char name[MAX_QPATH]; + vec3_t origin; +} oidInfo_t; + +#define NUM_ENDGAME_AWARDS 14 + + +// The client game static (cgs) structure hold everything +// loaded or calculated from the gamestate. It will NOT +// be cleared when a tournement restart is done, allowing +// all clients to begin playing instantly +typedef struct { + gameState_t gameState; // gamestate from server + glconfig_t glconfig; // rendering configuration + float screenXScale; // derived from glconfig + float screenYScale; + float screenXBias; + + int serverCommandSequence; // reliable command stream counter + int processedSnapshotNum; // the number of snapshots cgame has requested + + qboolean localServer; // detected on startup by checking sv_running + + // parsed from serverinfo + gametype_t gametype; + int antilag; + + float timelimit; // NERVE - SMF - made this a float + int maxclients; + char mapname[MAX_QPATH]; + char rawmapname[MAX_QPATH]; + char redTeam[MAX_QPATH]; // A team + char blueTeam[MAX_QPATH]; // B team + float weaponRestrictions; + + int voteTime; + int voteYes; + int voteNo; + qboolean voteModified; // beep whenever changed + char voteString[MAX_STRING_TOKENS]; + + int teamVoteTime[2]; + int teamVoteYes[2]; + int teamVoteNo[2]; + qboolean teamVoteModified[2]; // beep whenever changed + char teamVoteString[2][MAX_STRING_TOKENS]; + + int levelStartTime; + int intermissionStartTime; + + // + // locally derived information from gamestate + // + qhandle_t gameModels[MAX_MODELS]; + char gameShaderNames[MAX_CS_SHADERS][MAX_QPATH]; + qhandle_t gameShaders[MAX_CS_SHADERS]; + qhandle_t gameModelSkins[MAX_MODELS]; + bg_character_t *gameCharacters[MAX_CHARACTERS]; + sfxHandle_t gameSounds[MAX_SOUNDS]; + + int numInlineModels; + qhandle_t inlineDrawModel[MAX_MODELS]; + vec3_t inlineModelMidpoints[MAX_MODELS]; + + clientInfo_t clientinfo[MAX_CLIENTS]; + + // teamchat width is *3 because of embedded color codes + char teamChatMsgs[TEAMCHAT_HEIGHT][TEAMCHAT_WIDTH * 3 + 1]; + int teamChatMsgTimes[TEAMCHAT_HEIGHT]; + team_t teamChatMsgTeams[TEAMCHAT_HEIGHT]; + int teamChatPos; + int teamLastChatPos; + + // New notify mechanism for obits + char notifyMsgs[NOTIFY_HEIGHT][NOTIFY_WIDTH * 3 + 1]; + int notifyMsgTimes[NOTIFY_HEIGHT]; + int notifyPos; + int notifyLastPos; + + int cursorX; + int cursorY; + qboolean eventHandling; + qboolean mouseCaptured; + qboolean sizingHud; + void *capturedItem; + qhandle_t activeCursor; + + // screen fading + float fadeAlpha, fadeAlphaCurrent; + int fadeStartTime; + int fadeDuration; + + // media + cgMedia_t media; + + // player/AI model scripting (client repository) + animScriptData_t animScriptData; + + int currentVoiceClient; + int currentRound; + float nextTimeLimit; + int minclients; + gamestate_t gamestate; + char *currentCampaign; + int currentCampaignMap; + + int complaintClient; // DHM - Nerve + int complaintEndTime; // DHM - Nerve + float smokeWindDir; // JPW NERVE for smoke puffs & wind (arty, airstrikes, bullet impacts) + + playerStats_t playerStats; + int numOIDtriggers; + int teamobjectiveStats[MAX_OID_TRIGGERS]; + + qboolean campaignInfoLoaded; + cg_campaignInfo_t campaignData; + + qboolean arenaInfoLoaded; + arenaInfo_t arenaData; + + centity_t * gameManager; + + int ccLayers; + int ccLayerCeils[MAX_COMMANDMAP_LAYERS]; + float ccZoomFactor; + + int invitationClient; + int invitationEndTime; + + int applicationClient; + int applicationEndTime; + + int propositionClient; + int propositionClient2; + int propositionEndTime; + + int autoFireteamEndTime; + int autoFireteamNum; + + int autoFireteamCreateEndTime; + int autoFireteamCreateNum; + + int autoFireteamJoinEndTime; + int autoFireteamJoinNum; + + + qboolean autoMapExpanded; + int autoMapExpandTime; + + qboolean autoMapOff; // is automap on or off + + bg_character_t *offscreenCmdr; + + // OSP + int aviDemoRate; // Demo playback recording + int aReinfOffset[TEAM_NUM_TEAMS]; // Team reinforcement offsets + int cursorUpdate; // Timeout for mouse pointer view + fileHandle_t dumpStatsFile; // File to dump stats + char* dumpStatsFileName; // Name of file to dump stats + int dumpStatsTime; // Next stats command that comes back will be written to a logfile + int game_versioninfo; // game base version + gameStats_t gamestats; + topshotStats_t topshots; + qboolean fResize; // MV window "resize" status + qboolean fSelect; // MV window "select" status + qboolean fKeyPressed[256]; // Key status to get around console issues + int timescaleUpdate; // Timescale display for demo playback + int thirdpersonUpdate; + + cg_gamemodel_t miscGameModels[MAX_STATIC_GAMEMODELS]; + + vec2_t ccMenuPos; + qboolean ccMenuShowing; + int ccMenuType; + mapEntityData_t ccMenuEnt; + int ccSelectedLayer; + int ccSelectedObjective; + int ccSelectedTeam; // ( 1 = ALLIES, 0 = AXIS ) + int ccSelectedWeaponNumber; + int ccSelectedClass; + int ccSelectedWeapon; + int ccSelectedWeapon2; + int ccWeaponShots; + int ccWeaponHits; + vec3_t ccPortalPos; + vec3_t ccPortalAngles; + int ccPortalEnt; + int ccFilter; + int ccCurrentCamObjective; + int ccRequestedObjective; + int ccLastObjectiveRequestTime; + + int loadingLatch; // ( 0 = nothing yet, 1 = latched ) + +// qboolean playedLimboMusic; + + int dbSortedClients[MAX_CLIENTS]; + int dbSelectedClient; + + int dbMode; + qboolean dbShowing; + qboolean dbAccuraciesRecieved; + qboolean dbPlayerKillsDeathsRecieved; + qboolean dbWeaponStatsRecieved; + qboolean dbAwardsParsed; + char* dbAwardNames[NUM_ENDGAME_AWARDS]; + team_t dbAwardTeams[NUM_ENDGAME_AWARDS]; + char dbAwardNamesBuffer[1024]; + int dbLastRequestTime; + int dbLastScoreRequest; + int dbPlayerListOffset; + int dbWeaponListOffset; + cg_weaponstats_t dbWeaponStats[WS_MAX]; + int dbChatMode; + + int tdbAxisMapsXP[SK_NUM_SKILLS][MAX_MAPS_PER_CAMPAIGN]; + int tdbAlliedMapsXP[SK_NUM_SKILLS][MAX_MAPS_PER_CAMPAIGN]; + int tdbMapListOffset; + int tdbSelectedMap; + + int ftMenuPos; + int ftMenuMode; + int ftMenuModeEx; + + qboolean limboLoadoutSelected; + qboolean limboLoadoutModified; + + oidInfo_t oidInfo[MAX_OID_TRIGGERS]; + + qboolean initing; +} cgs_t; + +//============================================================================== + +extern cgs_t cgs; +extern cg_t cg; +extern centity_t cg_entities[MAX_GENTITIES]; +extern weaponInfo_t cg_weapons[MAX_WEAPONS]; +extern itemInfo_t cg_items[MAX_ITEMS]; +extern markPoly_t cg_markPolys[MAX_MARK_POLYS]; + +extern vmCvar_t cg_centertime; +extern vmCvar_t cg_runpitch; +extern vmCvar_t cg_runroll; +extern vmCvar_t cg_bobup; +extern vmCvar_t cg_bobpitch; +extern vmCvar_t cg_bobroll; +extern vmCvar_t cg_bobyaw; +extern vmCvar_t cg_swingSpeed; +extern vmCvar_t cg_shadows; +extern vmCvar_t cg_gibs; +extern vmCvar_t cg_draw2D; +extern vmCvar_t cg_drawFPS; +extern vmCvar_t cg_drawSnapshot; +extern vmCvar_t cg_drawCrosshair; +extern vmCvar_t cg_drawCrosshairNames; +extern vmCvar_t cg_drawCrosshairPickups; +extern vmCvar_t cg_useWeapsForZoom; +extern vmCvar_t cg_weaponCycleDelay; //----(SA) added +extern vmCvar_t cg_cycleAllWeaps; +extern vmCvar_t cg_drawTeamOverlay; +extern vmCvar_t cg_crosshairX; +extern vmCvar_t cg_crosshairY; +extern vmCvar_t cg_crosshairSize; +extern vmCvar_t cg_crosshairHealth; +extern vmCvar_t cg_drawStatus; +extern vmCvar_t cg_animSpeed; +extern vmCvar_t cg_debugAnim; +extern vmCvar_t cg_debugPosition; +extern vmCvar_t cg_debugEvents; +extern vmCvar_t cg_drawSpreadScale; +extern vmCvar_t cg_railTrailTime; +extern vmCvar_t cg_errorDecay; +extern vmCvar_t cg_nopredict; +extern vmCvar_t cg_noPlayerAnims; +extern vmCvar_t cg_showmiss; +extern vmCvar_t cg_footsteps; +extern vmCvar_t cg_markTime; +extern vmCvar_t cg_brassTime; +extern vmCvar_t cg_gun_frame; +extern vmCvar_t cg_gun_x; +extern vmCvar_t cg_gun_y; +extern vmCvar_t cg_gun_z; +extern vmCvar_t cg_drawGun; +extern vmCvar_t cg_cursorHints; +extern vmCvar_t cg_letterbox; //----(SA) added +extern vmCvar_t cg_tracerChance; +extern vmCvar_t cg_tracerWidth; +extern vmCvar_t cg_tracerLength; +extern vmCvar_t cg_tracerSpeed; +extern vmCvar_t cg_autoswitch; +extern vmCvar_t cg_ignore; +extern vmCvar_t cg_fov; +extern vmCvar_t cg_zoomFov; +extern vmCvar_t cg_zoomDefaultBinoc; +extern vmCvar_t cg_zoomDefaultSniper; +extern vmCvar_t cg_zoomDefaultFG; +extern vmCvar_t cg_zoomDefaultSnooper; +extern vmCvar_t cg_zoomStepBinoc; +extern vmCvar_t cg_zoomStepSniper; +extern vmCvar_t cg_zoomStepSnooper; +extern vmCvar_t cg_zoomStepFG; +extern vmCvar_t cg_thirdPersonRange; +extern vmCvar_t cg_thirdPersonAngle; +extern vmCvar_t cg_thirdPerson; +extern vmCvar_t cg_stereoSeparation; +extern vmCvar_t cg_lagometer; +#ifdef ALLOW_GSYNC +extern vmCvar_t cg_synchronousClients; +#endif // ALLOW_GSYNC +extern vmCvar_t cg_teamChatTime; +extern vmCvar_t cg_teamChatHeight; +extern vmCvar_t cg_stats; +extern vmCvar_t cg_forceModel; +extern vmCvar_t cg_coronafardist; +extern vmCvar_t cg_coronas; +extern vmCvar_t cg_buildScript; +extern vmCvar_t cg_paused; +extern vmCvar_t cg_blood; +extern vmCvar_t cg_predictItems; +extern vmCvar_t cg_deferPlayers; +extern vmCvar_t cg_teamChatsOnly; +extern vmCvar_t cg_noVoiceChats; // NERVE - SMF +extern vmCvar_t cg_noVoiceText; // NERVE - SMF +extern vmCvar_t cg_enableBreath; +extern vmCvar_t cg_autoactivate; +extern vmCvar_t cg_smoothClients; +extern vmCvar_t pmove_fixed; +extern vmCvar_t pmove_msec; + +extern vmCvar_t cg_cameraOrbit; +extern vmCvar_t cg_cameraOrbitDelay; +extern vmCvar_t cg_timescaleFadeEnd; +extern vmCvar_t cg_timescaleFadeSpeed; +extern vmCvar_t cg_timescale; +extern vmCvar_t cg_cameraMode; +extern vmCvar_t cg_smallFont; +extern vmCvar_t cg_bigFont; +extern vmCvar_t cg_noTaunt; // NERVE - SMF +extern vmCvar_t cg_voiceSpriteTime; // DHM - Nerve + +extern vmCvar_t cg_blinktime; //----(SA) added + +// Rafael - particle switch +extern vmCvar_t cg_wolfparticles; +// done + +// Ridah +extern vmCvar_t cg_gameType; +extern vmCvar_t cg_bloodTime; +extern vmCvar_t cg_norender; +extern vmCvar_t cg_skybox; + +// Rafael gameskill +//extern vmCvar_t cg_gameSkill; +// done + +// JPW NERVE +extern vmCvar_t cg_redlimbotime; +extern vmCvar_t cg_bluelimbotime; +// jpw + +extern vmCvar_t cg_movespeed; + +extern vmCvar_t cg_animState; + +extern vmCvar_t cg_drawCompass; +extern vmCvar_t cg_drawNotifyText; +extern vmCvar_t cg_quickMessageAlt; +extern vmCvar_t cg_popupLimboMenu; +extern vmCvar_t cg_descriptiveText; +// -NERVE - SMF + +extern vmCvar_t cg_antilag; + +extern vmCvar_t developer; + +// OSP +extern vmCvar_t authLevel; +extern vmCvar_t cf_wstats; +extern vmCvar_t cf_wtopshots; +//extern vmCvar_t cg_announcer; +extern vmCvar_t cg_autoAction; +extern vmCvar_t cg_autoReload; +extern vmCvar_t cg_bloodDamageBlend; +extern vmCvar_t cg_bloodFlash; +extern vmCvar_t cg_complaintPopUp; +extern vmCvar_t cg_crosshairAlpha; +extern vmCvar_t cg_crosshairAlphaAlt; +extern vmCvar_t cg_crosshairColor; +extern vmCvar_t cg_crosshairColorAlt; +extern vmCvar_t cg_crosshairPulse; +extern vmCvar_t cg_drawReinforcementTime; +extern vmCvar_t cg_drawWeaponIconFlash; +extern vmCvar_t cg_muzzleFlash; +extern vmCvar_t cg_noAmmoAutoSwitch; +extern vmCvar_t cg_printObjectiveInfo; +extern vmCvar_t cg_specHelp; +extern vmCvar_t cg_specSwing; +extern vmCvar_t cg_uinfo; +extern vmCvar_t cg_useScreenshotJPEG; +extern vmCvar_t ch_font; +extern vmCvar_t demo_avifpsF1; +extern vmCvar_t demo_avifpsF2; +extern vmCvar_t demo_avifpsF3; +extern vmCvar_t demo_avifpsF4; +extern vmCvar_t demo_avifpsF5; +extern vmCvar_t demo_drawTimeScale; +extern vmCvar_t demo_infoWindow; +extern vmCvar_t mv_sensitivity; +// engine mappings +extern vmCvar_t int_cl_maxpackets; +extern vmCvar_t int_cl_timenudge; +extern vmCvar_t int_m_pitch; +extern vmCvar_t int_sensitivity; +extern vmCvar_t int_ui_blackout; +// -OSP + +extern vmCvar_t cg_rconPassword; +extern vmCvar_t cg_refereePassword; +extern vmCvar_t cg_atmosphericEffects; +// START Mad Doc - TDF +extern vmCvar_t cg_drawRoundTimer; +// END Mad Doc - TDF +extern vmCvar_t cg_debugSkills; +extern vmCvar_t cg_drawFireteamOverlay; +extern vmCvar_t cg_drawSmallPopupIcons; + +#ifdef SAVEGAME_SUPPORT +extern vmCvar_t cg_reloading; +#endif // SAVEGAME_SUPPORT + +// Gordon: some optimization cvars +extern vmCvar_t cg_fastSolids; +extern vmCvar_t cg_instanttapout; + +// bani - demo recording cvars +extern vmCvar_t cl_demorecording; +extern vmCvar_t cl_demofilename; +extern vmCvar_t cl_demooffset; +extern vmCvar_t cl_waverecording; +extern vmCvar_t cl_wavefilename; +extern vmCvar_t cl_waveoffset; +extern vmCvar_t cg_recording_statusline; + +// +// cg_main.c +// +const char *CG_ConfigString( int index ); +int CG_ConfigStringCopy( int index, char* buff, int buffsize ); +const char *CG_Argv( int arg ); + +float CG_Cvar_Get( const char *cvar ); + +char *CG_generateFilename( void ); +int CG_findClientNum( char *s ); +void CG_printConsoleString( char *str ); + +void CG_LoadObjectiveData( void ); + +void QDECL CG_Printf( const char *msg, ... ); +void QDECL CG_Error( const char *msg, ... ); + +void CG_StartMusic( void ); +void CG_QueueMusic( void ); + +void CG_UpdateCvars( void ); + +int CG_CrosshairPlayer( void ); +int CG_LastAttacker( void ); +void CG_LoadMenus( const char *menuFile ); +void CG_KeyEvent( int key, qboolean down ); +void CG_MouseEvent( int x, int y ); +void CG_EventHandling( int type, qboolean fForced ); + +qboolean CG_GetTag( int clientNum, char *tagname, orientation_t * or ); +qboolean CG_GetWeaponTag( int clientNum, char *tagname, orientation_t * or ); + +// +// cg_view.c +// +void CG_TestModel_f( void ); +void CG_TestGun_f( void ); +void CG_TestModelNextFrame_f( void ); +void CG_TestModelPrevFrame_f( void ); +void CG_TestModelNextSkin_f( void ); +void CG_TestModelPrevSkin_f( void ); +void CG_ZoomDown_f( void ); +void CG_ZoomIn_f( void ); +void CG_ZoomOut_f( void ); +void CG_ZoomUp_f( void ); + +void CG_SetupFrustum( void ); +qboolean CG_CullPoint( vec3_t pt ); +qboolean CG_CullPointAndRadius( const vec3_t pt, vec_t radius ); + +void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ); +void CG_DrawSkyBoxPortal( qboolean fLocalView ); +void CG_Concussive( centity_t *cent ); + +void CG_Letterbox( float xsize, float ysize, qboolean center ); + +// +// cg_drawtools.c +// +void CG_AdjustFrom640( float *x, float *y, float *w, float *h ); +void CG_FillRect( float x, float y, float width, float height, const float *color ); +void CG_HorizontalPercentBar( float x, float y, float width, float height, float percent ); +void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void CG_DrawPicST( float x, float y, float width, float height, float s0, float t0, float s1, float t1, qhandle_t hShader ); +void CG_DrawRotatedPic( float x, float y, float width, float height, qhandle_t hShader, float angle ); // NERVE - SMF +void CG_DrawChar( int x, int y, int width, int height, int ch ); +void CG_FilledBar( float x, float y, float w, float h, float *startColor, float *endColor, const float *bgColor, float frac, int flags ); +// JOSEPH 10-26-99 +void CG_DrawStretchPic( float x, float y, float width, float height, qhandle_t hShader ); +// END JOSEPH +void CG_DrawString( float x, float y, const char *string, + float charWidth, float charHeight, const float *modulate ); + + +void CG_DrawStringExt( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ); +// JOSEPH 4-17-00 +void CG_DrawStringExt2( int x, int y, const char *string, const float *setColor, + qboolean forceColor, qboolean shadow, int charWidth, int charHeight, int maxChars ); +void CG_DrawStringExt_Shadow( int x, int y, const char *string, const float *setColor, + qboolean forceColor, int shadow, int charWidth, int charHeight, int maxChars ); +// END JOSEPH +void CG_DrawBigString( int x, int y, const char *s, float alpha ); +void CG_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); +void CG_DrawSmallString( int x, int y, const char *s, float alpha ); +void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ); +// JOSEPH 4-25-00 +void CG_DrawBigString2( int x, int y, const char *s, float alpha ); +void CG_DrawBigStringColor2( int x, int y, const char *s, vec4_t color ); +// END JOSEPH +int CG_DrawStrlen( const char *str ); + +float *CG_FadeColor( int startMsec, int totalMsec ); +float *CG_TeamColor( int team ); +void CG_TileClear( void ); +void CG_ColorForHealth( vec4_t hcolor ); +void CG_GetColorForHealth( int health, vec4_t hcolor ); + +void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ); + +// new hud stuff +void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ); +void CG_DrawRect_FixedBorder( float x, float y, float width, float height, int border, const float *color ); +void CG_DrawSides( float x, float y, float w, float h, float size ); +void CG_DrawTopBottom( float x, float y, float w, float h, float size ); +void CG_DrawTopBottom_NoScale( float x, float y, float w, float h, float size ); + +// NERVE - SMF - localization functions +void CG_InitTranslation(); +char* CG_TranslateString( const char *string ); +void CG_SaveTransTable(); +void CG_ReloadTranslation(); +// -NERVE - SMF + +// +// cg_draw.c, cg_newDraw.c +// +extern char cg_fxflags; // JPW NERVE + +void CG_InitStatsDebug( void ); +void CG_StatsDebugAddText( const char *text ); + +void CG_AddLagometerFrameInfo( void ); +void CG_AddLagometerSnapshotInfo( snapshot_t *snap ); +void CG_CenterPrint( const char *str, int y, int charWidth ); +void CG_PriorityCenterPrint( const char *str, int y, int charWidth, int priority ); // NERVE - SMF +void CG_ObjectivePrint( const char *str, int charWidth ); // NERVE - SMF +void CG_DrawActive( stereoFrame_t stereoView ); +void CG_CheckForCursorHints( void ); +void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team ); +void CG_OwnerDraw( float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle ); +void CG_Text_Paint_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ); +void CG_Text_Paint_Centred_Ext( float x, float y, float scalex, float scaley, vec4_t color, const char *text, float adjust, int limit, int style, fontInfo_t* font ); +void CG_Text_Paint( float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ); +void CG_Text_SetActiveFont( int font ); +int CG_Text_Width_Ext( const char *text, float scale, int limit, fontInfo_t* font ); +int CG_Text_Width( const char *text, float scale, int limit ); +int CG_Text_Height_Ext( const char *text, float scale, int limit, fontInfo_t* font ); +int CG_Text_Height( const char *text, float scale, int limit ); +float CG_GetValue( int ownerDraw, int type ); // 'type' is relative or absolute (fractional-'0.5' or absolute- '50' health) +qboolean CG_OwnerDrawVisible( int flags ); +void CG_RunMenuScript( char **args ); +void CG_GetTeamColor( vec4_t *color ); +void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ); +void CG_Text_PaintChar_Ext( float x, float y, float w, float h, float scalex, float scaley, float s, float t, float s2, float t2, qhandle_t hShader ); +void CG_Text_PaintChar( float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader ); +qboolean CG_YourTeamHasFlag(); +qboolean CG_OtherTeamHasFlag(); +void CG_DrawCursorhint( rectDef_t *rect ); +void CG_DrawWeapStability( rectDef_t *rect ); +void CG_DrawWeapHeat( rectDef_t *rect, int align ); +void CG_DrawPlayerWeaponIcon( rectDef_t *rect, qboolean drawHighlighted, int align, vec4_t *refcolor ); +int CG_CalculateReinfTime( qboolean menu ); +float CG_CalculateReinfTime_Float( qboolean menu ); +void CG_Fade( int r, int g, int b, int a, int time, int duration ); + + + + +// +// cg_player.c +// +qboolean CG_EntOnFire( centity_t *cent ); // Ridah +void CG_Player( centity_t *cent ); +void CG_ResetPlayerEntity( centity_t *cent ); +void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, int team, entityState_t *es, const vec3_t fireRiseDir ); +void CG_NewClientInfo( int clientNum ); +sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ); +void CG_ParseTeamXPs( int n ); + + + +// Rafael particles +extern qboolean initparticles; +int CG_NewParticleArea( int num ); + +// +// cg_predict.c +// +void CG_BuildSolidList( void ); +int CG_PointContents( const vec3_t point, int passEntityNum ); +void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ); +void CG_FTTrace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ); +void CG_PredictPlayerState( void ); +//void CG_LoadDeferredPlayers( void ); + + +// +// cg_events.c +// +void CG_CheckEvents( centity_t *cent ); +void CG_EntityEvent( centity_t *cent, vec3_t position ); +void CG_PainEvent( centity_t *cent, int health, qboolean crouching ); +void CG_PrecacheFXSounds( void ); + + +// +// cg_ents.c +// +void CG_SetEntitySoundPosition( centity_t *cent ); +void CG_AddPacketEntities( void ); +void CG_Beam( centity_t *cent ); +void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t outDeltaAngles ); +void CG_AddCEntity( centity_t *cent ); +qboolean CG_AddCEntity_Filter( centity_t* cent ); +qboolean CG_AddLinkedEntity( centity_t *cent, qboolean ignoreframe, int atTime ); +void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, const char *tagName, int startIndex, vec3_t *offset ); +void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, const char *tagName ); + + +// +// cg_weapons.c +// +void CG_LastWeaponUsed_f( void ); //----(SA) added +void CG_NextWeaponInBank_f( void ); //----(SA) added +void CG_PrevWeaponInBank_f( void ); //----(SA) added +void CG_AltWeapon_f( void ); +void CG_NextWeapon_f( void ); +void CG_PrevWeapon_f( void ); +void CG_Weapon_f( void ); +void CG_WeaponBank_f( void ); +qboolean CG_WeaponSelectable( int i ); + +void CG_FinishWeaponChange( int lastweap, int newweap ); + +void CG_RegisterWeapon( int weaponNum, qboolean force ); +void CG_RegisterItemVisuals( int itemNum ); + +void CG_FireWeapon( centity_t *cent ); //----(SA) modified. +//void CG_EndFireWeapon( centity_t *cent, int firemode ); //----(SA) added +void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, int surfaceFlags ); // (SA) modified to send missilehitwall surface parameters + +void CG_MissileHitWallSmall( int weapon, int clientNum, vec3_t origin, vec3_t dir ); +void CG_DrawTracer( vec3_t start, vec3_t finish ); + +// Rafael +void CG_MG42EFX( centity_t *cent ); + +void CG_FLAKEFX( centity_t *cent, int whichgun ); + +void CG_MortarEFX( centity_t *cent ); + +// Ridah +qboolean CG_MonsterUsingWeapon( centity_t *cent, int aiChar, int weaponNum ); + +// Rafael +void CG_MissileHitWall2( int weapon, int clientNum, vec3_t origin, vec3_t dir ); +// done + +void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, int entityNum ); +qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ); +void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum, int otherEntNum2, float waterfraction, int seed ); + +void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end, int type ); //----(SA) added 'type' +void CG_RailTrail2( clientInfo_t *ci, vec3_t start, vec3_t end ); +void CG_GrappleTrail( centity_t *ent, const weaponInfo_t *wi ); +void CG_AddViewWeapon( playerState_t *ps ); +void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent ); + +void CG_OutOfAmmoChange( qboolean allowforceswitch ); + +//----(SA) added to header to access from outside cg_weapons.c +void CG_AddDebris( vec3_t origin, vec3_t dir, int speed, int duration, int count ); +//----(SA) done + +//void CG_ClientDamage( int entnum, int enemynum, int id ); + +// +// cg_marks.c +// +void CG_InitMarkPolys( void ); +void CG_AddMarks( void ); +void CG_ImpactMark( qhandle_t markShader, + vec3_t origin, vec4_t projection, float radius, float orientation, + float r, float g, float b, float a, int lifeTime ); + +// Rafael particles +// +// cg_particles.c +// +void CG_ClearParticles( void ); +void CG_AddParticles( void ); +void CG_ParticleSnow( qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum ); +void CG_ParticleSmoke( qhandle_t pshader, centity_t *cent ); +void CG_AddParticleShrapnel( localEntity_t *le ); +void CG_ParticleSnowFlurry( qhandle_t pshader, centity_t *cent ); +void CG_ParticleBulletDebris( vec3_t org, vec3_t vel, int duration ); +void CG_ParticleDirtBulletDebris( vec3_t org, vec3_t vel, int duration ); // DHM - Nerve +void CG_ParticleDirtBulletDebris_Core( vec3_t org, vec3_t vel, int duration, float width, float height, float alpha, qhandle_t shader ); +void CG_ParticleSparks( vec3_t org, vec3_t vel, int duration, float x, float y, float speed ); +void CG_ParticleDust( centity_t *cent, vec3_t origin, vec3_t dir ); +void CG_ParticleMisc( qhandle_t pshader, vec3_t origin, int size, int duration, float alpha ); + +// Ridah +void CG_ParticleExplosion( char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd, qboolean dlight ); + +// Rafael snow pvs check +void CG_SnowLink( centity_t *cent, qboolean particleOn ); +// done. + +void CG_ParticleImpactSmokePuff( qhandle_t pshader, vec3_t origin ); +void CG_ParticleImpactSmokePuffExtended( qhandle_t pshader, vec3_t origin, int lifetime, int vel, int acc, int maxroll, float alpha, float size ); // (SA) so I can add more parameters without screwing up the one that's there +void CG_Particle_Bleed( qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration ); +void CG_GetBleedOrigin( vec3_t head_origin, vec3_t body_origin, int fleshEntityNum ); +void CG_Particle_OilParticle( qhandle_t pshader, vec3_t origin, vec3_t origin2, int ptime, int snum ); +void CG_Particle_OilSlick( qhandle_t pshader, centity_t *cent ); +void CG_OilSlickRemove( centity_t *cent ); +void CG_ParticleBloodCloudZombie( centity_t *cent, vec3_t origin, vec3_t dir ); +void CG_ParticleBloodCloud( centity_t *cent, vec3_t origin, vec3_t dir ); +// done + +// Ridah, trails +// +// cg_trails.c +// +// rain - usedby for zinx's trail fixes +int CG_AddTrailJunc( int headJuncIndex, void *usedby, qhandle_t shader, int spawnTime, int sType, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth, int flags, vec3_t colorStart, vec3_t colorEnd, float sRatio, float animSpeed ); +int CG_AddSparkJunc( int headJuncIndex, void *usedby, qhandle_t shader, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth ); +int CG_AddSmokeJunc( int headJuncIndex, void *usedby, qhandle_t shader, vec3_t pos, int trailLife, float alpha, float startWidth, float endWidth ); +void CG_AddTrails( void ); +void CG_ClearTrails( void ); +// done. + +// +// cg_sound.c +// + +// Ridah, sound scripting +int CG_SoundScriptPrecache( const char *name ); +int CG_SoundPlaySoundScript( const char *name, vec3_t org, int entnum, qboolean buffer ); +void CG_UpdateBufferedSoundScripts( void ); +// TTimo: prototype must match animScriptData_t::playSound +void CG_SoundPlayIndexedScript( int index, vec3_t org, int entnum ); +void CG_SoundInit( void ); +// done. +void CG_SetViewanglesForSpeakerEditor( void ); +void CG_SpeakerEditorDraw( void ); +void CG_SpeakerEditor_KeyHandling( int key, qboolean down ); +void CG_Debriefing_KeyEvent( int key, qboolean down ); +void CG_SpeakerEditorMouseMove_Handling( int x, int y ); +void CG_ActivateEditSoundMode( void ); +void CG_DeActivateEditSoundMode( void ); +void CG_ModifyEditSpeaker( void ); +void CG_UndoEditSpeaker( void ); +void CG_ToggleActiveOnScriptSpeaker( int index ); +void CG_UnsetActiveOnScriptSpeaker( int index ); +void CG_SetActiveOnScriptSpeaker( int index ); +void CG_AddScriptSpeakers( void ); + +// Ridah, flamethrower +void CG_FireFlameChunks( centity_t *cent, vec3_t origin, vec3_t angles, float speedScale, qboolean firing ); +void CG_InitFlameChunks( void ); +void CG_AddFlameChunks( void ); +void CG_UpdateFlamethrowerSounds( void ); +void CG_FlameDamage( int owner, vec3_t org, float radius ); +// done. + +// +// cg_localents.c +// +void CG_InitLocalEntities( void ); +localEntity_t *CG_AllocLocalEntity( void ); +void CG_AddLocalEntities( void ); + +// +// cg_effects.c +// +int CG_GetOriginForTag( centity_t * cent, refEntity_t * parent, char *tagName, int startIndex, vec3_t org, vec3_t axis[3] ); +localEntity_t *CG_SmokePuff( const vec3_t p, + const vec3_t vel, + float radius, + float r, float g, float b, float a, + float duration, + int startTime, + int fadeInTime, + int leFlags, + qhandle_t hShader ); + +void CG_BubbleTrail( vec3_t start, vec3_t end, float size, float spacing ); +void CG_SpawnEffect( vec3_t org ); +void CG_GibPlayer( centity_t *cent, vec3_t playerOrigin, vec3_t gdir ); +void CG_LoseHat( centity_t *cent, vec3_t dir ); //----(SA) added + +void CG_Bleed( vec3_t origin, int entityNum ); + +localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, + qhandle_t hModel, qhandle_t shader, int msec, + qboolean isSprite ); +// Ridah +void CG_SparklerSparks( vec3_t origin, int count ); +void CG_ClearFlameChunks( void ); +void CG_ProjectedSpotLight( vec3_t start, vec3_t dir ); +// done. + +//----(SA) +void CG_Spotlight( centity_t *cent, float *color, vec3_t start, vec3_t dir, int segs, float range, int startWidth, float coneAngle, int flags ); +#define SL_NOTRACE 0x001 // don't do a trace check for shortening the beam, always draw at full 'range' length +#define SL_NODLIGHT 0x002 // don't put a dlight at the end +#define SL_NOSTARTCAP 0x004 // dont' cap the start circle +#define SL_LOCKTRACETORANGE 0x010 // only trace out as far as the specified range (rather than to max spot range) +#define SL_NOFLARE 0x020 // don't draw a flare when the light is pointing at the camera +#define SL_NOIMPACT 0x040 // don't draw the impact mark +#define SL_LOCKUV 0x080 // lock the texture coordinates at the 'true' length of the requested beam. +#define SL_NOCORE 0x100 // don't draw the center 'core' beam +#define SL_TRACEWORLDONLY 0x200 +//----(SA) done + +void CG_RumbleEfx( float pitch, float yaw ); + +void InitSmokeSprites( void ); +void CG_RenderSmokeGrenadeSmoke( centity_t *cent, const weaponInfo_t *weapon ); +void CG_AddSmokeSprites( void ); + +// +// cg_snapshot.c +// +void CG_ProcessSnapshots( void ); + +// +// cg_spawn.c +// +qboolean CG_SpawnString( const char *key, const char *defaultString, char **out ); +// spawn string returns a temporary reference, you must CopyString() if you want to keep it +qboolean CG_SpawnFloat( const char *key, const char *defaultString, float *out ); +qboolean CG_SpawnInt( const char *key, const char *defaultString, int *out ); +qboolean CG_SpawnVector( const char *key, const char *defaultString, float *out ); +void CG_ParseEntitiesFromString( void ); + +// +// cg_info.c +// +void CG_LoadingString( const char *s ); +//void CG_LoadingItem( int itemNum ); +void CG_LoadingClient( int clientNum ); +void CG_DrawInformation( qboolean forcerefresh ); +void CG_DemoClick( int key, qboolean down ); +void CG_ShowHelp_Off( int *status ); +void CG_ShowHelp_On( int *status ); +qboolean CG_ViewingDraw( void ); + +// +// cg_scoreboard.c +// +qboolean CG_DrawScoreboard( void ); +//void CG_DrawTourneyScoreboard( void ); + +void CG_TransformToCommandMapCoord( float *coord_x, float *coord_y ); + +//qboolean CG_DrawCommandMap( void ); +void CG_CommandCentreClick( int key ); +void CG_DrawAutoMap( void ); + +qboolean CG_DrawLimboMenu( void ); +qboolean CG_DrawObjectivePanel( void ); +qboolean CG_DrawFireTeamMenu( void ); + +qboolean CG_LimboMenuClick( int key ); +qboolean CG_FireTeamClick( int key ); +qboolean CG_ObjectiveMenuClick( int key ); + +void CG_GameViewMenuClick( int key ); +void CG_GetLimboWeaponAnim( const char **torso_anim, const char **legs_anim ); +int CG_GetLimboSelectedWeapon(); + +qboolean CG_DrawMissionBriefing( void ); +void CG_MissionBriefingClick( int key ); + +void CG_LoadRankIcons( void ); +qboolean CG_DrawStatsRanksMedals( void ); +void CG_StatsRanksMedalsClick( int key ); + +typedef struct { + int pendingAnimationTime; + const char* pendingTorsoAnim; + const char* pendingLegsAnim; +} pendingAnimation_t; + +typedef struct { + lerpFrame_t legs; + lerpFrame_t torso; + lerpFrame_t headAnim; + + vec3_t headOrigin; // used for centering talking heads + + vec3_t viewAngles; + vec3_t moveAngles; + + pendingAnimation_t pendingAnimations[4]; + int numPendingAnimations; + + float y, z; + + int teamNum; + int classNum; +} playerInfo_t; + +typedef enum { + ANIM_IDLE, + ANIM_RAISE, +} animType_t; + +qboolean CG_DrawGameView( void ); +void CG_ParseFireteams( void ); +void CG_ParseOIDInfos( void ); +oidInfo_t* CG_OIDInfoForEntityNum( int num ); + +// +// cg_consolecmds.c +// +extern const char *aMonths[12]; +qboolean CG_ConsoleCommand( void ); +void CG_InitConsoleCommands( void ); +void CG_ScoresDown_f( void ); +void CG_ScoresUp_f( void ); +void CG_autoRecord_f( void ); +void CG_autoScreenShot_f( void ); +void CG_keyOn_f( void ); +void CG_keyOff_f( void ); +void CG_dumpStats_f( void ); +void CG_toggleSwing_f( void ); + +// +// cg_servercmds.c +// +void CG_ExecuteNewServerCommands( int latestSequence ); +void CG_ParseServerinfo( void ); +void CG_ParseWolfinfo( void ); // NERVE - SMF +void CG_ParseSpawns( void ); +void CG_ParseServerVersionInfo( const char *pszVersionInfo ); +void CG_ParseReinforcementTimes( const char *pszReinfSeedString ); +void CG_SetConfigValues( void ); +void CG_ShaderStateChanged( void ); +void CG_ChargeTimesChanged( void ); +void CG_LoadVoiceChats(); // NERVE - SMF +void CG_PlayBufferedVoiceChats(); // NERVE - SMF +void CG_AddToNotify( const char *str ); +const char* CG_LocalizeServerCommand( const char *buf ); +void CG_wstatsParse_cmd( void ); +void CG_wtopshotsParse_cmd( qboolean doBest ); +void CG_parseWeaponStats_cmd( void( txt_dump ) ( char * ) ); +void CG_parseBestShotsStats_cmd( qboolean doTop, void( txt_dump ) ( char * ) ); +void CG_parseTopShotsStats_cmd( qboolean doTop, void( txt_dump ) ( char * ) ); +void CG_scores_cmd( void ); + +// +// cg_playerstate.c +// +void CG_Respawn( qboolean revived ); +void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ); + +// +// cg_atmospheric.c +// +void CG_GenerateTracemap( void ); +void CG_EffectParse( const char *effectstr ); +void CG_AddAtmosphericEffects(); + +//=============================================== + +// +// system traps +// These functions are how the cgame communicates with the main game system +// + +void trap_PumpEventLoop( void ); + +// print message on the local console +void trap_Print( const char *fmt ); + +// abort the game +void trap_Error( const char *fmt ); + +// milliseconds should only be used for performance tuning, never +// for anything game related. Get time from the CG_DrawActiveFrame parameter +int trap_Milliseconds( void ); +int trap_RealTime( qtime_t *qtime ); + +// console variable interaction +void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ); +void trap_Cvar_Update( vmCvar_t *vmCvar ); +void trap_Cvar_Set( const char *var_name, const char *value ); +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); +void trap_Cvar_LatchedVariableStringBuffer( const char *var_name, char *buffer, int bufsize ); + + +// ServerCommand and ConsoleCommand parameter access +int trap_Argc( void ); +void trap_Argv( int n, char *buffer, int bufferLength ); +void trap_Args( char *buffer, int bufferLength ); + +// filesystem access +// returns length of file +int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ); +void trap_FS_Read( void *buffer, int len, fileHandle_t f ); +void trap_FS_Write( const void *buffer, int len, fileHandle_t f ); +void trap_FS_FCloseFile( fileHandle_t f ); +int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ); +int trap_FS_Delete( const char *filename ); + +// add commands to the local console as if they were typed in +// for map changing, etc. The command is not executed immediately, +// but will be executed in order the next time console commands +// are processed +void trap_SendConsoleCommand( const char *text ); + +// register a command name so the console can perform command completion. +// FIXME: replace this with a normal console command "defineCommand"? +void trap_AddCommand( const char *cmdName ); + +// send a string to the server over the network +void trap_SendClientCommand( const char *s ); + +// force a screen update, only used during gamestate load +void trap_UpdateScreen( void ); + +// model collision +void trap_CM_LoadMap( const char *mapname ); +int trap_CM_NumInlineModels( void ); +clipHandle_t trap_CM_InlineModel( int index ); // 0 = world, 1+ = bmodels +clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ); +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 ); + +// Returns the projection of a polygon onto the solid brushes in the world +int trap_CM_MarkFragments( int numPoints, const vec3_t *points, + const vec3_t projection, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer ); + +// ydnar: projects a decal onto brush model surfaces +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 ); + +// normal sounds will have their volume dynamically changed as their entity +// moves and the listener moves +void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); +void trap_S_StartSoundVControl( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int volume ); +void trap_S_StartSoundEx( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int flags ); +void trap_S_StartSoundExVControl( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int flags, int volume ); +void trap_S_StopStreamingSound( int entnum ); // usually AI. character is talking and needs to be shut up /now/ +int trap_S_GetSoundLength( sfxHandle_t sfx ); +int trap_S_GetCurrentSoundTime( void ); // ydnar + +// a local sound is always played full volume +void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ); +void trap_S_ClearLoopingSounds( void ); +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_AddRealLoopingSound( const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, int range, int volume, int soundTime ); +void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); + +// Ridah, talking animations +int trap_S_GetVoiceAmplitude( int entityNum ); +// done. + +// repatialize recalculates the volumes of sound as they should be heard by the +// given entityNum and position +void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); +sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ); // returns buzz if not found +void trap_S_StartBackgroundTrack( const char *intro, const char *loop, int fadeupTime ); // empty name stops music +void trap_S_FadeBackgroundTrack( float targetvol, int time, int num ); +void trap_S_StopBackgroundTrack( void ); +int trap_S_StartStreamingSound( const char *intro, const char *loop, int entnum, int channel, int attenuation ); +void trap_S_FadeAllSound( float targetvol, int time, qboolean stopsounds ); + +void trap_R_LoadWorldMap( const char *mapname ); + +// all media should be registered during level startup to prevent +// hitches during gameplay +qhandle_t trap_R_RegisterModel( const char *name ); // returns rgb axis if not found +qhandle_t trap_R_RegisterSkin( const char *name ); // returns all white if not found +qhandle_t trap_R_RegisterShader( const char *name ); // returns all white if not found +qhandle_t trap_R_RegisterShaderNoMip( const char *name ); // returns all white if not found + +qboolean trap_R_GetSkinModel( qhandle_t skinid, const char *type, char *name ); //----(SA) added +qhandle_t trap_R_GetShaderFromModel( qhandle_t modelid, int surfnum, int withlightmap ); //----(SA) added + +// a scene is built up by calls to R_ClearScene and the various R_Add functions. +// Nothing is drawn until R_RenderScene is called. +void trap_R_ClearScene( void ); +void trap_R_AddRefEntityToScene( const refEntity_t *re ); + +// polys are intended for simple wall marks, not really for doing +// significant construction +void trap_R_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts ); +void trap_R_AddPolyBufferToScene( polyBuffer_t* pPolyBuffer ); +// Ridah +void trap_R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ); +// done. +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_AddCoronaToScene( const vec3_t org, float r, float g, float b, float scale, int id, qboolean visible ); +void trap_R_RenderScene( const refdef_t *fd ); +void trap_R_SetColor( const float *rgba ); // NULL = 1,1,1,1 +void trap_R_DrawStretchPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ); +void trap_R_DrawRotatedPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader, float angle ); // NERVE - SMF +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_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ); + +// Save out the old render info so we don't kill the LOD system here +void trap_R_SaveViewParms(); + +// Reset the view parameters +void trap_R_RestoreViewParms(); + +// Save out the old render info so we don't kill the LOD system here +void trap_R_SaveViewParms(); + +// Reset the view parameters +void trap_R_RestoreViewParms(); + +// Set fog +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 ); + +// The glconfig_t will not change during the life of a cgame. +// If it needs to change, the entire cgame will be restarted, because +// all the qhandle_t are then invalid. +void trap_GetGlconfig( glconfig_t *glconfig ); + +// the gamestate should be grabbed at startup, and whenever a +// configstring changes +void trap_GetGameState( gameState_t *gamestate ); + +// cgame will poll each frame to see if a newer snapshot has arrived +// that it is interested in. The time is returned seperately so that +// snapshot latency can be calculated. +void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ); + +// a snapshot get can fail if the snapshot (or the entties it holds) is so +// old that it has fallen out of the client system queue +qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ); + +// retrieve a text command from the server stream +// the current snapshot will hold the number of the most recent command +// qfalse can be returned if the client system handled the command +// argc() / argv() can be used to examine the parameters of the command +qboolean trap_GetServerCommand( int serverCommandNumber ); + +// returns the most recent command number that can be passed to GetUserCmd +// this will always be at least one higher than the number in the current +// snapshot, and it may be quite a few higher if it is a fast computer on +// a lagged connection +int trap_GetCurrentCmdNumber( void ); + +qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ); + +// used for the weapon/holdable select and zoom +void trap_SetUserCmdValue( int stateValue, int flags, float sensitivityScale, int mpIdentClient ); +void trap_SetClientLerpOrigin( float x, float y, float z ); // DHM - Nerve + +// aids for VM testing +void testPrintInt( char *string, int i ); +void testPrintFloat( char *string, float f ); + +int trap_MemoryRemaining( void ); +void trap_R_RegisterFont( const char *fontName, int pointSize, fontInfo_t *font ); +qboolean trap_Key_IsDown( int keynum ); +int trap_Key_GetCatcher( void ); +void trap_Key_SetCatcher( int catcher ); +void trap_Key_KeysForBinding( const char* binding, int* key1, int* key2 ); +int trap_Key_GetKey( const char *binding ); +qboolean trap_Key_GetOverstrikeMode( void ); +void trap_Key_SetOverstrikeMode( qboolean state ); + +// RF +void trap_SendMoveSpeedsToGame( int entnum, char *movespeeds ); + +//void trap_UI_Popup(const char *arg0); //----(SA) added +void trap_UI_Popup( int arg0 ); + +// NERVE - SMF +qhandle_t getTestShader( void ); // JPW NERVE shhh +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 ); +void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ); +// -NERVE - SMF + +void trap_TranslateString( const char *string, char *buf ); // NERVE - SMF - localization + +int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits ); +e_status trap_CIN_StopCinematic( int handle ); +e_status trap_CIN_RunCinematic( int handle ); +void trap_CIN_DrawCinematic( int handle ); +void trap_CIN_SetExtents( int handle, int x, int y, int w, int h ); + +void trap_SnapVector( float *v ); + +qboolean trap_GetEntityToken( char *buffer, int bufferSize ); +qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ); +void trap_GetHunkData( int* hunkused, int* hunkexpected ); + +//zinx - binary message channel +void trap_SendMessage( char *buf, int buflen ); +messageStatus_t trap_MessageStatus( void ); + +//bani - dynamic shaders +qboolean trap_R_LoadDynamicShader( const char *shadername, const char *shadertext ); +//fretn - render to texture +void trap_R_RenderToTexture( int textureid, int x, int y, int w, int h ); +int trap_R_GetTextureId( const char *name ); +//bani - flush rendering buffer +void trap_R_Finish( void ); + +// Duffy, camera stuff +#define CAM_PRIMARY 0 // the main camera for cutscenes, etc. +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 ); +void CG_SetInitialCamera( const char *name, qboolean startBlack ); +void CG_StartCamera( const char *name, qboolean startBlack ); +void CG_StartInitialCamera(); +void CG_StopCamera( void ); + +//----(SA) added +int CG_LoadCamera( const char *name ); +void CG_FreeCamera( int camNum ); +//----(SA) end + +bg_playerclass_t* CG_PlayerClassForClientinfo( clientInfo_t *ci, centity_t* cent ); + +void CG_FitTextToWidth( char* instr, int w, int size ); +void CG_FitTextToWidth2( char* instr, float scale, float w, int size ); +void CG_FitTextToWidth_Ext( char* instr, float scale, float w, int size, fontInfo_t* font ); +int CG_TrimLeftPixels( char* instr, float scale, float w, int size ); +void CG_FitTextToWidth_SingleLine( char* instr, float scale, float w, int size ); + +void CG_LocateCampaign( void ); +void CG_LocateArena( void ); +const char* CG_DescriptionForCampaign( void ); +const char* CG_NameForCampaign( void ); +void CG_CloseMenus(); +// void CG_CampaignBriefing_f( void ); +void CG_LimboMenu_f( void ); + +void CG_DrawPlayer_Limbo( float x, float y, float w, float h, playerInfo_t *pi, int time, clientInfo_t *ci, qboolean animatedHead ); +animation_t* CG_GetLimboAnimation( playerInfo_t *pi, const char *name ); + +typedef struct { + weapon_t weapindex; + const char *desc; +} weaponType_t; + +extern weaponType_t weaponTypes[]; +weaponType_t* WM_FindWeaponTypeForWeapon( weapon_t weapon ); + +extern animation_t *lastTorsoAnim; +extern animation_t *lastLegsAnim; +extern qboolean ccInitial; + +void CG_MenuCheckPendingAnimation( playerInfo_t *pi ); +void CG_MenuPendingAnimation( playerInfo_t *pi, const char* legsAnim, const char* torsoAnim, int delay ); +void CG_MenuSetAnimation( playerInfo_t *pi, const char* legsAnim, const char* torsoAnim, qboolean force, qboolean clearpending ); + +#define CC_FILTER_AXIS ( 1 << 0 ) +#define CC_FILTER_ALLIES ( 1 << 1 ) +#define CC_FILTER_SPAWNS ( 1 << 2 ) +#define CC_FILTER_CMDPOST ( 1 << 3 ) +#define CC_FILTER_HACABINETS ( 1 << 4 ) +#define CC_FILTER_CONSTRUCTIONS ( 1 << 5 ) +#define CC_FILTER_DESTRUCTIONS ( 1 << 6 ) +#define CC_FILTER_OBJECTIVES ( 1 << 7 ) +//#define CC_FILTER_WAYPOINTS (1 << 7) +//#define CC_FILTER_OBJECTIVES (1 << 8) + +typedef struct { + qhandle_t shader; + const char *iconname; + int width; + int height; +} rankicon_t; + +extern rankicon_t rankicons[NUM_EXPERIENCE_LEVELS][2]; + +#define TAB_LEFT_WIDTH 178 +#define TAB_LEFT_EDGE ( 640 - TAB_LEFT_WIDTH ) + +fireteamData_t* CG_IsOnSameFireteam( int clientNum, int clientNum2 ); + + +// START Mad Doc - TDF + +#define MAX_SQUAD_SIZE 6 + +// +// merged the common UI elements +// +#define UI_CAMPAIGN_BRIEFING 0 +#define UI_COMMAND_MAP 1 +#define UI_SQUAD_SELECT 2 + + +void CG_DrawUITabs(); +void CG_DrawUICurrentSquad(); +qboolean CG_UICommonClick(); +void CG_DrawUISelectedSoldier( void ); +void CG_UICurrentSquadSetup( void ); +void CG_CampaignBriefingSetup( void ); + + +#define ORDER_ICON_FADE_TIME 3500 + +int CG_GetFirstSelectedBot(); +void CG_AddToJournal( char *text ); +// returns true if game is single player (or coop) +qboolean CG_IsSinglePlayer( void ); + +// END Mad Doc - TDF + +// Gordon: Fireteam stuff + +//fireteamData_t* CG_IsOnFireteam( int clientNum ); +#define /*fireteamData_t**/ CG_IsOnFireteam( /*int*/ clientNum ) /*{ return*/ cgs.clientinfo[clientNum].fireteamData /*}*/ +fireteamData_t* CG_IsOnSameFireteam( int clientNum, int clientNum2 ); +fireteamData_t* CG_IsFireTeamLeader( int clientNum ); + +clientInfo_t* CG_ClientInfoForPosition( int pos, int max ); +fireteamData_t* CG_FireTeamForPosition( int pos, int max ); +clientInfo_t* CG_FireTeamPlayerForPosition( int pos, int max ); + +void CG_SortClientFireteam(); + +void CG_DrawFireTeamOverlay( rectDef_t* rect ); +clientInfo_t* CG_SortedFireTeamPlayerForPosition( int pos, int max ); +qboolean CG_FireteamHasClass( int classnum, qboolean selectedonly ); +const char* CG_BuildSelectedFirteamString( void ); + +// OSP +#define Pri( x ) CG_Printf( "[cgnotify]%s", CG_LocalizeServerCommand( x ) ) +#define CPri( x ) CG_CenterPrint( CG_LocalizeServerCommand( x ), SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.2 ), SMALLCHAR_WIDTH ); + +// cg_multiview.c +void CG_mvDelete_f( void ); +void CG_mvHideView_f( void ); +void CG_mvNew_f( void ); +void CG_mvShowView_f( void ); +void CG_mvSwapViews_f( void ); +void CG_mvToggleAll_f( void ); +void CG_mvToggleView_f( void ); +// +cg_window_t *CG_mvClientLocate( int pID ); +void CG_mvCreate( int pID ); +cg_window_t *CG_mvCurrent( void ); +void CG_mvDraw( cg_window_t *sw ); +cg_window_t *CG_mvFindNonMainview( void ); +void CG_mvFree( int pID ); +void CG_mvMainviewSwap( cg_window_t *av ); +qboolean CG_mvMergedClientLocate( int pID ); +void CG_mvOverlayDisplay( void ); +void CG_mvOverlayUpdate( void ); +void CG_mvOverlayClientUpdate( int pID, int index ); +void CG_mvProcessClientList( void ); +void CG_mvTransitionPlayerState( playerState_t* ps ); +void CG_mvUpdateClientInfo( int pID ); +void CG_mvWindowOverlay( int pID, float b_x, float b_y, float b_w, float b_h, float s, int wState, qboolean fSelected ); +void CG_mvZoomBinoc( float x, float y, float w, float h ); +void CG_mvZoomSniper( float x, float y, float w, float h ); + +// cg_window.c +qboolean CG_addString( cg_window_t *w, char *buf ); +//void CG_createDemoHelpWindow(void); +//void CG_createSpecHelpWindow(void); +void CG_createStatsWindow( void ); +void CG_createTopShotsWindow( void ); +void CG_createWstatsMsgWindow( void ); +void CG_createWtopshotsMsgWindow( void ); +void CG_createMOTDWindow( void ); +void CG_cursorUpdate( void ); +void CG_initStrings( void ); +void CG_printWindow( char *str ); +void CG_removeStrings( cg_window_t *w ); +cg_window_t *CG_windowAlloc( int fx, int startupLength ); +void CG_windowDraw( void ); +void CG_windowFree( cg_window_t *w ); +void CG_windowInit( void ); +void CG_windowNormalizeOnText( cg_window_t *w ); +// OSP + +void CG_SetupCabinets( void ); + +extern displayContextDef_t cgDC; +void CG_ParseSkyBox( void ); +void CG_ParseTagConnect( int tagNum ); +void CG_ParseTagConnects( void ); + +// +// cg_ents.c +// + +void CG_AttachBitsToTank( centity_t* tank, refEntity_t* mg42base, refEntity_t* mg42upper, refEntity_t* mg42gun, refEntity_t* player, refEntity_t* flash, vec_t* playerangles, const char* tagName, qboolean browning ); + +// +// cg_character.c +// + +qboolean CG_RegisterCharacter( const char *characterFile, bg_character_t *character ); +bg_character_t *CG_CharacterForClientinfo( clientInfo_t *ci, centity_t *cent ); +bg_character_t *CG_CharacterForPlayerstate( playerState_t* ps ); +void CG_RegisterPlayerClasses( void ); + +// +// cg_polybus.c +// + +polyBuffer_t* CG_PB_FindFreePolyBuffer( qhandle_t shader, int numVerts, int numIndicies ); +void CG_PB_ClearPolyBuffers( void ); +void CG_PB_RenderPolyBuffers( void ); + + +// +// cg_limbopanel.c +// + +void CG_LimboPanel_KeyHandling( int key, qboolean down ); +int CG_LimboPanel_GetMaxObjectives( void ); + +qboolean CG_LimboPanel_WeaponLights_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_WeaponPanel_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_WeaponPanel_KeyUp( panel_button_t* button, int key ); +qboolean CG_LimboPanel_ObjectiveText_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_TeamButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_ClassButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_OkButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_PlusButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_MinusButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_CancelButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_Filter_KeyDown( panel_button_t* button, int key ); +qboolean CG_LimboPanel_BriefingButton_KeyDown( panel_button_t* button, int key ); + +void CG_LimboPanel_BriefingButton_Draw( panel_button_t* button ); +void CG_LimboPanel_ClassBar_Draw( panel_button_t* button ); +void CG_LimboPanel_Filter_Draw( panel_button_t* button ); +void CG_LimboPanel_RenderSkillIcon( panel_button_t* button ); +void CG_LimboPanel_RenderTeamButton( panel_button_t* button ); +void CG_LimboPanel_RenderClassButton( panel_button_t* button ); +void CG_LimboPanel_RenderObjectiveText( panel_button_t* button ); +void CG_LimboPanel_RenderCommandMap( panel_button_t* button ); +void CG_LimboPanel_RenderObjectiveBack( panel_button_t* button ); +void CG_LimboPanel_RenderLight( panel_button_t* button ); +void CG_LimboPanel_WeaponLights( panel_button_t* button ); +void CG_LimboPanel_RenderHead( panel_button_t* button ); +void CG_LimboPanel_WeaponPanel( panel_button_t* button ); +void CG_LimboPanel_Border_Draw( panel_button_t* button ); +void CG_LimboPanel_RenderMedal( panel_button_t* button ); +void CG_LimboPanel_RenderCounter( panel_button_t* button ); +void CG_LimboPanelRenderText_NoLMS( panel_button_t* button ); +void CG_LimboPanelRenderText_SkillsText( panel_button_t* button ); + +void CG_LimboPanel_NameEditFinish( panel_button_t* button ); + +void CG_LimboPanel_Setup( void ); +void CG_LimboPanel_Init( void ); + +void CG_LimboPanel_GetWeaponCardIconData( weapon_t weap, qhandle_t* shader, float* w, float* h, float* s0, float* t0, float* s1, float* t1 ); +void CG_LimboPanel_RequestObjective( void ); +void CG_LimboPanel_RequestWeaponStats( void ); +qboolean CG_LimboPanel_Draw( void ); +team_t CG_LimboPanel_GetTeam( void ); +team_t CG_LimboPanel_GetRealTeam( void ); +bg_character_t* CG_LimboPanel_GetCharacter( void ); +int CG_LimboPanel_GetClass( void ); +int CG_LimboPanel_WeaponCount( void ); +int CG_LimboPanel_WeaponCount_ForSlot( int number ); +int CG_LimboPanel_GetSelectedWeaponNum( void ); +void CG_LimboPanel_SetSelectedWeaponNum( int number ); +bg_playerclass_t* CG_LimboPanel_GetPlayerClass( void ); +weapon_t CG_LimboPanel_GetSelectedWeapon( void ); +weapon_t CG_LimboPanel_GetWeaponForNumber( int number, int slot, qboolean ignoreDisabled ); +extWeaponStats_t CG_LimboPanel_GetSelectedWeaponStat( void ); +qboolean CG_LimboPanel_WeaponIsDisabled( int weap ); +qboolean CG_LimboPanel_RealWeaponIsDisabled( weapon_t weap ); +int CG_LimboPanel_GetWeaponNumberForPos( int pos ); + + +void CG_LimboPanel_SetSelectedWeaponNumForSlot( int index, int number ); +weapon_t CG_LimboPanel_GetSelectedWeaponForSlot( int index ); + +// +// cg_commandmap.c +// +// A scissored map always has the player in the center +typedef struct mapScissor_s { + qboolean circular; // if qfalse, rect + float zoomFactor; + vec2_t tl; + vec2_t br; +} mapScissor_t; + +int CG_CurLayerForZ( int z ); +void CG_DrawMap( float x, float y, float w, float h, int mEntFilter, mapScissor_t *scissor, qboolean interactive, float alpha, qboolean borderblend ); +int CG_DrawSpawnPointInfo( int px, int py, int pw, int ph, qboolean draw, mapScissor_t *scissor, int expand ); +void CG_DrawMortarMarker( int px, int py, int pw, int ph, qboolean draw, mapScissor_t *scissor, int expand ); +void CG_CommandMap_SetHighlightText( const char* text, float x, float y ); +void CG_CommandMap_DrawHighlightText( void ); +qboolean CG_CommandCentreSpawnPointClick( void ); + +#define LIMBO_3D_X 287 //% 280 +#define LIMBO_3D_Y 382 +#define LIMBO_3D_W 128 +#define LIMBO_3D_H 96 //% 94 + +#define CC_2D_X 64 +#define CC_2D_Y 23 +#define CC_2D_W 352 +#define CC_2D_H 352 + +void CG_DrawPlayerHead( rectDef_t *rect, bg_character_t* character, bg_character_t* headcharacter, float yaw, float pitch, qboolean drawHat, hudHeadAnimNumber_t animation, qhandle_t painSkin, int rank, qboolean spectator ); + +// +// cg_popupmessages.c +// + +void CG_InitPM( void ); +void CG_InitPMGraphics( void ); +void CG_UpdatePMLists( void ); +void CG_AddPMItem( popupMessageType_t type, const char* message, qhandle_t shader ); +void CG_AddPMItemBig( popupMessageBigType_t type, const char* message, qhandle_t shader ); +void CG_DrawPMItems( void ); +void CG_DrawPMItemsBig( void ); +const char* CG_GetPMItemText( centity_t* cent ); +void CG_PlayPMItemSound( centity_t *cent ); +qhandle_t CG_GetPMItemIcon( centity_t* cent ); +void CG_DrawKeyHint( rectDef_t* rect, const char* binding ); + +// +// cg_debriefing.c +// + +clientInfo_t* CG_Debriefing_GetSelectedClientInfo( void ); +void CG_Debrieing_SetSelectedClient( int clientNum ); + +qboolean CG_Debriefing_Draw( void ); +void CG_ChatPanel_Setup( void ); + +void CG_Debriefing_ChatEditFinish( panel_button_t* button ); +void CG_Debriefing_BackButton_Draw( panel_button_t* button ); +void CG_Debriefing_HTMLButton_Draw( panel_button_t* button ); +void CG_Debriefing_NextButton_Draw( panel_button_t* button ); +void CG_Debriefing_ChatButton_Draw( panel_button_t* button ); +void CG_Debriefing_ReadyButton_Draw( panel_button_t* button ); +qboolean CG_Debriefing_ChatButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_Debriefing_BackButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_Debriefing_ReadyButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_Debriefing_QCButton_KeyDown( panel_button_t* button, int key ); +qboolean CG_Debriefing_NextButton_KeyDown( panel_button_t* button, int key ); + +void CG_PanelButtonsRender_Button_Ext( rectDef_t* r, const char* text ); + +void CG_Debriefing_PlayerName_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerRank_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerMedals_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerTime_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerXP_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerACC_Draw( panel_button_t* button ); +void CG_Debriefing_PlayerSkills_Draw( panel_button_t* button ); + + +void CG_DebriefingPlayerWeaponStats_Draw( panel_button_t* button ); + +void CG_DebriefingXPHeader_Draw( panel_button_t* button ); + +void CG_DebriefingTitle_Draw( panel_button_t* button ); +void CG_DebriefingPlayerList_Draw( panel_button_t* button ); +qboolean CG_DebriefingPlayerList_KeyDown( panel_button_t* button, int key ); + +void CG_Debriefing_ChatEdit_Draw( panel_button_t* button ); +void CG_Debriefing_ChatBox_Draw( panel_button_t* button ); +void CG_Debriefing_Scrollbar_Draw( panel_button_t* button ); +qboolean CG_Debriefing_Scrollbar_KeyDown( panel_button_t* button, int key ); +qboolean CG_Debriefing_Scrollbar_KeyUp( panel_button_t* button, int key ); +float CG_Debriefing_CalcCampaignProgress( void ); + +const char* CG_Debriefing_RankNameForClientInfo( clientInfo_t* ci ); +const char* CG_Debriefing_FullRankNameForClientInfo( clientInfo_t* ci ); +void CG_Debriefing_Startup( void ); +void CG_Debriefing_Shutdown( void ); +qboolean CG_Debriefing_ServerCommand( const char* cmd ); +void CG_Debriefing_MouseEvent( int x, int y ); + +void CG_TeamDebriefingOutcome_Draw( panel_button_t* button ); +void CG_TeamDebriefingMapList_Draw( panel_button_t* button ); +qboolean CG_TeamDebriefingMapList_KeyDown( panel_button_t* button, int key ); +void CG_TeamDebriefingMapWinner_Draw( panel_button_t* button ); +void CG_TeamDebriefingMapShot_Draw( panel_button_t* button ); +void CG_TeamDebriefingTeamXP_Draw( panel_button_t* button ); +void CG_TeamDebriefingTeamSkillXP_Draw( panel_button_t* button ); + +const char* CG_PickupItemText( int item ); + +void CG_LoadPanel_DrawPin( const char* text, float px, float py, float sx, float sy, qhandle_t shader, float pinsize, float backheight ); +void CG_LoadPanel_RenderCampaignPins( panel_button_t* button ); +void CG_LoadPanel_RenderMissionDescriptionText( panel_button_t* button ); +void CG_LoadPanel_RenderCampaignTypeText( panel_button_t* button ); +void CG_LoadPanel_RenderCampaignNameText( panel_button_t* button ); +void CG_LoadPanel_RenderPercentageMeter( panel_button_t* button ); +void CG_LoadPanel_RenderContinueButton( panel_button_t* button ); +void CG_LoadPanel_RenderLoadingBar( panel_button_t* button ); +void CG_LoadPanel_KeyHandling( int key, qboolean down ); +qboolean CG_LoadPanel_ContinueButtonKeyDown( panel_button_t* button, int key ); +void CG_DrawConnectScreen( qboolean interactive, qboolean forcerefresh ); + +qboolean CG_Debriefing2_Maps_KeyDown( panel_button_t* button, int key ); +void CG_Debriefing2TeamSkillHeaders_Draw( panel_button_t* button ); +void CG_Debriefing2TeamSkillXP_Draw( panel_button_t* button ); +void CG_Debreifing2_MissionTitle_Draw( panel_button_t* button ); +void CG_Debreifing2_Mission_Draw( panel_button_t* button ); +void CG_Debreifing2_Maps_Draw( panel_button_t* button ); +void CG_Debreifing2_Awards_Draw( panel_button_t* button ); +void CG_PanelButtonsRender_Window( panel_button_t* button ); +void CG_PanelButtonsRender_Button( panel_button_t* button ); + +team_t CG_Debriefing_FindWinningTeamForMap( void ); + +int CG_CalcViewValues( void ); +void CG_HudHeadAnimation( bg_character_t* ch, lerpFrame_t* lf, int *oldframe, int *frame, float *backlerp, hudHeadAnimNumber_t animation ); + +// +// cg_fireteams.c +// + +void CG_Fireteams_KeyHandling( int key, qboolean down ); +qboolean CG_FireteamCheckExecKey( int key, qboolean doaction ); +void CG_Fireteams_Draw( void ); +void CG_Fireteams_Setup( void ); + +void CG_Fireteams_MenuText_Draw( panel_button_t* button ); +void CG_Fireteams_MenuTitleText_Draw( panel_button_t* button ); diff --git a/src/cgame/cg_localents.c b/src/cgame/cg_localents.c new file mode 100644 index 0000000..fc3aafa --- /dev/null +++ b/src/cgame/cg_localents.c @@ -0,0 +1,1383 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +// cg_localents.c -- every frame, generate renderer commands for locally +// processed entities, like smoke puffs, gibs, shells, etc. + +#include "cg_local.h" + +// Ridah, increased this +//#define MAX_LOCAL_ENTITIES 512 +#define MAX_LOCAL_ENTITIES 768 // renderer can only handle 1024 entities max, so we should avoid + // overwriting game entities +// done. + +localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES]; +localEntity_t cg_activeLocalEntities; // double linked list +localEntity_t *cg_freeLocalEntities; // single linked list + +// Ridah, debugging +int localEntCount = 0; + +/* +=================== +CG_InitLocalEntities + +This is called at startup and for tournement restarts +=================== +*/ +void CG_InitLocalEntities( void ) { + int i; + + memset( cg_localEntities, 0, sizeof( cg_localEntities ) ); + cg_activeLocalEntities.next = &cg_activeLocalEntities; + cg_activeLocalEntities.prev = &cg_activeLocalEntities; + cg_freeLocalEntities = cg_localEntities; + for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) { + cg_localEntities[i].next = &cg_localEntities[i + 1]; + } + + // Ridah, debugging + localEntCount = 0; +} + + +/* +================== +CG_FreeLocalEntity +================== +*/ +void CG_FreeLocalEntity( localEntity_t *le ) { + if ( !le->prev ) { + CG_Error( "CG_FreeLocalEntity: not active" ); + } + + // Ridah, debugging + localEntCount--; +// trap_Print( va("FreeLocalEntity: locelEntCount = %d\n", localEntCount) ); + // done. + + // remove from the doubly linked active list + le->prev->next = le->next; + le->next->prev = le->prev; + + // the free list is only singly linked + le->next = cg_freeLocalEntities; + cg_freeLocalEntities = le; +} + +/* +=================== +CG_AllocLocalEntity + +Will allways succeed, even if it requires freeing an old active entity +=================== +*/ +localEntity_t *CG_AllocLocalEntity( void ) { + localEntity_t *le; + + if ( !cg_freeLocalEntities ) { + // no free entities, so free the one at the end of the chain + // remove the oldest active entity + CG_FreeLocalEntity( cg_activeLocalEntities.prev ); + } + + // Ridah, debugging + localEntCount++; +// trap_Print( va("AllocLocalEntity: locelEntCount = %d\n", localEntCount) ); + // done. + + le = cg_freeLocalEntities; + cg_freeLocalEntities = cg_freeLocalEntities->next; + + memset( le, 0, sizeof( *le ) ); + + // link into the active list + le->next = cg_activeLocalEntities.next; + le->prev = &cg_activeLocalEntities; + cg_activeLocalEntities.next->prev = le; + cg_activeLocalEntities.next = le; + return le; +} + + +/* +==================================================================================== + +FRAGMENT PROCESSING + +A fragment localentity interacts with the environment in some way (hitting walls), +or generates more localentities along a trail. + +==================================================================================== +*/ + +/* +================ +CG_BloodTrail + +Leave expanding blood puffs behind gibs +================ +*/ +// use this to change between particle and trail code +//#define BLOOD_PARTICLE_TRAIL +void CG_BloodTrail( localEntity_t *le ) { + int t; + int t2; + int step; + vec3_t newOrigin; + float vl; //bani + +#ifndef BLOOD_PARTICLE_TRAIL + static vec3_t col = {1,1,1}; +#endif + + centity_t *cent; + cent = &cg_entities[le->ownerNum]; + + if ( !cg_blood.integer ) { + return; + } + + // step = 150; +#ifdef BLOOD_PARTICLE_TRAIL + step = 10; +#else + // time it takes to move 3 units + vl = VectorLength( le->pos.trDelta ); + // rain - need to check FLT_EPSILON because floating-point math sucks <3 + if ( vl < FLT_EPSILON ) { + return; + } + step = ( 1000 * 3 ) / vl; +//bani - avoid another div by 0 +//zinx - check against <= 0 instead of == 0, because it can still wrap; (3000 / (FLT_EPSILON*11.7f)) < 0 + if ( step <= 0 ) { + return; + } +#endif + + t = step * ( ( cg.time - cg.frametime + step ) / step ); + t2 = step * ( cg.time / step ); + + for ( ; t <= t2; t += step ) { + BG_EvaluateTrajectory( &le->pos, t, newOrigin, qfalse, -1 ); + +#ifdef BLOOD_PARTICLE_TRAIL + CG_Particle_Bleed( cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500 + rand() % 200 ); +#else + // Ridah, blood trail using trail code (should be faster since we don't have to spawn as many) + le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, + le, // rain - zinx's trail fix + cgs.media.bloodTrailShader, + t, + STYPE_STRETCH, + newOrigin, + 180, + 1.0, // start alpha + 0.0, // end alpha + 12.0, + 12.0, + TJFL_NOCULL, + col, col, + 0, 0 ); +#endif + + } +} + + +/* +================ +CG_FragmentBounceMark +================ +*/ +void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) { + int radius; + vec4_t projection, color; + + if ( le->leMarkType == LEMT_BLOOD ) { + static int lastBloodMark; + + // don't drop too many blood marks + if ( !( lastBloodMark > cg.time || lastBloodMark > cg.time - 100 ) ) { + radius = 16 + ( rand() & 31 ); + //% CG_ImpactMark( cgs.media.bloodDotShaders[rand()%5], trace->endpos, trace->plane.normal, random()*360, + //% 1,1,1,1, qtrue, radius, qfalse, cg_bloodTime.integer * 1000 ); + #if 0 + VectorSubtract( vec3_origin, trace->plane.normal, projection ); + projection[ 3 ] = radius * 2.0f; + VectorMA( trace->endpos, -8.0f, projection, markOrigin ); + CG_ImpactMark( cgs.media.bloodDotShaders[ rand() % 5 ], markOrigin, projection, radius, random() * 360.0f, 1.0f, 1.0f, 1.0f, 1.0f, cg_bloodTime.integer * 1000 ); + #else + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = radius; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( cgs.media.bloodDotShaders[ rand() % 5 ], 1, (vec3_t*) trace->endpos, projection, color, + cg_bloodTime.integer * 1000, ( cg_bloodTime.integer * 1000 ) >> 4 ); + #endif + lastBloodMark = cg.time; + } + } + + // don't allow a fragment to make multiple marks, or they + // pile up while settling + le->leMarkType = LEMT_NONE; +} + +/* +================ +CG_FragmentBounceSound +================ +*/ +void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) { + int rnd; + + // Gordon: bleh, has no-one heard of switch statements... + switch ( le->leBounceSoundType ) { + // Gordon: adding machinegun brass bouncy sound for tk + case LEBS_BRASS: + rnd = rand() % 3; + + if ( trace->surfaceFlags & SURF_METAL ) { + trap_S_StartSoundVControl( trace->endpos, -1, CHAN_AUTO, cgs.media.sfx_brassSound[BRASSSOUND_METAL][rnd], 64 ); + } else if ( trace->surfaceFlags & SURF_WOOD ) { + trap_S_StartSoundVControl( trace->endpos, -1, CHAN_AUTO, cgs.media.sfx_brassSound[BRASSSOUND_WOOD][rnd], 64 ); + } else if ( trace->surfaceFlags & ( SURF_GRAVEL | SURF_SNOW | SURF_CARPET | SURF_GRASS ) ) { + trap_S_StartSoundVControl( trace->endpos, -1, CHAN_AUTO, cgs.media.sfx_brassSound[BRASSSOUND_SOFT][rnd], 64 ); + } else { + trap_S_StartSoundVControl( trace->endpos, -1, CHAN_AUTO, cgs.media.sfx_brassSound[BRASSSOUND_STONE][rnd], 64 ); + } + break; + case LEBS_ROCK: + rnd = rand() % 3; + + trap_S_StartSound( trace->endpos, -1, CHAN_AUTO, cgs.media.sfx_rubbleBounce[rnd] ); + break; + case LEBS_BONE: + trap_S_StartSound( trace->endpos, -1, CHAN_AUTO, cgs.media.boneBounceSound ); + break; + default: + return; + } + + // don't allow a fragment to make multiple bounce sounds, + // or it gets too noisy as they settle + le->leBounceSoundType = LEBS_NONE; +} + + +/* +================ +CG_ReflectVelocity +================ +*/ +void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) { + vec3_t velocity; + float dot; + int hitTime; + + // reflect the velocity on the trace plane + hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; + BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity, qfalse, -1 ); + dot = DotProduct( velocity, trace->plane.normal ); + VectorMA( velocity, -2 * dot, trace->plane.normal, le->pos.trDelta ); + + VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta ); + + VectorCopy( trace->endpos, le->pos.trBase ); + le->pos.trTime = cg.time; + + + // check for stop, making sure that even on low FPS systems it doesn't bobble + + if ( le->leMarkType == LEMT_BLOOD && trace->startsolid ) { + //centity_t *cent; + //cent = &cg_entities[trace->entityNum]; + //if (cent && cent->currentState.apos.trType != TR_STATIONARY) + // le->pos.trType = TR_STATIONARY; + } else if ( trace->allsolid || ( trace->plane.normal[2] > 0 && ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) { +//----(SA) if it's a fragment and it's not resting on the world... +// if(le->leType == LE_DEBRIS && trace->entityNum < (MAX_ENTITIES - 1)) + if ( le->leType == LE_FRAGMENT && trace->entityNum < ( MAX_ENTITIES - 1 ) ) { + le->pos.trType = TR_GRAVITY_PAUSED; + } else { + le->pos.trType = TR_STATIONARY; + } + } +} + + +//----(SA) added + +/* +============== +CG_AddEmitter +============== +*/ +void CG_AddEmitter( localEntity_t *le ) { + vec3_t dir; + + if ( le->breakCount > cg.time ) { // using 'breakCount' for 'wait' + return; + } + + VectorScale( le->angles.trBase, 30, dir ); + CG_Particle_OilParticle( cgs.media.oilParticle, le->pos.trBase, dir, 15000, le->ownerNum ); + + le->breakCount = cg.time + 50; +} + +//----(SA) end + + +void CG_Explodef( vec3_t origin, vec3_t dir, int mass, int type, qhandle_t sound, int forceLowGrav, qhandle_t shader ); + +/* +================ +CG_AddFragment +================ +*/ +void CG_AddFragment( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + refEntity_t *re; + float flameAlpha = 0.0; // TTimo: init + vec3_t flameDir; + qboolean hasFlame = qfalse; + int i; + + // Ridah + re = &le->refEntity; + if ( !re->fadeStartTime || re->fadeEndTime < le->endTime ) { + if ( le->endTime - cg.time > 5000 ) { + re->fadeStartTime = le->endTime - 5000; + } else { + re->fadeStartTime = le->endTime - 1000; + } + re->fadeEndTime = le->endTime; + } + + // Ridah, flaming gibs + if ( le->onFireStart && ( le->onFireStart < cg.time && le->onFireEnd > cg.time ) ) { + hasFlame = qtrue; + // calc the alpha + flameAlpha = 1.0 - ( (float)( cg.time - le->onFireStart ) / (float)( le->onFireEnd - le->onFireStart ) ); + if ( flameAlpha < 0.0 ) { + flameAlpha = 0.0; + } + if ( flameAlpha > 1.0 ) { + flameAlpha = 1.0; + } + trap_S_AddLoopingSound( le->refEntity.origin, vec3_origin, cgs.media.flameCrackSound, (int)( 20.0 * flameAlpha ), 0 ); + } + +//----(SA) added + if ( le->leFlags & LEF_SMOKING ) { + float alpha; + refEntity_t flash; + + // create a little less smoke + + // TODO: FIXME: this is not quite right, because it'll become fps dependant - in a bad way. + // the slower the fps, the /more/ smoke there'll be, probably driving the fps lower. + if ( !( rand() % 5 ) ) { + alpha = 1.0 - ( (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime ) ); + alpha *= 0.25f; + memset( &flash, 0, sizeof( flash ) ); + CG_PositionEntityOnTag( &flash, &le->refEntity, "tag_flash", 0, NULL ); + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, 1000, 8, 20, 20, alpha, 8.f ); + } + } +//----(SA) end + + if ( le->pos.trType == TR_STATIONARY ) { + int t; + + // Ridah, add the flame + if ( hasFlame ) { + refEntity_t backupEnt; + + backupEnt = le->refEntity; + + VectorClear( flameDir ); + flameDir[2] = 1; + + le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha ); + VectorCopy( flameDir, le->refEntity.fireRiseDir ); + le->refEntity.customShader = cgs.media.onFireShader; + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.customShader = cgs.media.onFireShader2; + trap_R_AddRefEntityToScene( &le->refEntity ); + + le->refEntity = backupEnt; + } + + t = le->endTime - cg.time; + trap_R_AddRefEntityToScene( &le->refEntity ); + + return; + + } else if ( le->pos.trType == TR_GRAVITY_PAUSED ) { + int t; + + // Ridah, add the flame + if ( hasFlame ) { + refEntity_t backupEnt; + + backupEnt = le->refEntity; + + VectorClear( flameDir ); + flameDir[2] = 1; + + le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha ); + VectorCopy( flameDir, le->refEntity.fireRiseDir ); + le->refEntity.customShader = cgs.media.onFireShader; + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.customShader = cgs.media.onFireShader2; + trap_R_AddRefEntityToScene( &le->refEntity ); + + le->refEntity = backupEnt; + } + + t = le->endTime - cg.time; + trap_R_AddRefEntityToScene( &le->refEntity ); + + + // trace a line from previous position down, to see if I should start falling again + + VectorCopy( le->refEntity.origin, newOrigin ); + newOrigin [2] -= 5; + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MISSILECLIP ); + + if ( trace.fraction == 1.0 ) { // it's clear, start moving again + VectorClear( le->pos.trDelta ); + VectorClear( le->angles.trDelta ); + le->pos.trType = TR_GRAVITY; // nothing below me, start falling again + } else { + return; + } + } + + // calculate new position + BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin, qfalse, -1 ); + + if ( hasFlame ) { + // calc the flame dir + VectorSubtract( le->refEntity.origin, newOrigin, flameDir ); + if ( VectorLengthSquared( flameDir ) == 0 ) { + flameDir[2] = 1; + // play a burning sound when not moving + trap_S_AddLoopingSound( newOrigin, vec3_origin, cgs.media.flameSound, (int)( 0.3 * 255.0 * flameAlpha ), 0 ); + } else { + VectorNormalize( flameDir ); + // play a flame blow sound when moving + trap_S_AddLoopingSound( newOrigin, vec3_origin, cgs.media.flameBlowSound, (int)( 0.3 * 255.0 * flameAlpha ), 0 ); + } + } + + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); + if ( trace.fraction == 1.0 ) { + // still in free fall + VectorCopy( newOrigin, le->refEntity.origin ); + + if ( le->leFlags & LEF_TUMBLE || le->angles.trType == TR_LINEAR ) { + vec3_t angles; + + BG_EvaluateTrajectory( &le->angles, cg.time, angles, qtrue, -1 ); + AnglesToAxis( angles, le->refEntity.axis ); + if ( le->sizeScale && le->sizeScale != 1.0 ) { + for ( i = 0; i < 3; i++ ) { + VectorScale( le->refEntity.axis[i], le->sizeScale, le->refEntity.axis[i] ); + } + le->refEntity.nonNormalizedAxes = qtrue; + } + } else { + AnglesToAxis( le->angles.trBase, le->refEntity.axis ); + if ( le->sizeScale && le->sizeScale != 1.0 ) { + for ( i = 0; i < 3; i++ ) { + VectorScale( le->refEntity.axis[i], le->sizeScale, le->refEntity.axis[i] ); + } + le->refEntity.nonNormalizedAxes = qtrue; + } + } + + // Ridah, add the flame + if ( hasFlame ) { + refEntity_t backupEnt; + + backupEnt = le->refEntity; + + le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha ); + VectorCopy( flameDir, le->refEntity.fireRiseDir ); + le->refEntity.customShader = cgs.media.onFireShader; + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.customShader = cgs.media.onFireShader2; + trap_R_AddRefEntityToScene( &le->refEntity ); + + le->refEntity = backupEnt; + } + + trap_R_AddRefEntityToScene( &le->refEntity ); + + // add a blood trail + if ( le->leBounceSoundType == LEBS_BLOOD ) { + CG_BloodTrail( le ); + } + + return; + } + + // if it is in a nodrop zone, remove it + // this keeps gibs from waiting at the bottom of pits of death + // and floating levels + if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { + CG_FreeLocalEntity( le ); + return; + } + + // do a bouncy sound + CG_FragmentBounceSound( le, &trace ); + + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + + if ( le->leFlags & LEF_TUMBLE_SLOW ) { + VectorScale( le->angles.trDelta, 0.8, le->angles.trDelta ); + } + + // break on contact? + if ( le->breakCount ) { + if ( le->leFlags & LEF_TUMBLE_SLOW ) { // Gordon: HACK HACK x_X + vec3_t org, dir; + float sizeScale; + + // make it smaller + sizeScale = le->sizeScale * 0.8; + if ( sizeScale < 0.7 ) { + sizeScale = 0.7; + } + + // move us a bit + VectorNormalize2( le->pos.trDelta, dir ); + VectorMA( trace.endpos, 4.0 * sizeScale, dir, org ); + + // randomize vel a bit + VectorMA( le->pos.trDelta, VectorLength( le->pos.trDelta ) * 0.3, bytedirs[rand() % NUMVERTEXNORMALS], dir ); + + CG_Explodef( org, dir, sizeScale * 50, 0, 0, qfalse, trap_R_GetShaderFromModel( le->refEntity.hModel, 0, 0 ) ); + + CG_FreeLocalEntity( le ); + return; + } else { +#if 0 + // FIXME: re-add gibmodel support? + clientInfo_t *ci; + int clientNum; + localEntity_t *nle; + vec3_t dir; + bg_character_t *character; + + + clientNum = le->ownerNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + CG_Error( "Bad clientNum on player entity" ); + } + ci = &cgs.clientinfo[ clientNum ]; + character = CG_CharacterForClientinfo( ci, NULL ); + + // spawn some new fragments + for ( i = 0; i <= le->breakCount; i++ ) { + nle = CG_AllocLocalEntity(); + memcpy( &( nle->leType ), &( le->leType ), sizeof( localEntity_t ) - 2 * sizeof( localEntity_t * ) ); + if ( nle->breakCount-- < 2 ) { + nle->refEntity.hModel = character->gibModels[rand() % 2]; + } else { + nle->refEntity.hModel = character->gibModels[rand() % 4]; + } + // make it smaller + nle->endTime = cg.time + 5000 + rand() % 2000; + nle->sizeScale *= 0.8; + if ( nle->sizeScale < 0.7 ) { + nle->sizeScale = 0.7; + nle->leBounceSoundType = 0; + } + // move us a bit + VectorNormalize2( nle->pos.trDelta, dir ); + VectorMA( trace.endpos, 4.0 * le->sizeScale * i, dir, nle->pos.trBase ); + // randomize vel a bit + VectorMA( nle->pos.trDelta, VectorLength( nle->pos.trDelta ) * 0.3, bytedirs[rand() % NUMVERTEXNORMALS], nle->pos.trDelta ); + } + // we're done + CG_FreeLocalEntity( le ); +#endif // 0 + return; + } + } + + if ( le->pos.trType == TR_STATIONARY && le->leMarkType == LEMT_BLOOD ) { + + // leave a mark + if ( le->leMarkType ) { + CG_FragmentBounceMark( le, &trace ); + } + } + + // Ridah, add the flame + if ( hasFlame ) { + refEntity_t backupEnt; + + backupEnt = le->refEntity; + + le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha ); + VectorCopy( flameDir, le->refEntity.fireRiseDir ); + le->refEntity.customShader = cgs.media.onFireShader; + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.customShader = cgs.media.onFireShader2; + trap_R_AddRefEntityToScene( &le->refEntity ); + + le->refEntity = backupEnt; + } + + trap_R_AddRefEntityToScene( &le->refEntity ); +} + +// Ridah +/* +================ +CG_AddMovingTracer +================ +*/ +void CG_AddMovingTracer( localEntity_t *le ) { + vec3_t start, end, dir; + + BG_EvaluateTrajectory( &le->pos, cg.time, start, qfalse, -1 ); + VectorNormalize2( le->pos.trDelta, dir ); + VectorMA( start, cg_tracerLength.value, dir, end ); + + CG_DrawTracer( start, end ); +} + +/* +================ +CG_AddSparkElements +================ +*/ +void CG_AddSparkElements( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + float time; + float lifeFrac; + + time = (float)( cg.time - cg.frametime ); + + while ( 1 ) { + // calculate new position + BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin, qfalse, -1 ); + +// if ((le->endTime - le->startTime) > 500) { + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT ); + + // if stuck, kill it + if ( trace.startsolid ) { + // HACK, some walls screw up, so just pass through if starting in a solid + VectorCopy( newOrigin, trace.endpos ); + trace.fraction = 1.0; + } + + // moved some distance + VectorCopy( trace.endpos, le->refEntity.origin ); +/* + } else + { // just move it there + + VectorCopy( newOrigin, le->refEntity.origin ); + trace.fraction = 1.0; + + } +*/ + time += cg.frametime * trace.fraction; + + lifeFrac = (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime ); + + // add a trail + le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, + le, // rain - zinx's trail fix + le->refEntity.customShader, + le->refEntity.origin, + 200, + 1.0 - lifeFrac, // start alpha + 0.0, //1.0 - lifeFrac, // end alpha + lifeFrac * 2.0 * ( ( ( le->endTime - le->startTime ) > 400 ) + 1 ) * 1.5, + lifeFrac * 2.0 * ( ( ( le->endTime - le->startTime ) > 400 ) + 1 ) * 1.5 ); + + // if it is in a nodrop zone, remove it + // this keeps gibs from waiting at the bottom of pits of death + // and floating levels +// for some reason SFM1.BSP is one big NODROP zone +// if ( CG_PointContents( le->refEntity.origin, 0 ) & CONTENTS_NODROP ) { +// CG_FreeLocalEntity( le ); +// return; +// } + + if ( trace.fraction < 1.0 ) { + // just kill it + CG_FreeLocalEntity( le ); + return; +/* + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + // the intersection is a fraction of the frametime + le->pos.trTime = (int)time; +*/ + } + + if ( trace.fraction == 1.0 || time >= (float)cg.time ) { + return; + } + } + +} + +/* +================ +CG_AddFuseSparkElements +================ +*/ +void CG_AddFuseSparkElements( localEntity_t *le ) { + + float FUSE_SPARK_WIDTH = 1.0; + + int step = 10; + float time; + float lifeFrac; + static vec3_t whiteColor = {1,1,1}; + + time = (float)( le->lastTrailTime ); + + while ( time < cg.time ) { + + // calculate new position + BG_EvaluateTrajectory( &le->pos, time, le->refEntity.origin, qfalse, -1 ); + + lifeFrac = (float)( time - le->startTime ) / (float)( le->endTime - le->startTime ); + + //if (lifeFrac > 0.2) { + // add a trail + // rain - added le for zinx's trail fix + le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, le, cgs.media.sparkParticleShader, time, STYPE_STRETCH, le->refEntity.origin, (int)( lifeFrac * (float)( le->endTime - le->startTime ) / 2.0 ), + 1.0 /*(1.0 - lifeFrac)*/, 0.0, FUSE_SPARK_WIDTH * ( 1.0 - lifeFrac ), FUSE_SPARK_WIDTH * ( 1.0 - lifeFrac ), TJFL_SPARKHEADFLARE, whiteColor, whiteColor, 0, 0 ); + //} + + time += step; + + le->lastTrailTime = time; + } + +} + +/* +================ +CG_AddBloodElements +================ +*/ +void CG_AddBloodElements( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + float time; + float lifeFrac; + int numbounces; + + time = (float)( cg.time - cg.frametime ); + + for ( numbounces = 0; numbounces < 5; numbounces++ ) { + // calculate new position + BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin, qfalse, -1 ); + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT ); + + // if stuck, kill it + if ( trace.startsolid ) { + // HACK, some walls screw up, so just pass through if starting in a solid + VectorCopy( newOrigin, trace.endpos ); + trace.fraction = 1.0; + } + + // moved some distance + VectorCopy( trace.endpos, le->refEntity.origin ); + time += cg.frametime * trace.fraction; + + lifeFrac = (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime ); + + // add a trail + le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, + le, // rain - zinx's trail fix + cgs.media.bloodTrailShader, + le->refEntity.origin, + 200, + 1.0 - lifeFrac, // start alpha + 1.0 - lifeFrac, // end alpha + 3.0, + 5.0 ); + + if ( trace.fraction < 1.0 ) { + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + + // TODO: spawn a blood decal here? + + // the intersection is a fraction of the frametime + le->pos.trTime = (int)time; + } + + if ( trace.fraction == 1.0 || time >= (float)cg.time ) { + return; + } + } + +} + +/* +================ +CG_AddDebrisElements +================ +*/ +void CG_AddDebrisElements( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + float lifeFrac; + int t, step = 50; + + for ( t = le->lastTrailTime + step; t < cg.time; t += step ) { + // calculate new position + BG_EvaluateTrajectory( &le->pos, t, newOrigin, qfalse, -1 ); + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT ); + + // if stuck, kill it + if ( trace.startsolid ) { + // HACK, some walls screw up, so just pass through if starting in a solid + VectorCopy( newOrigin, trace.endpos ); + trace.fraction = 1.0; + } + + // moved some distance + VectorCopy( trace.endpos, le->refEntity.origin ); + + // add a trail + lifeFrac = (float)( t - le->startTime ) / (float)( le->endTime - le->startTime ); + +#if 0 + // fire +#if 1 // flame + if ( le->effectWidth > 0 ) { + le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, + le, // rain - zinx's trail fix + cgs.media.fireTrailShader, + le->refEntity.origin, + (int)( 500.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ), // trail life + 1.0, // alpha + 0.5, // end alpha + 3, // start width + le->effectWidth ); // end width +#else // spark line + if ( le->effectWidth > 0 ) { + le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, + le, // rain - zinx's trail fix + cgs.media.sparkParticleShader, + le->refEntity.origin, + (int)( 600.0 * ( 0.5 + 0.5 * ( 0.5 - lifeFrac ) ) ), // trail life + 1.0 - lifeFrac * 2, // alpha + 0.5 * ( 1.0 - lifeFrac ), // end alpha + 5.0 * ( 1.0 - lifeFrac ), // start width + 5.0 * ( 1.0 - lifeFrac ) ); // end width +#endif + } +#endif + + // smoke + if ( le->effectFlags & 1 ) { + le->headJuncIndex2 = CG_AddSmokeJunc( le->headJuncIndex2, + le, // rain - zinx's trail fix + cgs.media.smokeTrailShader, + le->refEntity.origin, + (int)( 2000.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ), // trail life + 1.0 * ( trace.fraction == 1.0 ) * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ), // alpha + 1, // start width + (int)( 60.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ) ); // end width + } + + // if it is in a nodrop zone, remove it + // this keeps gibs from waiting at the bottom of pits of death + // and floating levels +// if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { +// CG_FreeLocalEntity( le ); +// return; +// } + + if ( trace.fraction < 1.0 ) { + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + if ( VectorLengthSquared( le->pos.trDelta ) < SQR( 1 ) ) { + CG_FreeLocalEntity( le ); + return; + } + // the intersection is a fraction of the frametime + le->pos.trTime = t; + } + + le->lastTrailTime = t; + } + +} + +// Rafael Shrapnel +/* +=============== +CG_AddShrapnel +=============== +*/ +void CG_AddShrapnel( localEntity_t *le ) { + vec3_t newOrigin; + trace_t trace; + + if ( le->pos.trType == TR_STATIONARY ) { + // sink into the ground if near the removal time + int t; + float oldZ; + + t = le->endTime - cg.time; + if ( t < SINK_TIME ) { + // we must use an explicit lighting origin, otherwise the + // lighting would be lost as soon as the origin went + // into the ground + VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); + le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; + oldZ = le->refEntity.origin[2]; + le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); + trap_R_AddRefEntityToScene( &le->refEntity ); + le->refEntity.origin[2] = oldZ; + } else { + trap_R_AddRefEntityToScene( &le->refEntity ); + CG_AddParticleShrapnel( le ); + } + + return; + } + + // calculate new position + BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin, qfalse, -1 ); + + // trace a line from previous position to new position + CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); + if ( trace.fraction == 1.0 ) { + // still in free fall + VectorCopy( newOrigin, le->refEntity.origin ); + + if ( le->leFlags & LEF_TUMBLE ) { + vec3_t angles; + + BG_EvaluateTrajectory( &le->angles, cg.time, angles, qtrue, -1 ); + AnglesToAxis( angles, le->refEntity.axis ); + } + + trap_R_AddRefEntityToScene( &le->refEntity ); + CG_AddParticleShrapnel( le ); + return; + } + + // if it is in a nodrop zone, remove it + // this keeps gibs from waiting at the bottom of pits of death + // and floating levels + if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { + CG_FreeLocalEntity( le ); + return; + } + + // leave a mark + CG_FragmentBounceMark( le, &trace ); + + // do a bouncy sound + CG_FragmentBounceSound( le, &trace ); + + // reflect the velocity on the trace plane + CG_ReflectVelocity( le, &trace ); + + trap_R_AddRefEntityToScene( &le->refEntity ); + CG_AddParticleShrapnel( le ); +} +// done. + +/* +===================================================================== + +TRIVIAL LOCAL ENTITIES + +These only do simple scaling or modulation before passing to the renderer +===================================================================== +*/ + +/* +==================== +CG_AddFadeRGB +==================== +*/ +void CG_AddFadeRGB( localEntity_t *le ) { + refEntity_t *re; + float c; + + re = &le->refEntity; + + c = ( le->endTime - cg.time ) * le->lifeRate; + c *= 0xff; + + re->shaderRGBA[0] = le->color[0] * c; + re->shaderRGBA[1] = le->color[1] * c; + re->shaderRGBA[2] = le->color[2] * c; + re->shaderRGBA[3] = le->color[3] * c; + + trap_R_AddRefEntityToScene( re ); +} + +/* +================== +CG_AddMoveScaleFade +================== +*/ +static void CG_AddMoveScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + // fade / grow time +// c = ( le->endTime - cg.time ) * le->lifeRate; + if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { + // fade / grow time + c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime ); + } else { + // fade / grow time + c = ( le->endTime - cg.time ) * le->lifeRate; + } + + // Ridah, spark + if ( !( le->leFlags & LEF_NOFADEALPHA ) ) { + // done. + re->shaderRGBA[3] = 0xff * c * le->color[3]; + } + + if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { + c = ( le->endTime - cg.time ) * le->lifeRate; + re->radius = le->radius * ( 1.0 - c ) + 8; + } + + BG_EvaluateTrajectory( &le->pos, cg.time, re->origin, qfalse, -1 ); + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef_current->vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + +/* +=================== +CG_AddScaleFade + +For rocket smokes that hang in place, fade out, and are +removed if the view passes through them. +There are often many of these, so it needs to be simple. +=================== +*/ +static void CG_AddScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + // fade / grow time + c = ( le->endTime - cg.time ) * le->lifeRate; + + re->shaderRGBA[3] = 0xff * c * le->color[3]; + if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { + re->radius = le->radius * ( 1.0 - c ) + 8; + } + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef_current->vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + +/* +================= +CG_AddFallScaleFade + +This is just an optimized CG_AddMoveScaleFade +For blood mists that drift down, fade out, and are +removed if the view passes through them. +There are often 100+ of these, so it needs to be simple. +================= +*/ +static void CG_AddFallScaleFade( localEntity_t *le ) { + refEntity_t *re; + float c; + vec3_t delta; + float len; + + re = &le->refEntity; + + // fade time + c = ( le->endTime - cg.time ) * le->lifeRate; + + re->shaderRGBA[3] = 0xff * c * le->color[3]; + + re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2]; + + re->radius = le->radius * ( 1.0 - c ) + 16; + + // if the view would be "inside" the sprite, kill the sprite + // so it doesn't add too much overdraw + VectorSubtract( re->origin, cg.refdef_current->vieworg, delta ); + len = VectorLength( delta ); + if ( len < le->radius ) { + CG_FreeLocalEntity( le ); + return; + } + + trap_R_AddRefEntityToScene( re ); +} + + + +/* +================ +CG_AddExplosion +================ +*/ +static void CG_AddExplosion( localEntity_t *ex ) { + refEntity_t *ent; + + ent = &ex->refEntity; + + // add the entity + // RF, don't add if shader is invalid + if ( ent->customShader >= 0 ) { + trap_R_AddRefEntityToScene( ent ); + } + + // add the dlight + if ( ex->light || 1 ) { + float light; + + light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime ); + if ( light < 0.5 ) { + light = 1.0; + } else { + light = 1.0 - ( light - 0.5 ) * 2; + } + light = ex->light * light; + //% trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2], 0 ); + trap_R_AddLightToScene( ent->origin, 512, light, ex->lightColor[ 0 ], ex->lightColor[ 1 ], ex->lightColor[ 2 ], 0, 0 ); + } +} + +/* +================ +CG_AddSpriteExplosion +================ +*/ +static void CG_AddSpriteExplosion( localEntity_t *le ) { + refEntity_t re; + float c; + + re = le->refEntity; + + c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime ); + if ( c > 1 ) { + c = 1.0; // can happen during connection problems + } + + re.shaderRGBA[0] = 0xff; + re.shaderRGBA[1] = 0xff; + re.shaderRGBA[2] = 0xff; + re.shaderRGBA[3] = 0xff * c * 0.33; + + re.reType = RT_SPRITE; + re.radius = 42 * ( 1.0 - c ) + 30; + + // Ridah, move away from surface + VectorMA( le->pos.trBase, ( 1.0 - c ), le->pos.trDelta, re.origin ); + // done. + + // RF, don't add if shader is invalid + if ( re.customShader >= 0 ) { + trap_R_AddRefEntityToScene( &re ); + } + + // add the dlight + if ( le->light || 1 ) { + float light; + + // Ridah, modified this so the light fades out rather than shrinking + /* + light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime ); + if ( light < 0.5 ) { + light = 1.0; + } else { + light = 1.0 - ( light - 0.5 ) * 2; + } + light = le->light * light; + trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2], 0 ); + */ + light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime ); + if ( light < 0.5 ) { + light = 1.0; + } else { + light = 1.0 - ( light - 0.5 ) * 2; + } + //% trap_R_AddLightToScene(re.origin, le->light, light*le->lightColor[0], light*le->lightColor[1], light*le->lightColor[2], 0 ); + trap_R_AddLightToScene( re.origin, 320, light, le->lightColor[ 0 ], le->lightColor[ 1 ], le->lightColor[ 2 ], 0, 0 ); + // done. + } +} + + +//============================================================================== + +/* +=================== +CG_AddLocalEntities + +=================== +*/ +void CG_AddLocalEntities( void ) { + localEntity_t *le, *next; + + // walk the list backwards, so any new local entities generated + // (trails, marks, etc) will be present this frame + le = cg_activeLocalEntities.prev; + for ( ; le != &cg_activeLocalEntities ; le = next ) { + // grab next now, so if the local entity is freed we + // still have it + next = le->prev; + + if ( cg.time >= le->endTime ) { + CG_FreeLocalEntity( le ); + continue; + } + switch ( le->leType ) { + default: + CG_Error( "Bad leType: %i", le->leType ); + break; + + // Ridah + case LE_MOVING_TRACER: + CG_AddMovingTracer( le ); + break; + case LE_SPARK: + CG_AddSparkElements( le ); + break; + case LE_FUSE_SPARK: + CG_AddFuseSparkElements( le ); + break; + case LE_DEBRIS: + CG_AddDebrisElements( le ); + break; + case LE_BLOOD: + CG_AddBloodElements( le ); + break; +/* case LE_ZOMBIE_SPIRIT: + case LE_ZOMBIE_BAT: + CG_AddClientCritter( le ); + break;*/ + // done. + + case LE_MARK: + break; + + case LE_SPRITE_EXPLOSION: + CG_AddSpriteExplosion( le ); + break; + + case LE_EXPLOSION: + CG_AddExplosion( le ); + break; + + case LE_FRAGMENT: // gibs and brass + CG_AddFragment( le ); + break; + + case LE_MOVE_SCALE_FADE: // water bubbles + CG_AddMoveScaleFade( le ); + break; + + case LE_FADE_RGB: // teleporters, railtrails + CG_AddFadeRGB( le ); + break; + + case LE_FALL_SCALE_FADE: // gib blood trails + CG_AddFallScaleFade( le ); + break; + + case LE_SCALE_FADE: // rocket trails + CG_AddScaleFade( le ); + break; + + case LE_EMITTER: + CG_AddEmitter( le ); + break; + + } + } +} + diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c new file mode 100644 index 0000000..6839323 --- /dev/null +++ b/src/cgame/cg_main.c @@ -0,0 +1,2893 @@ +/* +=========================================================================== + +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: cg_main.c + * + * desc: initialization and primary entry point for cgame + * +*/ + + +#include "cg_local.h" + +displayContextDef_t cgDC; + +void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum, qboolean demoPlayback ); +void CG_Shutdown( void ); +qboolean CG_CheckExecKey( int key ); +extern itemDef_t* g_bindItem; +extern qboolean g_waitingForKey; + +/* +================ +vmMain + +This is the only way control passes into the module. +This must be the very first function compiled into the .q3vm file +================ +*/ +#if __GNUC__ >= 4 +#pragma GCC visibility push(default) +#endif +int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { +#if __GNUC__ >= 4 +#pragma GCC visibility pop +#endif + switch ( command ) { + case CG_INIT: + CG_Init( arg0, arg1, arg2, arg3 ); + cgs.initing = qfalse; + return 0; + case CG_SHUTDOWN: + CG_Shutdown(); + return 0; + case CG_CONSOLE_COMMAND: + return CG_ConsoleCommand(); + case CG_DRAW_ACTIVE_FRAME: + CG_DrawActiveFrame( arg0, arg1, arg2 ); + return 0; + case CG_CROSSHAIR_PLAYER: + return CG_CrosshairPlayer(); + case CG_LAST_ATTACKER: + return CG_LastAttacker(); + case CG_KEY_EVENT: + CG_KeyEvent( arg0, arg1 ); + return 0; + case CG_MOUSE_EVENT: + cgDC.cursorx = cgs.cursorX; + cgDC.cursory = cgs.cursorY; + CG_MouseEvent( arg0, arg1 ); + return 0; + case CG_EVENT_HANDLING: + CG_EventHandling( arg0, qtrue ); + return 0; + case CG_GET_TAG: + return CG_GetTag( arg0, (char *)arg1, (orientation_t *)arg2 ); + case CG_CHECKEXECKEY: + return CG_CheckExecKey( arg0 ); + case CG_WANTSBINDKEYS: + return ( g_waitingForKey && g_bindItem ) ? qtrue : qfalse; + case CG_MESSAGERECEIVED: + return -1; + default: + CG_Error( "vmMain: unknown command %i", command ); + break; + } + return -1; +} + +cg_t cg; +cgs_t cgs; +centity_t cg_entities[MAX_GENTITIES]; +weaponInfo_t cg_weapons[MAX_WEAPONS]; +itemInfo_t cg_items[MAX_ITEMS]; + +vmCvar_t cg_railTrailTime; +vmCvar_t cg_centertime; +vmCvar_t cg_runpitch; +vmCvar_t cg_runroll; +vmCvar_t cg_bobup; +vmCvar_t cg_bobpitch; +vmCvar_t cg_bobroll; +vmCvar_t cg_bobyaw; +vmCvar_t cg_swingSpeed; +vmCvar_t cg_shadows; +vmCvar_t cg_gibs; +vmCvar_t cg_draw2D; +vmCvar_t cg_drawFPS; +vmCvar_t cg_drawSnapshot; +vmCvar_t cg_drawCrosshair; +vmCvar_t cg_drawCrosshairNames; +vmCvar_t cg_drawCrosshairPickups; +vmCvar_t cg_weaponCycleDelay; //----(SA) added +vmCvar_t cg_cycleAllWeaps; +vmCvar_t cg_useWeapsForZoom; +vmCvar_t cg_crosshairSize; +vmCvar_t cg_crosshairX; +vmCvar_t cg_crosshairY; +vmCvar_t cg_crosshairHealth; +vmCvar_t cg_teamChatsOnly; +vmCvar_t cg_noVoiceChats; // NERVE - SMF +vmCvar_t cg_noVoiceText; // NERVE - SMF +vmCvar_t cg_drawStatus; +vmCvar_t cg_animSpeed; +vmCvar_t cg_drawSpreadScale; +vmCvar_t cg_debugAnim; +vmCvar_t cg_debugPosition; +vmCvar_t cg_debugEvents; +vmCvar_t cg_errorDecay; +vmCvar_t cg_nopredict; +vmCvar_t cg_noPlayerAnims; +vmCvar_t cg_showmiss; +vmCvar_t cg_footsteps; +vmCvar_t cg_markTime; +vmCvar_t cg_brassTime; +vmCvar_t cg_letterbox; //----(SA) added +vmCvar_t cg_drawGun; +vmCvar_t cg_cursorHints; //----(SA) added +vmCvar_t cg_gun_frame; +vmCvar_t cg_gun_x; +vmCvar_t cg_gun_y; +vmCvar_t cg_gun_z; +vmCvar_t cg_tracerChance; +vmCvar_t cg_tracerWidth; +vmCvar_t cg_tracerLength; +vmCvar_t cg_tracerSpeed; +vmCvar_t cg_autoswitch; +vmCvar_t cg_ignore; +vmCvar_t cg_fov; +vmCvar_t cg_zoomFov; +vmCvar_t cg_zoomStepBinoc; +vmCvar_t cg_zoomStepSniper; +vmCvar_t cg_zoomStepSnooper; +vmCvar_t cg_zoomStepFG; //----(SA) added +vmCvar_t cg_zoomDefaultBinoc; +vmCvar_t cg_zoomDefaultSniper; +vmCvar_t cg_zoomDefaultSnooper; +vmCvar_t cg_zoomDefaultFG; //----(SA) added +vmCvar_t cg_thirdPerson; +vmCvar_t cg_thirdPersonRange; +vmCvar_t cg_thirdPersonAngle; +vmCvar_t cg_stereoSeparation; +vmCvar_t cg_lagometer; +#ifdef ALLOW_GSYNC +vmCvar_t cg_synchronousClients; +#endif // ALLOW_GSYNC +vmCvar_t cg_teamChatTime; +vmCvar_t cg_teamChatHeight; +vmCvar_t cg_stats; +vmCvar_t cg_buildScript; +vmCvar_t cg_coronafardist; +vmCvar_t cg_coronas; +vmCvar_t cg_paused; +vmCvar_t cg_blood; +vmCvar_t cg_predictItems; +vmCvar_t cg_deferPlayers; +vmCvar_t cg_drawTeamOverlay; +vmCvar_t cg_enableBreath; +vmCvar_t cg_autoactivate; +vmCvar_t cg_blinktime; //----(SA) added + +vmCvar_t cg_smoothClients; +vmCvar_t pmove_fixed; +vmCvar_t pmove_msec; + +// Rafael - particle switch +vmCvar_t cg_wolfparticles; +// done + +// Ridah +vmCvar_t cg_gameType; +vmCvar_t cg_bloodTime; +vmCvar_t cg_norender; +vmCvar_t cg_skybox; + +// ydnar: say, team say, etc. +vmCvar_t cg_message; +vmCvar_t cg_messageType; +vmCvar_t cg_messagePlayer; +vmCvar_t cg_messagePlayerName; +vmCvar_t cg_movespeed; +vmCvar_t cg_cameraMode; +vmCvar_t cg_cameraOrbit; +vmCvar_t cg_cameraOrbitDelay; +vmCvar_t cg_timescaleFadeEnd; +vmCvar_t cg_timescaleFadeSpeed; +vmCvar_t cg_timescale; +vmCvar_t cg_smallFont; +vmCvar_t cg_bigFont; +vmCvar_t cg_noTaunt; // NERVE - SMF +vmCvar_t cg_voiceSpriteTime; // DHM - Nerve + +vmCvar_t cg_animState; + +vmCvar_t cg_drawCompass; +vmCvar_t cg_drawNotifyText; +vmCvar_t cg_quickMessageAlt; +vmCvar_t cg_popupLimboMenu; +vmCvar_t cg_descriptiveText; +// -NERVE - SMF + +vmCvar_t cg_redlimbotime; +vmCvar_t cg_bluelimbotime; + +vmCvar_t cg_antilag; + +vmCvar_t developer; + +// OSP +vmCvar_t authLevel; + +vmCvar_t cf_wstats; // Font scale for +wstats window +vmCvar_t cf_wtopshots; // Font scale for +wtopshots window + +//vmCvar_t cg_announcer; +vmCvar_t cg_autoAction; +vmCvar_t cg_autoReload; +vmCvar_t cg_bloodDamageBlend; +vmCvar_t cg_bloodFlash; +vmCvar_t cg_complaintPopUp; +vmCvar_t cg_crosshairAlpha; +vmCvar_t cg_crosshairAlphaAlt; +vmCvar_t cg_crosshairColor; +vmCvar_t cg_crosshairColorAlt; +vmCvar_t cg_crosshairPulse; +vmCvar_t cg_drawReinforcementTime; +vmCvar_t cg_drawWeaponIconFlash; +vmCvar_t cg_noAmmoAutoSwitch; +vmCvar_t cg_printObjectiveInfo; +vmCvar_t cg_specHelp; +vmCvar_t cg_uinfo; +vmCvar_t cg_useScreenshotJPEG; + +vmCvar_t ch_font; + +vmCvar_t demo_avifpsF1; +vmCvar_t demo_avifpsF2; +vmCvar_t demo_avifpsF3; +vmCvar_t demo_avifpsF4; +vmCvar_t demo_avifpsF5; +vmCvar_t demo_drawTimeScale; +vmCvar_t demo_infoWindow; + +vmCvar_t mv_sensitivity; + +vmCvar_t int_cl_maxpackets; +vmCvar_t int_cl_timenudge; +vmCvar_t int_m_pitch; +vmCvar_t int_sensitivity; +vmCvar_t int_timescale; +vmCvar_t int_ui_blackout; +// -OSP + +vmCvar_t cg_rconPassword; +vmCvar_t cg_refereePassword; +vmCvar_t cg_atmosphericEffects; +// START Mad Doc - TDF +vmCvar_t cg_drawRoundTimer; +// END Mad Doc - TDF + +#ifdef SAVEGAME_SUPPORT +vmCvar_t cg_reloading; +#endif // SAVEGAME_SUPPORT + +vmCvar_t cg_fastSolids; +vmCvar_t cg_instanttapout; + +vmCvar_t cg_debugSkills; +vmCvar_t cg_drawFireteamOverlay; +vmCvar_t cg_drawSmallPopupIcons; + +//bani - demo recording cvars +vmCvar_t cl_demorecording; +vmCvar_t cl_demofilename; +vmCvar_t cl_demooffset; +//bani - wav recording cvars +vmCvar_t cl_waverecording; +vmCvar_t cl_wavefilename; +vmCvar_t cl_waveoffset; +vmCvar_t cg_recording_statusline; + +typedef struct { + vmCvar_t *vmCvar; + char *cvarName; + char *defaultString; + int cvarFlags; + int modificationCount; +} cvarTable_t; + +cvarTable_t cvarTable[] = { + { &cg_ignore, "cg_ignore", "0", 0 }, // used for debugging + { &cg_autoswitch, "cg_autoswitch", "2", CVAR_ARCHIVE }, + { &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE }, + { &cg_gun_frame, "cg_gun_frame", "0", CVAR_TEMP }, + { &cg_cursorHints, "cg_cursorHints", "1", CVAR_ARCHIVE }, + { &cg_zoomFov, "cg_zoomfov", "22.5", CVAR_ARCHIVE }, + { &cg_zoomDefaultBinoc, "cg_zoomDefaultBinoc", "22.5", CVAR_ARCHIVE }, + { &cg_zoomDefaultSniper, "cg_zoomDefaultSniper", "20", CVAR_ARCHIVE }, // JPW NERVE changed per atvi req + { &cg_zoomDefaultSnooper, "cg_zoomDefaultSnooper", "40", CVAR_ARCHIVE }, // JPW NERVE made temp + { &cg_zoomDefaultFG, "cg_zoomDefaultFG", "55", CVAR_ARCHIVE }, //----(SA) added // JPW NERVE made temp + { &cg_zoomStepBinoc, "cg_zoomStepBinoc", "3", CVAR_ARCHIVE }, + { &cg_zoomStepSniper, "cg_zoomStepSniper", "2", CVAR_ARCHIVE }, + { &cg_zoomStepSnooper, "cg_zoomStepSnooper", "5", CVAR_ARCHIVE }, + { &cg_zoomStepFG, "cg_zoomStepFG", "10", CVAR_ARCHIVE }, //----(SA) added + { &cg_fov, "cg_fov", "90", CVAR_ARCHIVE }, + { &cg_letterbox, "cg_letterbox", "0", CVAR_TEMP }, //----(SA) added + { &cg_stereoSeparation, "cg_stereoSeparation", "0.4", CVAR_ARCHIVE }, + { &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE }, + { &cg_gibs, "cg_gibs", "1", CVAR_ARCHIVE }, +//bani - #127 - we now draw reticles always in non demoplayback +// { &cg_draw2D, "cg_draw2D", "1", CVAR_CHEAT }, // JPW NERVE changed per atvi req to prevent sniper rifle zoom cheats + { &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE }, + { &cg_drawSpreadScale, "cg_drawSpreadScale", "1", CVAR_ARCHIVE }, + { &cg_drawStatus, "cg_drawStatus", "1", CVAR_ARCHIVE }, + { &cg_drawFPS, "cg_drawFPS", "0", CVAR_ARCHIVE }, + { &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE }, + { &cg_drawCrosshair, "cg_drawCrosshair", "1", CVAR_ARCHIVE }, + { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, + { &cg_drawCrosshairPickups, "cg_drawCrosshairPickups", "1", CVAR_ARCHIVE }, + { &cg_useWeapsForZoom, "cg_useWeapsForZoom", "1", CVAR_ARCHIVE }, + { &cg_weaponCycleDelay, "cg_weaponCycleDelay", "150", CVAR_ARCHIVE }, //----(SA) added + { &cg_cycleAllWeaps, "cg_cycleAllWeaps", "1", CVAR_ARCHIVE }, + { &cg_crosshairSize, "cg_crosshairSize", "48", CVAR_ARCHIVE }, + { &cg_crosshairHealth, "cg_crosshairHealth", "0", CVAR_ARCHIVE }, + { &cg_crosshairX, "cg_crosshairX", "0", CVAR_ARCHIVE }, + { &cg_crosshairY, "cg_crosshairY", "0", CVAR_ARCHIVE }, + { &cg_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, // JPW NERVE + { &cg_markTime, "cg_marktime", "20000", CVAR_ARCHIVE }, + { &cg_lagometer, "cg_lagometer", "0", CVAR_ARCHIVE }, + { &cg_railTrailTime, "cg_railTrailTime", "400", CVAR_ARCHIVE }, + { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT }, + { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT }, + { &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT }, + { &cg_centertime, "cg_centertime", "5", CVAR_CHEAT }, // DHM - Nerve :: changed from 3 to 5 + { &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE}, + { &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE }, + { &cg_bobup, "cg_bobup", "0.005", CVAR_ARCHIVE }, + { &cg_bobpitch, "cg_bobpitch", "0.002", CVAR_ARCHIVE }, + { &cg_bobroll, "cg_bobroll", "0.002", CVAR_ARCHIVE }, + { &cg_bobyaw, "cg_bobyaw", "0.002", CVAR_ARCHIVE }, + + // JOSEPH 10-27-99 + { &cg_autoactivate, "cg_autoactivate", "1", CVAR_ARCHIVE }, + // END JOSEPH + + // Ridah, more fluid rotations + { &cg_swingSpeed, "cg_swingSpeed", "0.1", CVAR_CHEAT }, // was 0.3 for Q3 + { &cg_bloodTime, "cg_bloodTime", "120", CVAR_ARCHIVE }, + + { &cg_skybox, "cg_skybox", "1", CVAR_CHEAT }, + // done. + + // ydnar: say, team say, etc. + { &cg_message, "cg_message", "1", CVAR_TEMP }, + { &cg_messageType, "cg_messageType", "1", CVAR_TEMP }, + { &cg_messagePlayer, "cg_messagePlayer", "", CVAR_TEMP }, + { &cg_messagePlayerName, "cg_messagePlayerName", "", CVAR_TEMP }, + + { &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT }, + { &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT }, + { &cg_debugPosition, "cg_debugposition", "0", CVAR_CHEAT }, + { &cg_debugEvents, "cg_debugevents", "0", CVAR_CHEAT }, + { &cg_errorDecay, "cg_errordecay", "100", 0 }, + { &cg_nopredict, "cg_nopredict", "0", CVAR_CHEAT }, + { &cg_noPlayerAnims, "cg_noplayeranims", "0", CVAR_CHEAT }, + { &cg_showmiss, "cg_showmiss", "0", 0 }, + { &cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT }, + { &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT }, + { &cg_tracerWidth, "cg_tracerwidth", "0.8", CVAR_CHEAT }, + { &cg_tracerSpeed, "cg_tracerSpeed", "4500", CVAR_CHEAT }, + { &cg_tracerLength, "cg_tracerlength", "160", CVAR_CHEAT }, + { &cg_thirdPersonRange, "cg_thirdPersonRange", "80", CVAR_CHEAT }, // JPW NERVE per atvi req + { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT }, + { &cg_thirdPerson, "cg_thirdPerson", "0", CVAR_CHEAT }, // JPW NERVE per atvi req + { &cg_teamChatTime, "cg_teamChatTime", "8000", CVAR_ARCHIVE }, + { &cg_teamChatHeight, "cg_teamChatHeight", "8", CVAR_ARCHIVE }, + { &cg_coronafardist, "cg_coronafardist", "1536", CVAR_ARCHIVE }, + { &cg_coronas, "cg_coronas", "1", CVAR_ARCHIVE }, + { &cg_predictItems, "cg_predictItems", "1", CVAR_ARCHIVE }, + { &cg_deferPlayers, "cg_deferPlayers", "1", CVAR_ARCHIVE }, + { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "2", CVAR_ARCHIVE }, + { &cg_stats, "cg_stats", "0", 0 }, + { &cg_blinktime, "cg_blinktime", "100", CVAR_ARCHIVE}, //----(SA) added + + { &cg_enableBreath, "cg_enableBreath", "1", CVAR_SERVERINFO}, + { &cg_cameraOrbit, "cg_cameraOrbit", "0", CVAR_CHEAT}, + { &cg_cameraOrbitDelay, "cg_cameraOrbitDelay", "50", CVAR_ARCHIVE}, + { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", 0}, + { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", 0}, + { &cg_timescale, "timescale", "1", 0}, +// { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, + { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, + + { &pmove_fixed, "pmove_fixed", "0", 0}, + { &pmove_msec, "pmove_msec", "8", 0}, + + { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, // NERVE - SMF + { &cg_voiceSpriteTime, "cg_voiceSpriteTime", "6000", CVAR_ARCHIVE}, // DHM - Nerve + + { &cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, + { &cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, + + { &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE }, + { &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE }, // NERVE - SMF + { &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE }, // NERVE - SMF + + // the following variables are created in other parts of the system, + // but we also reference them here + + { &cg_buildScript, "com_buildScript", "0", 0 }, // force loading of all possible data amd error on failures + { &cg_paused, "cl_paused", "0", CVAR_ROM }, + + { &cg_blood, "cg_showblood", "1", CVAR_ARCHIVE }, +#ifdef ALLOW_GSYNC + { &cg_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO | CVAR_CHEAT }, // communicated by systeminfo +#endif // ALLOW_GSYNC + + // Rafael - particle switch + { &cg_wolfparticles, "cg_wolfparticles", "1", CVAR_ARCHIVE }, + { &cg_gameType, "g_gametype", "0", 0 }, // communicated by systeminfo + { &cg_norender, "cg_norender", "0", 0 }, // only used during single player, to suppress rendering until the server is ready + { &cg_bluelimbotime, "", "30000", 0 }, // communicated by systeminfo + { &cg_redlimbotime, "", "30000", 0 }, // communicated by systeminfo + { &cg_movespeed, "g_movespeed", "76", 0 }, // actual movespeed of player + { &cg_animState, "cg_animState", "0", CVAR_CHEAT}, + { &cg_drawCompass, "cg_drawCompass", "1", CVAR_ARCHIVE }, + { &cg_drawNotifyText, "cg_drawNotifyText", "1", CVAR_ARCHIVE }, + { &cg_quickMessageAlt, "cg_quickMessageAlt", "0", CVAR_ARCHIVE }, + { &cg_popupLimboMenu, "cg_popupLimboMenu", "1", CVAR_ARCHIVE }, + { &cg_descriptiveText, "cg_descriptiveText", "1", CVAR_ARCHIVE }, + { &cg_antilag, "g_antilag", "1", 0 }, + { &developer, "developer", "0", CVAR_CHEAT }, + { &cf_wstats, "cf_wstats", "1.2", CVAR_ARCHIVE }, + { &cf_wtopshots, "cf_wtopshots", "1.0", CVAR_ARCHIVE }, + //{ &cg_announcer, "cg_announcer", "1", CVAR_ARCHIVE }, + { &cg_autoAction, "cg_autoAction", "0", CVAR_ARCHIVE }, + { &cg_autoReload, "cg_autoReload", "1", CVAR_ARCHIVE }, + { &cg_bloodDamageBlend, "cg_bloodDamageBlend", "1.0", CVAR_ARCHIVE }, + { &cg_bloodFlash, "cg_bloodFlash", "1.0", CVAR_ARCHIVE }, + { &cg_complaintPopUp, "cg_complaintPopUp", "1", CVAR_ARCHIVE }, + { &cg_crosshairAlpha, "cg_crosshairAlpha", "1.0", CVAR_ARCHIVE }, + { &cg_crosshairAlphaAlt, "cg_crosshairAlphaAlt", "1.0", CVAR_ARCHIVE }, + { &cg_crosshairColor, "cg_crosshairColor", "White", CVAR_ARCHIVE }, + { &cg_crosshairColorAlt, "cg_crosshairColorAlt", "White", CVAR_ARCHIVE }, + { &cg_crosshairPulse, "cg_crosshairPulse", "1", CVAR_ARCHIVE }, + { &cg_drawReinforcementTime, "cg_drawReinforcementTime", "1", CVAR_ARCHIVE }, + { &cg_drawWeaponIconFlash, "cg_drawWeaponIconFlash", "0", CVAR_ARCHIVE }, + { &cg_noAmmoAutoSwitch, "cg_noAmmoAutoSwitch", "1", CVAR_ARCHIVE }, + { &cg_printObjectiveInfo, "cg_printObjectiveInfo", "1", CVAR_ARCHIVE }, + { &cg_specHelp, "cg_specHelp", "1", CVAR_ARCHIVE }, + { &cg_uinfo, "cg_uinfo", "0", CVAR_ROM | CVAR_USERINFO }, + { &cg_useScreenshotJPEG, "cg_useScreenshotJPEG", "1", CVAR_ARCHIVE }, + + { &demo_avifpsF1, "demo_avifpsF1", "0", CVAR_ARCHIVE }, + { &demo_avifpsF2, "demo_avifpsF2", "10", CVAR_ARCHIVE }, + { &demo_avifpsF3, "demo_avifpsF3", "15", CVAR_ARCHIVE }, + { &demo_avifpsF4, "demo_avifpsF4", "20", CVAR_ARCHIVE }, + { &demo_avifpsF5, "demo_avifpsF5", "24", CVAR_ARCHIVE }, + { &demo_drawTimeScale, "demo_drawTimeScale", "1", CVAR_ARCHIVE }, + { &demo_infoWindow, "demo_infoWindow", "1", CVAR_ARCHIVE }, + +#ifdef MV_SUPPORT + { &mv_sensitivity, "mv_sensitivity", "20", CVAR_ARCHIVE }, +#endif + + // Engine mappings + { &int_cl_maxpackets, "cl_maxpackets", "30", CVAR_ARCHIVE }, + { &int_cl_timenudge, "cl_timenudge", "0", CVAR_ARCHIVE }, + { &int_m_pitch, "m_pitch", "0.022", CVAR_ARCHIVE }, + { &int_sensitivity, "sensitivity", "5", CVAR_ARCHIVE }, + { &int_ui_blackout, "ui_blackout", "0", CVAR_ROM }, + // -OSP + + { &cg_atmosphericEffects, "cg_atmosphericEffects", "1", CVAR_ARCHIVE }, + { &authLevel, "authLevel", "0", CVAR_TEMP | CVAR_ROM}, + + { &cg_rconPassword, "auth_rconPassword", "", CVAR_TEMP}, + { &cg_refereePassword, "auth_refereePassword", "", CVAR_TEMP}, + + { &cg_drawRoundTimer, "cg_drawRoundTimer", "1", CVAR_ARCHIVE }, + +#ifdef SAVEGAME_SUPPORT + { &cg_reloading, "g_reloading", "0", 0 }, +#endif // SAVEGAME_SUPPORT + + // Gordon: optimization cvars: 18/12/02 enabled by default now + { &cg_fastSolids, "cg_fastSolids", "1", CVAR_ARCHIVE }, + + { &cg_instanttapout, "cg_instanttapout", "0", CVAR_ARCHIVE }, + { &cg_debugSkills, "cg_debugSkills", "0", 0 }, + { NULL, "cg_etVersion", "", CVAR_USERINFO | CVAR_ROM }, + { &cg_drawFireteamOverlay, "cg_drawFireteamOverlay", "1", CVAR_ARCHIVE }, + { &cg_drawSmallPopupIcons, "cg_drawSmallPopupIcons", "0", CVAR_ARCHIVE }, + + //bani - demo recording cvars + { &cl_demorecording, "cl_demorecording", "0", CVAR_ROM }, + { &cl_demofilename, "cl_demofilename", "", CVAR_ROM }, + { &cl_demooffset, "cl_demooffset", "0", CVAR_ROM }, + //bani - wav recording cvars + { &cl_waverecording, "cl_waverecording", "0", CVAR_ROM }, + { &cl_wavefilename, "cl_wavefilename", "", CVAR_ROM }, + { &cl_waveoffset, "cl_waveoffset", "0", CVAR_ROM }, + { &cg_recording_statusline, "cg_recording_statusline", "9", CVAR_ARCHIVE }, +}; + +int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); +qboolean cvarsLoaded = qfalse; +void CG_setClientFlags( void ); + + +/* +================= +CG_RegisterCvars +================= +*/ +void CG_RegisterCvars( void ) { + int i; + cvarTable_t *cv; + char var[MAX_TOKEN_CHARS]; + + trap_Cvar_Set( "cg_letterbox", "0" ); // force this for people who might have it in their + + for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { + trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); + if ( cv->vmCvar != NULL ) { + // rain - force the update to range check this cvar on first run + if ( cv->vmCvar == &cg_errorDecay ) { + cv->modificationCount = !cv->vmCvar->modificationCount; + } else { + cv->modificationCount = cv->vmCvar->modificationCount; + } + } + } + + // see if we are also running the server on this machine + trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) ); + cgs.localServer = atoi( var ); + + // Gordon: um, here, why? + CG_setClientFlags(); + BG_setCrosshair( cg_crosshairColor.string, cg.xhairColor, cg_crosshairAlpha.value, "cg_crosshairColor" ); + BG_setCrosshair( cg_crosshairColorAlt.string, cg.xhairColorAlt, cg_crosshairAlphaAlt.value, "cg_crosshairColorAlt" ); + + cvarsLoaded = qtrue; +} + +/* +================= +CG_UpdateCvars +================= +*/ +void CG_UpdateCvars( void ) { + int i; + qboolean fSetFlags = qfalse; + cvarTable_t *cv; + + if ( !cvarsLoaded ) { + return; + } + + for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { + if ( cv->vmCvar ) { + trap_Cvar_Update( cv->vmCvar ); + if ( cv->modificationCount != cv->vmCvar->modificationCount ) { + cv->modificationCount = cv->vmCvar->modificationCount; + + // Check if we need to update any client flags to be sent to the server + if ( cv->vmCvar == &cg_autoAction || cv->vmCvar == &cg_autoReload || + cv->vmCvar == &int_cl_timenudge || cv->vmCvar == &int_cl_maxpackets || + cv->vmCvar == &cg_autoactivate || cv->vmCvar == &cg_predictItems ) { + fSetFlags = qtrue; + } else if ( cv->vmCvar == &cg_crosshairColor || cv->vmCvar == &cg_crosshairAlpha ) { + BG_setCrosshair( cg_crosshairColor.string, cg.xhairColor, cg_crosshairAlpha.value, "cg_crosshairColor" ); + } else if ( cv->vmCvar == &cg_crosshairColorAlt || cv->vmCvar == &cg_crosshairAlphaAlt ) { + BG_setCrosshair( cg_crosshairColorAlt.string, cg.xhairColorAlt, cg_crosshairAlphaAlt.value, "cg_crosshairColorAlt" ); + } else if ( cv->vmCvar == &cg_rconPassword && *cg_rconPassword.string ) { + trap_SendConsoleCommand( va( "rconAuth %s", cg_rconPassword.string ) ); + } else if ( cv->vmCvar == &cg_refereePassword && *cg_refereePassword.string ) { + trap_SendConsoleCommand( va( "ref %s", cg_refereePassword.string ) ); + } else if ( cv->vmCvar == &demo_infoWindow ) { + if ( demo_infoWindow.integer == 0 && cg.demohelpWindow == SHOW_ON ) { + CG_ShowHelp_On( &cg.demohelpWindow ); + } else if ( demo_infoWindow.integer > 0 && cg.demohelpWindow != SHOW_ON ) { + CG_ShowHelp_On( &cg.demohelpWindow ); + } + } else if ( cv->vmCvar == &cg_errorDecay ) { + // rain - cap errordecay because + // prediction is EXTREMELY broken + // right now. + if ( cg_errorDecay.value < 0.0 ) { + trap_Cvar_Set( "cg_errorDecay", "0" ); + } else if ( cg_errorDecay.value > 500.0 ) { + trap_Cvar_Set( "cg_errorDecay", "500" ); + } + } + } + } + } + + // Send any relevent updates + if ( fSetFlags ) { + CG_setClientFlags(); + } +} + +void CG_setClientFlags( void ) { + if ( cg.demoPlayback ) { + return; + } + + cg.pmext.bAutoReload = ( cg_autoReload.integer > 0 ); + trap_Cvar_Set( "cg_uinfo", va( "%d %d %d", + // Client Flags + ( + ( ( cg_autoReload.integer > 0 ) ? CGF_AUTORELOAD : 0 ) | + ( ( cg_autoAction.integer & AA_STATSDUMP ) ? CGF_STATSDUMP : 0 ) | + ( ( cg_autoactivate.integer > 0 ) ? CGF_AUTOACTIVATE : 0 ) | + ( ( cg_predictItems.integer > 0 ) ? CGF_PREDICTITEMS : 0 ) + // Add more in here, as needed + ), + + // Timenudge + int_cl_timenudge.integer, + // MaxPackets + int_cl_maxpackets.integer + ) ); +} + +int CG_CrosshairPlayer( void ) { + if ( cg.time > ( cg.crosshairClientTime + 1000 ) ) { + return -1; + } + return cg.crosshairClientNum; +} + +int CG_LastAttacker( void ) { + // OSP - used for messaging clients in the currect active window + if ( cg.mvTotalClients > 0 ) { + return( cg.mvCurrentActive->mvInfo & MV_PID ); + } + // OSP + return( ( !cg.attackerTime ) ? -1 : cg.snap->ps.persistant[PERS_ATTACKER] ); +} + +void QDECL CG_Printf( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, msg ); + Q_vsnprintf( text, sizeof( text ), msg, argptr ); + va_end( argptr ); + if ( !Q_strncmp( text, "[cgnotify]", 10 ) ) { + char buf[1024]; + + if ( !cg_drawNotifyText.integer ) { + Q_strncpyz( buf, &text[10], 1013 ); + trap_Print( buf ); + return; + } + + CG_AddToNotify( &text[10] ); + Q_strncpyz( buf, &text[10], 1013 ); + Q_strncpyz( text, "[skipnotify]", 13 ); + Q_strcat( text, 1011, buf ); + } + + trap_Print( text ); +} + +void QDECL CG_Error( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, msg ); + Q_vsnprintf( text, sizeof( text ), msg, argptr ); + va_end( argptr ); + + trap_Error( text ); +} + +#ifndef CGAME_HARD_LINKED +// this is only here so the functions in q_shared.c and bg_*.c can link (FIXME) + +void QDECL Com_Error( int level, const char *error, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + + CG_Error( "%s", text ); +} + +void QDECL Com_Printf( const char *msg, ... ) { + va_list argptr; + char text[1024]; + + va_start( argptr, msg ); + Q_vsnprintf( text, sizeof( text ), msg, argptr ); + va_end( argptr ); + + CG_Printf( "%s", text ); +} + +#endif + +/* +================ +CG_Argv +================ +*/ +const char *CG_Argv( int arg ) { + static char buffer[MAX_STRING_CHARS]; + + trap_Argv( arg, buffer, sizeof( buffer ) ); + + return buffer; +} + + +// Cleans a string for filesystem compatibility +void CG_nameCleanFilename( const char *pszIn, char *pszOut, unsigned int dwOutSize ) { + unsigned int dwCurrLength = 0; + + while ( *pszIn && dwCurrLength < dwOutSize ) { + if ( *pszIn == 27 || *pszIn == '^' ) { + pszIn++; + dwCurrLength++; + + if ( *pszIn ) { + pszIn++; // skip color code + dwCurrLength++; + continue; + } + } + + // Illegal Windows characters + if ( *pszIn == '\\' || *pszIn == '/' || *pszIn == ':' || *pszIn == '"' || + *pszIn == '*' || *pszIn == '?' || *pszIn == '<' || *pszIn == '>' || + *pszIn == '|' || *pszIn == '.' ) { + pszIn++; + dwCurrLength++; + continue; + } + + if ( *pszIn <= 32 ) { + pszIn++; + dwCurrLength++; + continue; + } + + *pszOut++ = *pszIn++; + dwCurrLength++; + } + + *pszOut = 0; +} + +// Standard naming for screenshots/demos +char *CG_generateFilename( void ) { + qtime_t ct; +// int index = (cg.snap == NULL || (cg.snap->ps.pm_flags & PMF_LIMBO)) ? cg.clientNum : cg.snap->ps.clientNum; +// char strCleanName[64]; + const char *pszServerInfo = CG_ConfigString( CS_SERVERINFO ); +// const char *pszPlayerInfo = CG_ConfigString(CS_PLAYERS + index); + + trap_RealTime( &ct ); +// CG_nameCleanFilename(Info_ValueForKey(pszPlayerInfo, "n"), strCleanName, sizeof(strCleanName)); + return( va( "%d-%02d-%02d-%02d%02d%02d-%s%s", + 1900 + ct.tm_year, ct.tm_mon + 1,ct.tm_mday, + ct.tm_hour, ct.tm_min, ct.tm_sec, + Info_ValueForKey( pszServerInfo, "mapname" ), + ( cg.mvTotalClients < 1 ) ? "" : "-MVD" ) ); +} + +int CG_findClientNum( char *s ) { + int id; + char s2[64], n2[64]; + qboolean fIsNumber = qtrue; + + // See if its a number or string + for ( id = 0; id < strlen( s ) && s[id] != 0; id++ ) { + if ( s[id] < '0' || s[id] > '9' ) { + fIsNumber = qfalse; + break; + } + } + + // numeric values are just slot numbers + if ( fIsNumber ) { + id = atoi( s ); + if ( id >= 0 && id < cgs.maxclients && cgs.clientinfo[id].infoValid ) { + return( id ); + } + } + + // check for a name match + BG_cleanName( s, s2, sizeof( s2 ), qfalse ); + for ( id = 0; id < cgs.maxclients; id++ ) { + if ( !cgs.clientinfo[id].infoValid ) { + continue; + } + + BG_cleanName( cgs.clientinfo[id].name, n2, sizeof( n2 ), qfalse ); + if ( !Q_stricmp( n2, s2 ) ) { + return( id ); + } + } + + CG_Printf( "[cgnotify]%s ^3%s^7 %s.\n", CG_TranslateString( "User" ), s, CG_TranslateString( "is not on the server" ) ); + return( -1 ); +} + +void CG_printConsoleString( char *str ) { + CG_Printf( "[skipnotify]%s", str ); +} + +void CG_LoadObjectiveData( void ) { + pc_token_t token, token2; + int handle; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + handle = trap_PC_LoadSource( va( "maps/%s_lms.objdata", Q_strlwr( cgs.rawmapname ) ) ); + } else { + handle = trap_PC_LoadSource( va( "maps/%s.objdata", Q_strlwr( cgs.rawmapname ) ) ); + } + + if ( !handle ) { + return; + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( !Q_stricmp( token.string, "wm_mapdescription" ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + CG_Printf( "^1ERROR: bad objdata line : team parameter required\n" ); + break; + } + + if ( !trap_PC_ReadToken( handle, &token2 ) ) { + CG_Printf( "^1ERROR: bad objdata line : description parameter required\n" ); + break; + } + + if ( !Q_stricmp( token.string, "axis" ) ) { + Q_strncpyz( cg.objMapDescription_Axis, token2.string, sizeof( cg.objMapDescription_Axis ) ); + } else if ( !Q_stricmp( token.string, "allied" ) ) { + Q_strncpyz( cg.objMapDescription_Allied, token2.string, sizeof( cg.objMapDescription_Allied ) ); + } else if ( !Q_stricmp( token.string, "neutral" ) ) { + Q_strncpyz( cg.objMapDescription_Neutral, token2.string, sizeof( cg.objMapDescription_Neutral ) ); + } + } else if ( !Q_stricmp( token.string, "wm_objective_axis_desc" ) ) { + int i; + + if ( !PC_Int_Parse( handle, &i ) ) { + CG_Printf( "^1ERROR: bad objdata line : number parameter required\n" ); + break; + } + + if ( !trap_PC_ReadToken( handle, &token ) ) { + CG_Printf( "^1ERROR: bad objdata line : description parameter required\n" ); + break; + } + + i--; + + if ( i < 0 || i >= MAX_OBJECTIVES ) { + CG_Printf( "^1ERROR: bad objdata line : invalid objective number\n" ); + break; + } + + Q_strncpyz( cg.objDescription_Axis[i], token.string, sizeof( cg.objDescription_Axis[i] ) ); + } else if ( !Q_stricmp( token.string, "wm_objective_allied_desc" ) ) { + int i; + + if ( !PC_Int_Parse( handle, &i ) ) { + CG_Printf( "^1ERROR: bad objdata line : number parameter required\n" ); + break; + } + + if ( !trap_PC_ReadToken( handle, &token ) ) { + CG_Printf( "^1ERROR: bad objdata line : description parameter required\n" ); + break; + } + + i--; + + if ( i < 0 || i >= MAX_OBJECTIVES ) { + CG_Printf( "^1ERROR: bad objdata line : invalid objective number\n" ); + break; + } + + Q_strncpyz( cg.objDescription_Allied[i], token.string, sizeof( cg.objDescription_Allied[i] ) ); + } + } + + trap_PC_FreeSource( handle ); +} + +//======================================================================== +void CG_SetupDlightstyles( void ) { + int i, j; + char *str; + char *token; + int entnum; + centity_t *cent; + + cg.lightstylesInited = qtrue; + + for ( i = 1; i < MAX_DLIGHT_CONFIGSTRINGS; i++ ) + { + str = (char *) CG_ConfigString( CS_DLIGHTS + i ); + if ( !strlen( str ) ) { + break; + } + + token = COM_Parse( &str ); // ent num + entnum = atoi( token ); + cent = &cg_entities[entnum]; + + token = COM_Parse( &str ); // stylestring + Q_strncpyz( cent->dl_stylestring, token, strlen( token ) ); + + token = COM_Parse( &str ); // offset + cent->dl_frame = atoi( token ); + cent->dl_oldframe = cent->dl_frame - 1; + if ( cent->dl_oldframe < 0 ) { + cent->dl_oldframe = strlen( cent->dl_stylestring ); + } + + token = COM_Parse( &str ); // sound id + cent->dl_sound = atoi( token ); + + token = COM_Parse( &str ); // attenuation + cent->dl_atten = atoi( token ); + + for ( j = 0; j < strlen( cent->dl_stylestring ); j++ ) { + + cent->dl_stylestring[j] += cent->dl_atten; // adjust character for attenuation/amplification + + // clamp result + if ( cent->dl_stylestring[j] < 'a' ) { + cent->dl_stylestring[j] = 'a'; + } + if ( cent->dl_stylestring[j] > 'z' ) { + cent->dl_stylestring[j] = 'z'; + } + } + + cent->dl_backlerp = 0.0; + cent->dl_time = cg.time; + } + +} + +//======================================================================== + +/* +================= +CG_RegisterItemSounds + +The server says this item is used on this level +================= +*/ +static void CG_RegisterItemSounds( int itemNum ) { + gitem_t *item; + char data[MAX_QPATH]; + char *s, *start; + int len; + + item = &bg_itemlist[ itemNum ]; + + if ( item->pickup_sound && *item->pickup_sound ) { + trap_S_RegisterSound( item->pickup_sound, qfalse ); + } + + // parse the space seperated precache string for other media + s = item->sounds; + if ( !s || !s[0] ) { + return; + } + + while ( *s ) { + start = s; + while ( *s && *s != ' ' ) { + s++; + } + + len = s - start; + if ( len >= MAX_QPATH || len < 5 ) { + CG_Error( "PrecacheItem: %s has bad precache string", + item->classname ); + return; + } + memcpy( data, start, len ); + data[len] = 0; + if ( *s ) { + s++; + } + + if ( !strcmp( data + len - 3, "wav" ) ) { + trap_S_RegisterSound( data, qfalse ); + } + } +} + + +/* +================= +CG_RegisterSounds + +called during a precache command +================= +*/ +static void CG_RegisterSounds( void ) { + int i; + char name[MAX_QPATH]; + const char *soundName; + bg_speaker_t *speaker; + + // NERVE - SMF - voice commands + CG_LoadVoiceChats(); + + // Ridah, init sound scripts + CG_SoundInit(); + // done. + + BG_ClearScriptSpeakerPool(); + + BG_LoadSpeakerScript( va( "sound/maps/%s.sps", cgs.rawmapname ) ); + + for ( i = 0; i < BG_NumScriptSpeakers(); i++ ) { + speaker = BG_GetScriptSpeaker( i ); + + speaker->noise = trap_S_RegisterSound( speaker->filename, qfalse ); + } + + cgs.media.noAmmoSound = trap_S_RegisterSound( "sound/weapons/misc/fire_dry.wav", qfalse ); + cgs.media.noFireUnderwater = trap_S_RegisterSound( "sound/weapons/misc/fire_water.wav", qfalse ); + cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/misc/change.wav", qfalse ); + cgs.media.landHurt = trap_S_RegisterSound( "sound/player/land_hurt.wav", qfalse ); + cgs.media.gibSound = trap_S_RegisterSound( "sound/player/gib.wav", qfalse ); + cgs.media.dynamitebounce1 = trap_S_RegisterSound( "sound/weapons/dynamite/dynamite_bounce.wav", qfalse ); + cgs.media.satchelbounce1 = trap_S_RegisterSound( "sound/weapons/satchel/satchel_bounce.wav", qfalse ); + cgs.media.landminebounce1 = trap_S_RegisterSound( "sound/weapons/landmine/mine_bounce.wav", qfalse ); + + cgs.media.watrInSound = trap_S_RegisterSound( "sound/player/water_in.wav", qfalse ); + cgs.media.watrOutSound = trap_S_RegisterSound( "sound/player/water_out.wav", qfalse ); + cgs.media.watrUnSound = trap_S_RegisterSound( "sound/player/water_un.wav", qfalse ); + cgs.media.watrGaspSound = trap_S_RegisterSound( "sound/player/gasp.wav", qfalse ); + cgs.media.underWaterSound = trap_S_RegisterSound( "sound/player/underwater.wav", qfalse ); + + for ( i = 0; i < 2; i++ ) { + cgs.media.grenadebounce[FOOTSTEP_NORMAL][i] = \ + cgs.media.grenadebounce[FOOTSTEP_GRAVEL][i] = \ + cgs.media.grenadebounce[FOOTSTEP_SPLASH][i] = trap_S_RegisterSound( va( "sound/weapons/grenade/bounce_hard%i.wav", i + 1 ), qfalse ); + + cgs.media.grenadebounce[FOOTSTEP_METAL][i] = \ + cgs.media.grenadebounce[FOOTSTEP_ROOF][i] = trap_S_RegisterSound( va( "sound/weapons/grenade/bounce_metal%i.wav", i + 1 ), qfalse ); + + cgs.media.grenadebounce[FOOTSTEP_WOOD][i] = trap_S_RegisterSound( va( "sound/weapons/grenade/bounce_wood%i.wav", i + 1 ), qfalse ); + + cgs.media.grenadebounce[FOOTSTEP_GRASS][i] = \ + cgs.media.grenadebounce[FOOTSTEP_SNOW][i] = \ + cgs.media.grenadebounce[FOOTSTEP_CARPET][i] = trap_S_RegisterSound( va( "sound/weapons/grenade/bounce_soft%i.wav", i + 1 ), qfalse ); + + } + + cgs.media.landSound[FOOTSTEP_NORMAL] = trap_S_RegisterSound( "sound/player/footsteps/stone_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_SPLASH] = trap_S_RegisterSound( "sound/player/footsteps/water_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_METAL] = trap_S_RegisterSound( "sound/player/footsteps/metal_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_WOOD] = trap_S_RegisterSound( "sound/player/footsteps/wood_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_GRASS] = trap_S_RegisterSound( "sound/player/footsteps/grass_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_GRAVEL] = trap_S_RegisterSound( "sound/player/footsteps/gravel_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_ROOF] = trap_S_RegisterSound( "sound/player/footsteps/roof_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_SNOW] = trap_S_RegisterSound( "sound/player/footsteps/snow_jump.wav", qfalse ); + cgs.media.landSound[FOOTSTEP_CARPET] = trap_S_RegisterSound( "sound/player/footsteps/carpet_jump.wav", qfalse ); + + for ( i = 0; i < 4; i++ ) { + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/stone%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_NORMAL][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/water%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_SPLASH][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/metal%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_METAL][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/wood%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_WOOD][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/grass%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_GRASS][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/gravel%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_GRAVEL][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/roof%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_ROOF][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/snow%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_SNOW][i] = trap_S_RegisterSound( name, qfalse ); + + Com_sprintf( name, sizeof( name ), "sound/player/footsteps/carpet%i.wav", i + 1 ); + cgs.media.footsteps[FOOTSTEP_CARPET][i] = trap_S_RegisterSound( name, qfalse ); + } + + for ( i = 1 ; i < bg_numItems ; i++ ) { + CG_RegisterItemSounds( i ); + } + + for ( i = 1 ; i < MAX_SOUNDS ; i++ ) { + soundName = CG_ConfigString( CS_SOUNDS + i ); + if ( !soundName[0] ) { + break; + } + if ( soundName[0] == '*' ) { + continue; // custom sound + } + + // Ridah, register sound scripts seperately + if ( !strstr( soundName, ".wav" ) ) { + CG_SoundScriptPrecache( soundName ); + } else { + cgs.gameSounds[i] = trap_S_RegisterSound( soundName, qfalse ); // FIXME: allow option to compress? + } + } + +/* + // OSP + cgs.media.countFight = trap_S_RegisterSound( "sound/osp/fight.wav" ); + cgs.media.countPrepare = trap_S_RegisterSound( "sound/osp/prepare.wav" ); + cgs.media.goatAxis = trap_S_RegisterSound( "sound/osp/goat.wav" ); + cgs.media.winAllies = trap_S_RegisterSound( "sound/osp/winallies.wav" ); + cgs.media.winAxis = trap_S_RegisterSound( "sound/osp/winaxis.wav" ); + // OSP +*/ + + cgs.media.flameSound = trap_S_RegisterSound( "sound/weapons/flamethrower/flame_burn.wav", qfalse ); + cgs.media.flameBlowSound = trap_S_RegisterSound( "sound/weapons/flamethrower/flame_pilot.wav", qfalse ); + cgs.media.flameStartSound = trap_S_RegisterSound( "sound/weapons/flamethrower/flame_up.wav", qfalse ); + cgs.media.flameStreamSound = trap_S_RegisterSound( "sound/weapons/flamethrower/flame_fire.wav", qfalse ); + cgs.media.flameCrackSound = 0; // -trap_S_RegisterSound( "sound/world/firecrack1.wav", qfalse ); + cgs.media.grenadePulseSound4 = trap_S_RegisterSound( "sound/weapons/grenade/gren_timer4.wav", qfalse ); + cgs.media.grenadePulseSound3 = trap_S_RegisterSound( "sound/weapons/grenade/gren_timer3.wav", qfalse ); + cgs.media.grenadePulseSound2 = trap_S_RegisterSound( "sound/weapons/grenade/gren_timer2.wav", qfalse ); + cgs.media.grenadePulseSound1 = trap_S_RegisterSound( "sound/weapons/grenade/gren_timer1.wav", qfalse ); + + + cgs.media.boneBounceSound = trap_S_RegisterSound( "sound/world/boardbreak.wav", qfalse ); // TODO: need a real sound for this + + cgs.media.sfx_rockexp = trap_S_RegisterSound( "sound/weapons/rocket/rocket_expl.wav", qfalse ); + cgs.media.sfx_rockexpDist = trap_S_RegisterSound( "sound/weapons/rocket/rocket_expl_far.wav", qfalse ); + + cgs.media.sfx_artilleryExp[0] = trap_S_RegisterSound( "sound/weapons/artillery/artillery_expl_1.wav", qfalse ); + cgs.media.sfx_artilleryExp[1] = trap_S_RegisterSound( "sound/weapons/artillery/artillery_expl_2.wav", qfalse ); + cgs.media.sfx_artilleryExp[2] = trap_S_RegisterSound( "sound/weapons/artillery/artillery_expl_3.wav", qfalse ); + cgs.media.sfx_artilleryDist = trap_S_RegisterSound( "sound/weapons/artillery/artillery_expl_far.wav", qfalse ); + + cgs.media.sfx_airstrikeExp[0] = trap_S_RegisterSound( "sound/weapons/airstrike/airstrike_expl_1.wav", qfalse ); + cgs.media.sfx_airstrikeExp[1] = trap_S_RegisterSound( "sound/weapons/airstrike/airstrike_expl_2.wav", qfalse ); + cgs.media.sfx_airstrikeExp[2] = trap_S_RegisterSound( "sound/weapons/airstrike/airstrike_expl_3.wav", qfalse ); + cgs.media.sfx_airstrikeDist = trap_S_RegisterSound( "sound/weapons/airstrike/airstrike_expl_far.wav", qfalse ); + + cgs.media.sfx_dynamiteexp = trap_S_RegisterSound( "sound/weapons/dynamite/dynamite_expl.wav", qfalse ); + cgs.media.sfx_dynamiteexpDist = trap_S_RegisterSound( "sound/weapons/dynamite/dynamite_expl_far.wav", qfalse ); + + cgs.media.sfx_satchelexp = trap_S_RegisterSound( "sound/weapons/satchel/satchel_expl.wav", qfalse ); + cgs.media.sfx_satchelexpDist = trap_S_RegisterSound( "sound/weapons/satchel/satchel_expl_far.wav", qfalse ); + cgs.media.sfx_landmineexp = trap_S_RegisterSound( "sound/weapons/landmine/mine_expl.wav", qfalse ); + cgs.media.sfx_landmineexpDist = trap_S_RegisterSound( "sound/weapons/landmine/mine_expl_far.wav", qfalse ); + cgs.media.sfx_mortarexp[0] = trap_S_RegisterSound( "sound/weapons/mortar/mortar_expl1.wav", qfalse ); + cgs.media.sfx_mortarexp[1] = trap_S_RegisterSound( "sound/weapons/mortar/mortar_expl2.wav", qfalse ); + cgs.media.sfx_mortarexp[2] = trap_S_RegisterSound( "sound/weapons/mortar/mortar_expl3.wav", qfalse ); + cgs.media.sfx_mortarexp[3] = trap_S_RegisterSound( "sound/weapons/mortar/mortar_expl.wav", qfalse ); + cgs.media.sfx_mortarexpDist = trap_S_RegisterSound( "sound/weapons/mortar/mortar_expl_far.wav", qfalse ); + cgs.media.sfx_grenexp = trap_S_RegisterSound( "sound/weapons/grenade/gren_expl.wav", qfalse ); + cgs.media.sfx_grenexpDist = trap_S_RegisterSound( "sound/weapons/grenade/gren_expl_far.wav", qfalse ); + cgs.media.sfx_rockexpWater = trap_S_RegisterSound( "sound/weapons/grenade/gren_expl_water.wav", qfalse ); + + + for ( i = 0; i < 3; i++ ) { + // Gordon: bouncy shell sounds \o/ + cgs.media.sfx_brassSound[BRASSSOUND_METAL][i] = trap_S_RegisterSound( va( "sound/weapons/misc/shell_metal%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_brassSound[BRASSSOUND_SOFT][i] = trap_S_RegisterSound( va( "sound/weapons/misc/shell_soft%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_brassSound[BRASSSOUND_STONE][i] = trap_S_RegisterSound( va( "sound/weapons/misc/shell_stone%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_brassSound[BRASSSOUND_WOOD][i] = trap_S_RegisterSound( va( "sound/weapons/misc/shell_wood%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_rubbleBounce[i] = trap_S_RegisterSound( va( "sound/world/debris%i.wav", i + 1 ), qfalse ); + } + cgs.media.sfx_knifehit[0] = trap_S_RegisterSound( "sound/weapons/knife/knife_hit1.wav", qfalse ); + cgs.media.sfx_knifehit[1] = trap_S_RegisterSound( "sound/weapons/knife/knife_hit2.wav", qfalse ); + cgs.media.sfx_knifehit[2] = trap_S_RegisterSound( "sound/weapons/knife/knife_hit3.wav", qfalse ); + cgs.media.sfx_knifehit[3] = trap_S_RegisterSound( "sound/weapons/knife/knife_hit4.wav", qfalse ); + cgs.media.sfx_knifehit[4] = trap_S_RegisterSound( "sound/weapons/knife/knife_hitwall1.wav", qfalse ); + + for ( i = 0; i < 5; i++ ) { + cgs.media.sfx_bullet_fleshhit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/flesh%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_bullet_metalhit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/metal%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_bullet_woodhit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/wood%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_bullet_glasshit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/glass%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_bullet_stonehit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/stone%i.wav", i + 1 ), qfalse ); + cgs.media.sfx_bullet_waterhit[i] = trap_S_RegisterSound( va( "sound/weapons/impact/water%i.wav", i + 1 ), qfalse ); + } + + cgs.media.uniformPickup = trap_S_RegisterSound( "sound/misc/body_pickup.wav", qfalse ); + cgs.media.buildDecayedSound = trap_S_RegisterSound( "sound/world/build_abort.wav", qfalse ); + + cgs.media.sndLimboSelect = trap_S_RegisterSound( "sound/menu/select.wav", qfalse ); + cgs.media.sndLimboFocus = trap_S_RegisterSound( "sound/menu/focus.wav", qfalse ); + cgs.media.sndLimboFilter = trap_S_RegisterSound( "sound/menu/filter.wav", qfalse ); + cgs.media.sndLimboCancel = trap_S_RegisterSound( "sound/menu/cancel.wav", qfalse ); + + cgs.media.sndRankUp = trap_S_RegisterSound( "sound/misc/rank_up.wav", qfalse ); + cgs.media.sndSkillUp = trap_S_RegisterSound( "sound/misc/skill_up.wav", qfalse ); + + cgs.media.sndMedicCall[0] = trap_S_RegisterSound( "sound/chat/axis/medic.wav", qfalse ); + cgs.media.sndMedicCall[1] = trap_S_RegisterSound( "sound/chat/allies/medic.wav", qfalse ); + + + // FIXME: send as a special event + trap_S_RegisterSound( "sound/weapons/artillery/artillery_fly_1.wav", qfalse ); + trap_S_RegisterSound( "sound/weapons/artillery/artillery_fly_2.wav", qfalse ); + trap_S_RegisterSound( "sound/weapons/artillery/artillery_fly_3.wav", qfalse ); + trap_S_RegisterSound( "sound/weapons/airstrike/airstrike_plane.wav", qfalse ); + + + if ( cg_buildScript.integer ) { + CG_PrecacheFXSounds(); + } +} + + +//=================================================================================== + +/* +================= +CG_RegisterGraphics + +This function may execute for a couple of minutes with a slow disk. +================= +*/ + +qboolean CG_RegisterClientSkin( bg_playerclass_t* classInfo ); +qboolean CG_RegisterClientModelname( bg_playerclass_t* classInfo ); +void WM_RegisterWeaponTypeShaders(); + +static void CG_RegisterGraphics( void ) { + char name[1024]; + int i; + static char *sb_nums[11] = { + "gfx/2d/numbers/zero_32b", + "gfx/2d/numbers/one_32b", + "gfx/2d/numbers/two_32b", + "gfx/2d/numbers/three_32b", + "gfx/2d/numbers/four_32b", + "gfx/2d/numbers/five_32b", + "gfx/2d/numbers/six_32b", + "gfx/2d/numbers/seven_32b", + "gfx/2d/numbers/eight_32b", + "gfx/2d/numbers/nine_32b", + "gfx/2d/numbers/minus_32b", + }; + + CG_LoadingString( cgs.mapname ); + + trap_R_LoadWorldMap( cgs.mapname ); + + CG_LoadingString( "entities" ); + + numSplinePaths = 0; + numPathCorners = 0; + + cg.numOIDtriggers2 = 0; + + BG_ClearAnimationPool(); + + BG_ClearCharacterPool(); + + BG_InitWeaponStrings(); + + CG_ParseEntitiesFromString(); + + CG_LoadObjectiveData(); + + // precache status bar pics + CG_LoadingString( "game media" ); + + CG_LoadingString( " - textures" ); + +//bani - dynamic shader api example +//replaces a fueldump texture with a dynamically generated one. +#ifdef TEST_API_DYNAMICSHADER + trap_R_LoadDynamicShader( "my_terrain1_2", + "my_terrain1_2\n\ +{\n\ + qer_editorimage textures/stone/mxsnow3.tga\n\ + q3map_baseshader textures/fueldump/terrain_base\n\ + {\n\ + map textures/stone/mxrock1aa.tga\n\ + rgbGen identity\n\ + tcgen environment\n\ + }\n\ + {\n\ + lightmap $lightmap\n\ + blendFunc GL_DST_COLOR GL_ZERO\n\ + rgbgen identity\n\ + }\n\ +}\n\ +" ); + + trap_R_RegisterShader( "my_terrain1_2" ); + trap_R_RemapShader( "textures/fueldump/terrain1_2", "my_terrain1_2", "0" ); +#endif + + for ( i = 0 ; i < 11 ; i++ ) { + cgs.media.numberShaders[i] = trap_R_RegisterShader( sb_nums[i] ); + } + + cgs.media.fleshSmokePuffShader = trap_R_RegisterShader( "fleshimpactsmokepuff" ); // JPW NERVE + cgs.media.nerveTestShader = trap_R_RegisterShader( "jpwtest1" ); + cgs.media.idTestShader = trap_R_RegisterShader( "jpwtest2" ); + cgs.media.hud1Shader = trap_R_RegisterShader( "jpwhud1" ); + cgs.media.hud2Shader = trap_R_RegisterShader( "jpwhud2" ); + cgs.media.hud3Shader = trap_R_RegisterShader( "jpwhud3" ); + cgs.media.hud4Shader = trap_R_RegisterShader( "jpwhud4" ); + cgs.media.hud5Shader = trap_R_RegisterShader( "jpwhud5" ); + cgs.media.smokePuffShader = trap_R_RegisterShader( "smokePuff" ); + + // RF, blood cloud + cgs.media.bloodCloudShader = trap_R_RegisterShader( "bloodCloud" ); + + // OSP - MV cursor +// cgs.media.cursor = trap_R_RegisterShaderNoMip( "ui/assets/mvcursor.tga" ); + + // Rafael - cannon + cgs.media.smokePuffShaderdirty = trap_R_RegisterShader( "smokePuffdirty" ); + cgs.media.smokePuffShaderb1 = trap_R_RegisterShader( "smokePuffblack1" ); + cgs.media.smokePuffShaderb2 = trap_R_RegisterShader( "smokePuffblack2" ); + cgs.media.smokePuffShaderb3 = trap_R_RegisterShader( "smokePuffblack3" ); + cgs.media.smokePuffShaderb4 = trap_R_RegisterShader( "smokePuffblack4" ); + cgs.media.smokePuffShaderb5 = trap_R_RegisterShader( "smokePuffblack5" ); + // done + + // Rafael - bleedanim + for ( i = 0; i < 5; i++ ) { + cgs.media.viewBloodAni[i] = trap_R_RegisterShader( va( "viewBloodBlend%i", i + 1 ) ); + } + + cgs.media.viewFlashBlood = trap_R_RegisterShader( "viewFlashBlood" ); + for ( i = 0; i < 16; i++ ) { + cgs.media.viewFlashFire[i] = trap_R_RegisterShader( va( "viewFlashFire%i", i + 1 ) ); + } + + cgs.media.smokePuffRageProShader = trap_R_RegisterShader( "smokePuffRagePro" ); + cgs.media.shotgunSmokePuffShader = trap_R_RegisterShader( "shotgunSmokePuff" ); + cgs.media.bloodTrailShader = trap_R_RegisterShader( "bloodTrail" ); + cgs.media.lagometerShader = trap_R_RegisterShader( "lagometer" ); + cgs.media.reticleShaderSimple = trap_R_RegisterShader( "gfx/misc/reticlesimple" ); + cgs.media.binocShaderSimple = trap_R_RegisterShader( "gfx/misc/binocsimple" ); + cgs.media.snowShader = trap_R_RegisterShader( "snow_tri" ); + cgs.media.oilParticle = trap_R_RegisterShader( "oilParticle" ); + cgs.media.oilSlick = trap_R_RegisterShader( "oilSlick" ); + cgs.media.waterBubbleShader = trap_R_RegisterShader( "waterBubble" ); + cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" ); + cgs.media.usableHintShader = trap_R_RegisterShader( "gfx/2d/usableHint" ); + cgs.media.notUsableHintShader = trap_R_RegisterShader( "gfx/2d/notUsableHint" ); + cgs.media.doorHintShader = trap_R_RegisterShader( "gfx/2d/doorHint" ); + cgs.media.doorRotateHintShader = trap_R_RegisterShader( "gfx/2d/doorRotateHint" ); + + // Arnout: these were never used in default wolf + cgs.media.doorLockHintShader = trap_R_RegisterShader( "gfx/2d/lockedhint" ); + cgs.media.doorRotateLockHintShader = trap_R_RegisterShader( "gfx/2d/lockedhint" ); + cgs.media.mg42HintShader = trap_R_RegisterShader( "gfx/2d/mg42Hint" ); + cgs.media.breakableHintShader = trap_R_RegisterShader( "gfx/2d/breakableHint" ); + cgs.media.chairHintShader = trap_R_RegisterShader( "gfx/2d/chairHint" ); + cgs.media.alarmHintShader = trap_R_RegisterShader( "gfx/2d/alarmHint" ); + cgs.media.healthHintShader = trap_R_RegisterShader( "gfx/2d/healthHint" ); + cgs.media.treasureHintShader = trap_R_RegisterShader( "gfx/2d/treasureHint" ); + cgs.media.knifeHintShader = trap_R_RegisterShader( "gfx/2d/knifeHint" ); + cgs.media.ladderHintShader = trap_R_RegisterShader( "gfx/2d/ladderHint" ); + cgs.media.buttonHintShader = trap_R_RegisterShader( "gfx/2d/buttonHint" ); + cgs.media.waterHintShader = trap_R_RegisterShader( "gfx/2d/waterHint" ); + cgs.media.cautionHintShader = trap_R_RegisterShader( "gfx/2d/cautionHint" ); + cgs.media.dangerHintShader = trap_R_RegisterShader( "gfx/2d/dangerHint" ); + cgs.media.secretHintShader = trap_R_RegisterShader( "gfx/2d/secretHint" ); + cgs.media.qeustionHintShader = trap_R_RegisterShader( "gfx/2d/questionHint" ); + cgs.media.exclamationHintShader = trap_R_RegisterShader( "gfx/2d/exclamationHint" ); + cgs.media.clipboardHintShader = trap_R_RegisterShader( "gfx/2d/clipboardHint" ); + cgs.media.weaponHintShader = trap_R_RegisterShader( "gfx/2d/weaponHint" ); + cgs.media.ammoHintShader = trap_R_RegisterShader( "gfx/2d/ammoHint" ); + cgs.media.armorHintShader = trap_R_RegisterShader( "gfx/2d/armorHint" ); + cgs.media.powerupHintShader = trap_R_RegisterShader( "gfx/2d/powerupHint" ); + cgs.media.holdableHintShader = trap_R_RegisterShader( "gfx/2d/holdableHint" ); + cgs.media.inventoryHintShader = trap_R_RegisterShader( "gfx/2d/inventoryHint" ); + + cgs.media.friendShader = trap_R_RegisterShaderNoMip( "gfx/2d/friendlycross.tga" ); + + // (SA) not used yet +// cgs.media.hintPlrFriendShader = trap_R_RegisterShader( "gfx/2d/hintPlrFriend" ); +// cgs.media.hintPlrNeutralShader = trap_R_RegisterShader( "gfx/2d/hintPlrNeutral" ); +// cgs.media.hintPlrEnemyShader = trap_R_RegisterShader( "gfx/2d/hintPlrEnemy" ); +// cgs.media.hintPlrUnknownShader = trap_R_RegisterShader( "gfx/2d/hintPlrUnknown" ); + + cgs.media.buildHintShader = trap_R_RegisterShader( "gfx/2d/buildHint" ); // DHM - Nerve + cgs.media.disarmHintShader = trap_R_RegisterShader( "gfx/2d/disarmHint" ); // DHM - Nerve + cgs.media.reviveHintShader = trap_R_RegisterShader( "gfx/2d/reviveHint" ); // DHM - Nerve + cgs.media.dynamiteHintShader = trap_R_RegisterShader( "gfx/2d/dynamiteHint" ); // DHM - Nerve + + cgs.media.tankHintShader = trap_R_RegisterShaderNoMip( "gfx/2d/tankHint" ); + cgs.media.satchelchargeHintShader = trap_R_RegisterShaderNoMip( "gfx/2d/satchelchargeHint" ), + cgs.media.landmineHintShader = trap_R_RegisterShaderNoMip( "gfx/2d/landmineHint" ); + cgs.media.uniformHintShader = trap_R_RegisterShaderNoMip( "gfx/2d/uniformHint" ); + cgs.media.waypointAttackShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_attack" ); + cgs.media.waypointDefendShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_defend" ); + cgs.media.waypointRegroupShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_regroup" ); + // TAT - load up the bot shader as well +// cgs.media.waypointBotShader = trap_R_RegisterShaderNoMip( "sprites/botorder" ); +// cgs.media.waypointBotQueuedShader=trap_R_RegisterShaderNoMip( "sprites/botqueuedorder" ); +// cgs.media.waypointCompassAttackShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_attack_compass" ); +// cgs.media.waypointCompassDefendShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_defend_compass" ); +// cgs.media.waypointCompassRegroupShader = trap_R_RegisterShaderNoMip( "sprites/waypoint_regroup_compass" ); +// cgs.media.commandCentreWoodShader = trap_R_RegisterShaderNoMip( "ui/assets2/commandMap" ); + if ( cgs.ccLayers ) { + for ( i = 0; i < cgs.ccLayers; i++ ) { + cgs.media.commandCentreMapShader[i] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_%i_cc.tga", cgs.rawmapname, i ) ); + cgs.media.commandCentreMapShaderTrans[i] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_%i_cc_trans", cgs.rawmapname, i ) ); + cgs.media.commandCentreAutomapShader[i] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_%i_cc_automap", cgs.rawmapname, i ) ); + } + } else { + cgs.media.commandCentreMapShader[0] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_cc.tga", cgs.rawmapname ) ); + cgs.media.commandCentreMapShaderTrans[0] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_cc_trans", cgs.rawmapname ) ); + cgs.media.commandCentreAutomapShader[0] = trap_R_RegisterShaderNoMip( va( "levelshots/%s_cc_automap", cgs.rawmapname ) ); + } + cgs.media.commandCentreAutomapMaskShader = trap_R_RegisterShaderNoMip( "levelshots/automap_mask" ); + cgs.media.commandCentreAutomapBorderShader = trap_R_RegisterShaderNoMip( "ui/assets2/maptrim_long" ); + cgs.media.commandCentreAutomapBorder2Shader = trap_R_RegisterShaderNoMip( "ui/assets2/maptrim_long2" ); + cgs.media.commandCentreAutomapCornerShader = trap_R_RegisterShaderNoMip( "ui/assets2/maptrim_edge.tga" ); + cgs.media.commandCentreAxisMineShader = trap_R_RegisterShaderNoMip( "sprites/landmine_axis" ); + cgs.media.commandCentreAlliedMineShader = trap_R_RegisterShaderNoMip( "sprites/landmine_allied" ); + cgs.media.commandCentreSpawnShader[0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_flagaxis" ); + cgs.media.commandCentreSpawnShader[1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_flagallied" ); + cgs.media.compassConstructShader = trap_R_RegisterShaderNoMip( "sprites/construct.tga" ); + + // Mad Doc - TDF + //cgs.media.ingameAutomapBackground = trap_R_RegisterShaderNoMip("ui/assets2/ingame/mapbackground"); + + //cgs.media.hudBorderVert = trap_R_RegisterShaderNoMip( "ui/assets2/border_vert.tga" ); + //cgs.media.hudBorderVert2 = trap_R_RegisterShaderNoMip( "ui/assets2/border_vert2.tga" ); + + cgs.media.compassDestroyShader = trap_R_RegisterShaderNoMip( "sprites/destroy.tga" ); + cgs.media.slashShader = trap_R_RegisterShaderNoMip( "gfx/2d/numbers/slash" ); + cgs.media.compass2Shader = trap_R_RegisterShaderNoMip( "gfx/2d/compass2.tga" ); + cgs.media.compassShader = trap_R_RegisterShaderNoMip( "gfx/2d/compass.tga" ); + cgs.media.buddyShader = trap_R_RegisterShaderNoMip( "sprites/buddy.tga" ); + + for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) { + cgs.media.crosshairShader[i] = trap_R_RegisterShader( va( "gfx/2d/crosshair%c", 'a' + i ) ); + cg.crosshairShaderAlt[i] = trap_R_RegisterShader( va( "gfx/2d/crosshair%c_alt", 'a' + i ) ); + } + + for ( i = 0 ; i < SK_NUM_SKILLS ; i++ ) { + cgs.media.medals[i] = trap_R_RegisterShaderNoMip( va( "gfx/limbo/medals0%i", i ) ); + } + + cgs.media.backTileShader = trap_R_RegisterShader( "gfx/2d/backtile" ); + cgs.media.noammoShader = trap_R_RegisterShader( "icons/noammo" ); + + cgs.media.teamStatusBar = trap_R_RegisterShader( "gfx/2d/colorbar.tga" ); + + //cgs.media.redColorBar = trap_R_RegisterShader("redcolorbar"); + //cgs.media.blueColorBar = trap_R_RegisterShader("bluecolorbar"); + cgs.media.hudSprintBar = trap_R_RegisterShader( "sprintbar" ); + + cgs.media.hudAlliedHelmet = trap_R_RegisterShader( "AlliedHelmet" ); + cgs.media.hudAxisHelmet = trap_R_RegisterShader( "AxisHelmet" ); + + CG_LoadingString( " - models" ); + + cgs.media.machinegunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" ); + cgs.media.panzerfaustBrassModel = trap_R_RegisterModel( "models/weapons2/shells/pf_shell.md3" ); + + // Rafael + cgs.media.smallgunBrassModel = trap_R_RegisterModel( "models/weapons2/shells/sm_shell.md3" ); + + //----(SA) wolf debris + cgs.media.debBlock[0] = trap_R_RegisterModel( "models/mapobjects/debris/brick1.md3" ); + cgs.media.debBlock[1] = trap_R_RegisterModel( "models/mapobjects/debris/brick2.md3" ); + cgs.media.debBlock[2] = trap_R_RegisterModel( "models/mapobjects/debris/brick3.md3" ); + cgs.media.debBlock[3] = trap_R_RegisterModel( "models/mapobjects/debris/brick4.md3" ); + cgs.media.debBlock[4] = trap_R_RegisterModel( "models/mapobjects/debris/brick5.md3" ); + cgs.media.debBlock[5] = trap_R_RegisterModel( "models/mapobjects/debris/brick6.md3" ); + + cgs.media.debRock[0] = trap_R_RegisterModel( "models/mapobjects/debris/rubble1.md3" ); + cgs.media.debRock[1] = trap_R_RegisterModel( "models/mapobjects/debris/rubble2.md3" ); + cgs.media.debRock[2] = trap_R_RegisterModel( "models/mapobjects/debris/rubble3.md3" ); + + + cgs.media.debWood[0] = trap_R_RegisterModel( "models/gibs/wood/wood1.md3" ); + cgs.media.debWood[1] = trap_R_RegisterModel( "models/gibs/wood/wood2.md3" ); + cgs.media.debWood[2] = trap_R_RegisterModel( "models/gibs/wood/wood3.md3" ); + cgs.media.debWood[3] = trap_R_RegisterModel( "models/gibs/wood/wood4.md3" ); + cgs.media.debWood[4] = trap_R_RegisterModel( "models/gibs/wood/wood5.md3" ); + cgs.media.debWood[5] = trap_R_RegisterModel( "models/gibs/wood/wood6.md3" ); + + cgs.media.debFabric[0] = trap_R_RegisterModel( "models/shards/fabric1.md3" ); + cgs.media.debFabric[1] = trap_R_RegisterModel( "models/shards/fabric2.md3" ); + cgs.media.debFabric[2] = trap_R_RegisterModel( "models/shards/fabric3.md3" ); + + //----(SA) end + + cgs.media.spawnInvincibleShader = trap_R_RegisterShader( "sprites/shield" ); + cgs.media.scoreEliminatedShader = trap_R_RegisterShader( "sprites/skull" ); + cgs.media.medicReviveShader = trap_R_RegisterShader( "sprites/medic_revive" ); + + //cgs.media.vehicleShader = trap_R_RegisterShader( "sprites/vehicle" ); + cgs.media.destroyShader = trap_R_RegisterShader( "sprites/destroy" ); + + cgs.media.voiceChatShader = trap_R_RegisterShader( "sprites/voiceChat" ); + cgs.media.balloonShader = trap_R_RegisterShader( "sprites/balloon3" ); + + cgs.media.objectiveShader = trap_R_RegisterShader( "sprites/objective" ); + + cgs.media.bloodExplosionShader = trap_R_RegisterShader( "bloodExplosion" ); + + //cgs.media.bleedExplosionShader = trap_R_RegisterShader( "bleedExplosion" ); + + //----(SA) water splash + cgs.media.waterSplashModel = trap_R_RegisterModel( "models/weaphits/bullet.md3" ); + cgs.media.waterSplashShader = trap_R_RegisterShader( "waterSplash" ); + //----(SA) end + + // Ridah, spark particles + cgs.media.sparkParticleShader = trap_R_RegisterShader( "sparkParticle" ); + cgs.media.smokeTrailShader = trap_R_RegisterShader( "smokeTrail" ); + //cgs.media.lightningBoltShader = trap_R_RegisterShader( "lightningBolt" ); + cgs.media.flamethrowerFireStream = trap_R_RegisterShader( "flamethrowerFireStream" ); + cgs.media.flamethrowerBlueStream = trap_R_RegisterShader( "flamethrowerBlueStream" ); + cgs.media.onFireShader2 = trap_R_RegisterShader( "entityOnFire1" ); + cgs.media.onFireShader = trap_R_RegisterShader( "entityOnFire2" ); + cgs.media.viewFadeBlack = trap_R_RegisterShader( "viewFadeBlack" ); + cgs.media.sparkFlareShader = trap_R_RegisterShader( "sparkFlareParticle" ); + cgs.media.spotLightShader = trap_R_RegisterShader( "spotLight" ); + cgs.media.spotLightBeamShader = trap_R_RegisterShader( "lightBeam" ); + cgs.media.bulletParticleTrailShader = trap_R_RegisterShader( "bulletParticleTrail" ); + cgs.media.smokeParticleShader = trap_R_RegisterShader( "smokeParticle" ); + + // DHM - Nerve :: bullet hitting dirt + cgs.media.dirtParticle1Shader = trap_R_RegisterShader( "dirt_splash" ); + cgs.media.dirtParticle2Shader = trap_R_RegisterShader( "water_splash" ); + //cgs.media.dirtParticle3Shader = trap_R_RegisterShader( "dirtParticle3" ); + + cgs.media.genericConstructionShader = trap_R_RegisterShader( "textures/sfx/construction" ); + //cgs.media.genericConstructionShaderBrush = trap_R_RegisterShader( "textures/sfx/construction" ); + //cgs.media.genericConstructionShaderModel = trap_R_RegisterShader( "textures/sfx/construction_model" ); + cgs.media.alliedUniformShader = trap_R_RegisterShader( "sprites/uniform_allied" ); + cgs.media.axisUniformShader = trap_R_RegisterShader( "sprites/uniform_axis" ); + + // used in: + // command map + cgs.media.ccFilterPics[0] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_axis" ); + cgs.media.ccFilterPics[1] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_allied" ); + cgs.media.ccFilterPics[2] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_spawn" ); + + cgs.media.ccFilterPics[3] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_bo" ); + cgs.media.ccFilterPics[4] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_healthammo" ); + cgs.media.ccFilterPics[5] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_construction" ); + cgs.media.ccFilterPics[6] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_destruction" ); + cgs.media.ccFilterPics[7] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_objective" ); + //cgs.media.ccFilterPics[7] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_waypoint" ); + //cgs.media.ccFilterPics[8] = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_objective" ); + + cgs.media.ccFilterBackOn = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_back_on" ); + cgs.media.ccFilterBackOff = trap_R_RegisterShaderNoMip( "gfx/limbo/filter_back_off" ); +/* +#define CC_FILTER_AXIS (1 << 0) +#define CC_FILTER_ALLIES (1 << 1) +#define CC_FILTER_SPAWNS (1 << 2) +#define CC_FILTER_CMDPOST (1 << 3) // TODO +#define CC_FILTER_HACABINETS (1 << 4) // TODO +#define CC_FILTER_CONSTRUCTIONS (1 << 5) +#define CC_FILTER_DESTRUCTIONS (1 << 6) +#define CC_FILTER_WAYPOINTS (1 << 7) +#define CC_FILTER_OBJECTIVES (1 << 8) // TODO +*/ + + // used in: + // statsranksmedals + // command map + // limbo menu + cgs.media.ccStamps[0] = trap_R_RegisterShaderNoMip( "ui/assets2/stamp_complete" ); + cgs.media.ccStamps[1] = trap_R_RegisterShaderNoMip( "ui/assets2/stamp_failed" ); + + //cgs.media.ccArrow = trap_R_RegisterShaderNoMip( "ui/assets2/arrow_up" ); + cgs.media.ccPlayerHighlight = trap_R_RegisterShaderNoMip( "ui/assets/mp_player_highlight.tga" ); + cgs.media.ccConstructIcon[0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_constaxis" ); + cgs.media.ccConstructIcon[1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_constallied" ); + cgs.media.ccDestructIcon[0][0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_axisgren" ); + cgs.media.ccDestructIcon[0][1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_alliedgren" ); + cgs.media.ccDestructIcon[1][0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_satchel" ); + cgs.media.ccDestructIcon[1][1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_satchel" ); + cgs.media.ccDestructIcon[2][0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_dynamite" ); + cgs.media.ccDestructIcon[2][1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_dynamite" ); + cgs.media.ccTankIcon = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_churchill" ); + + cgs.media.ccCmdPost[0] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_bo_axis" ); + cgs.media.ccCmdPost[1] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_bo_allied" ); + + cgs.media.ccMortarHit = trap_R_RegisterShaderNoMip( "gfx/limbo/mort_hit" ); + cgs.media.ccMortarTarget = trap_R_RegisterShaderNoMip( "gfx/limbo/mort_target" ); + cgs.media.ccMortarTargetArrow = trap_R_RegisterShaderNoMip( "gfx/limbo/mort_targetarrow" ); + + + cgs.media.skillPics[SK_BATTLE_SENSE] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_battlesense" ); + cgs.media.skillPics[SK_EXPLOSIVES_AND_CONSTRUCTION] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_engineer" ); + cgs.media.skillPics[SK_FIRST_AID] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_medic" ); + cgs.media.skillPics[SK_SIGNALS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_fieldops" ); + cgs.media.skillPics[SK_LIGHT_WEAPONS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_lightweap" ); + cgs.media.skillPics[SK_HEAVY_WEAPONS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_soldier" ); + cgs.media.skillPics[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_covertops" ); + + /*cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_MOVETOLOC] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_default" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_CONSTRUCT] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_construct" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_USEDYNAMITE] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_usedynamite" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_REPAIR] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_fixgun" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_MOUNTGUN] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_mountgun" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_OPENDOOR] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_opendoor" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_REVIVE] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_revive" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_GETDISGUISE] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_getdisguise" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_HEAL] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_heal" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_AMMO] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_ammo" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_DISARM] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_disarmdynamite" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_ATTACK] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_attack" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_COVER] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_cover" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_RECON] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_recon" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_SMOKEBOMB] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_smoke" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_FINDMINES] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_findmines" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_PLANTMINE] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_plantmine" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_ARTILLERY] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_artillery" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_AIRSTRIKE] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_airstrike" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_GRENADELAUNCH]= trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_grenadelaunch" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_PICKUPITEM] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_pickup" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_PANZERFAUST] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_panzerfaust" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_FLAMETHROW] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_flamethrow" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_MG42] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_mg42" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_MOUNTEDATTACK]= trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_mountedattack" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_KNIFEATTACK] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_knifeattack" ); + cgs.media.SPTeamOverlayBotOrders[BOT_ACTION_LOCKPICK] = trap_R_RegisterShaderNoMip( "ui/assets2/ingame/bot_action_lockpick" );*/ + + WM_RegisterWeaponTypeShaders(); + + CG_LoadRankIcons(); + + // Gordon: limbo menu setup + CG_LimboPanel_Init(); + + CG_ChatPanel_Setup(); + + CG_Fireteams_Setup(); + + cgs.media.waypointMarker = trap_R_RegisterModel( "models/multiplayer/flagpole/flag_waypoint.md3" ); + + cgs.media.railCoreShader = trap_R_RegisterShaderNoMip( "railCore" ); // (SA) for debugging server traces + cgs.media.ropeShader = trap_R_RegisterShader( "textures/props/cable_m01" ); + + cgs.media.thirdPersonBinocModel = trap_R_RegisterModel( "models/multiplayer/binocs/binocs.md3" ); // NERVE - SMF + cgs.media.flamebarrel = trap_R_RegisterModel( "models/furniture/barrel/barrel_a.md3" ); + cgs.media.mg42muzzleflash = trap_R_RegisterModel( "models/weapons2/machinegun/mg42_flash.md3" ); + + // Rafael shards + cgs.media.shardGlass1 = trap_R_RegisterModel( "models/shards/glass1.md3" ); + cgs.media.shardGlass2 = trap_R_RegisterModel( "models/shards/glass2.md3" ); + cgs.media.shardWood1 = trap_R_RegisterModel( "models/shards/wood1.md3" ); + cgs.media.shardWood2 = trap_R_RegisterModel( "models/shards/wood2.md3" ); + cgs.media.shardMetal1 = trap_R_RegisterModel( "models/shards/metal1.md3" ); + cgs.media.shardMetal2 = trap_R_RegisterModel( "models/shards/metal2.md3" ); +// cgs.media.shardCeramic1 = trap_R_RegisterModel( "models/shards/ceramic1.md3" ); +// cgs.media.shardCeramic2 = trap_R_RegisterModel( "models/shards/ceramic2.md3" ); + // done + + cgs.media.shardRubble1 = trap_R_RegisterModel( "models/mapobjects/debris/brick000.md3" ); + cgs.media.shardRubble2 = trap_R_RegisterModel( "models/mapobjects/debris/brick001.md3" ); + cgs.media.shardRubble3 = trap_R_RegisterModel( "models/mapobjects/debris/brick002.md3" ); + + for ( i = 0; i < MAX_LOCKER_DEBRIS; i++ ) + { + Com_sprintf( name, sizeof( name ), "models/mapobjects/debris/personal%i.md3", i + 1 ); + cgs.media.shardJunk[i] = trap_R_RegisterModel( name ); + } + + memset( cg_items, 0, sizeof( cg_items ) ); + memset( cg_weapons, 0, sizeof( cg_weapons ) ); + +// TODO: FIXME: REMOVE REGISTRATION OF EACH MODEL FOR EVERY LEVEL LOAD + + //----(SA) okay, new stuff to intialize rather than doing it at level load time (or "give all" time) + // (I'm certainly not against being efficient here, but I'm tired of the rocket launcher effect only registering + // sometimes and want it to work for sure for this demo) + + CG_LoadingString( " - weapons" ); + for ( i = WP_KNIFE; i < WP_NUM_WEAPONS; i++ ) { + // DHM - Nerve :: Only register weapons we use in WolfMP + if ( BG_WeaponInWolfMP( i ) ) { + CG_RegisterWeapon( i, qfalse ); + } + } + + CG_LoadingString( " - items" ); + for ( i = 1 ; i < bg_numItems ; i++ ) { + CG_RegisterItemVisuals( i ); + } + + cgs.media.grenadeExplosionShader = trap_R_RegisterShader( "grenadeExplosion" ); + cgs.media.rocketExplosionShader = trap_R_RegisterShader( "rocketExplosion" ); + + cgs.media.hWeaponSnd = trap_S_RegisterSound( "sound/weapons/mg42/mg42_fire.wav", qfalse ); + cgs.media.hWeaponEchoSnd = trap_S_RegisterSound( "sound/weapons/mg42/mg42_far.wav", qfalse ); + cgs.media.hWeaponHeatSnd = trap_S_RegisterSound( "sound/weapons/mg42/mg42_heat.wav", qfalse ); + + cgs.media.hWeaponSnd_2 = trap_S_RegisterSound( "sound/weapons/browning/browning_fire.wav", qfalse ); + cgs.media.hWeaponEchoSnd_2 = trap_S_RegisterSound( "sound/weapons/browning/browning_far.wav", qfalse ); + cgs.media.hWeaponHeatSnd_2 = trap_S_RegisterSound( "sound/weapons/browning/browning_heat.wav", qfalse ); + +// cgs.media.hflakWeaponSnd = trap_S_RegisterSound( "sound/vehicles/misc/20mm_fire.wav", qfalse ); + + cgs.media.minePrimedSound = trap_S_RegisterSound( "sound/weapons/landmine/mine_on.wav", qfalse ); + + // wall marks + cgs.media.bulletMarkShader = trap_R_RegisterShaderNoMip( "gfx/damage/bullet_mrk" ); + cgs.media.burnMarkShader = trap_R_RegisterShaderNoMip( "gfx/damage/burn_med_mrk" ); + cgs.media.shadowFootShader = trap_R_RegisterShaderNoMip( "markShadowFoot" ); + cgs.media.shadowTorsoShader = trap_R_RegisterShaderNoMip( "markShadowTorso" ); + cgs.media.wakeMarkShader = trap_R_RegisterShaderNoMip( "wake" ); + cgs.media.wakeMarkShaderAnim = trap_R_RegisterShaderNoMip( "wakeAnim" ); // (SA) + + //----(SA) added + cgs.media.bulletMarkShaderMetal = trap_R_RegisterShaderNoMip( "gfx/damage/metal_mrk" ); + cgs.media.bulletMarkShaderWood = trap_R_RegisterShaderNoMip( "gfx/damage/wood_mrk" ); + cgs.media.bulletMarkShaderGlass = trap_R_RegisterShaderNoMip( "gfx/damage/glass_mrk" ); + + for ( i = 0 ; i < 5 ; i++ ) { + char name[32]; + //Com_sprintf( name, sizeof(name), "textures/decals/blood%i", i+1 ); + //cgs.media.bloodMarkShaders[i] = trap_R_RegisterShader( name ); + Com_sprintf( name, sizeof( name ), "blood_dot%i", i + 1 ); + cgs.media.bloodDotShaders[i] = trap_R_RegisterShader( name ); + } + + CG_LoadingString( " - inline models" ); + + // register the inline models + cgs.numInlineModels = trap_CM_NumInlineModels(); + // TAT 12/23/2002 - as a safety check, let's not let the number of models exceed MAX_MODELS + if ( cgs.numInlineModels > MAX_MODELS ) { + CG_Error( "CG_RegisterGraphics: Too many inline models: %i\n", cgs.numInlineModels ); + //CG_Printf( S_COLOR_RED "WARNING: CG_RegisterGraphics: Too many inline models: %i\n", cgs.numInlineModels ); + //cgs.numInlineModels = MAX_MODELS; + } + + for ( i = 1 ; i < cgs.numInlineModels ; i++ ) { + char name[10]; + vec3_t mins, maxs; + int j; + + Com_sprintf( name, sizeof( name ), "*%i", i ); + cgs.inlineDrawModel[i] = trap_R_RegisterModel( name ); + trap_R_ModelBounds( cgs.inlineDrawModel[i], mins, maxs ); + for ( j = 0 ; j < 3 ; j++ ) { + cgs.inlineModelMidpoints[i][j] = mins[j] + 0.5 * ( maxs[j] - mins[j] ); + } + } + + CG_LoadingString( " - server models" ); + + // register all the server specified models + for ( i = 1 ; i < MAX_MODELS ; i++ ) { + const char *modelName; + + modelName = CG_ConfigString( CS_MODELS + i ); + if ( !modelName[0] ) { + break; + } + cgs.gameModels[i] = trap_R_RegisterModel( modelName ); + } + + for ( i = 1 ; i < MAX_MODELS ; i++ ) { + const char *skinName; + + skinName = CG_ConfigString( CS_SKINS + i ); + if ( !skinName[0] ) { + break; + } + cgs.gameModelSkins[i] = trap_R_RegisterSkin( skinName ); + } + + for ( i = 1 ; i < MAX_CS_SHADERS ; i++ ) { + const char *shaderName; + + shaderName = CG_ConfigString( CS_SHADERS + i ); + if ( !shaderName[0] ) { + break; + } + cgs.gameShaders[i] = shaderName[0] == '*' ? trap_R_RegisterShader( shaderName + 1 ) : trap_R_RegisterShaderNoMip( shaderName ); + Q_strncpyz( cgs.gameShaderNames[i], shaderName[0] == '*' ? shaderName + 1 : shaderName, MAX_QPATH ); + } + + for ( i = 1 ; i < MAX_CHARACTERS ; i++ ) { + const char *characterName; + + characterName = CG_ConfigString( CS_CHARACTERS + i ); + if ( !characterName[0] ) { + break; + } + + if ( !BG_FindCharacter( characterName ) ) { + cgs.gameCharacters[ i ] = BG_FindFreeCharacter( characterName ); + + Q_strncpyz( cgs.gameCharacters[ i ]->characterFile, characterName, sizeof( cgs.gameCharacters[ i ]->characterFile ) ); + + if ( !CG_RegisterCharacter( characterName, cgs.gameCharacters[ i ] ) ) { + CG_Error( "ERROR: CG_RegisterGraphics: failed to load character file '%s'\n", characterName ); + } + } + } + + CG_LoadingString( " - particles" ); + CG_ClearParticles(); + + InitSmokeSprites(); + + CG_LoadingString( " - classes" ); + + CG_RegisterPlayerClasses(); + + CG_InitPMGraphics(); + + // mounted gun on tank models + cgs.media.hMountedMG42Base = trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42nestbase.md3" ); + cgs.media.hMountedMG42Nest = trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42nest.md3" ); + cgs.media.hMountedMG42 = trap_R_RegisterModel( "models/mapobjects/tanks_sd/mg42.md3" ); + cgs.media.hMountedBrowning = trap_R_RegisterModel( "models/multiplayer/browning/thirdperson.md3" ); + + // FIXME: temp models + cgs.media.hMountedFPMG42 = trap_R_RegisterModel( "models/multiplayer/mg42/v_mg42.md3" ); + cgs.media.hMountedFPBrowning = trap_R_RegisterModel( "models/multiplayer/browning/tankmounted.md3" ); + + // medic icon for commandmap + cgs.media.medicIcon = trap_R_RegisterShaderNoMip( "sprites/voiceMedic" ); + + trap_R_RegisterFont( "ariblk", 27, &cgs.media.limboFont1 ); + trap_R_RegisterFont( "ariblk", 16, &cgs.media.limboFont1_lo ); + trap_R_RegisterFont( "courbd", 30, &cgs.media.limboFont2 ); + + cgs.media.medal_back = trap_R_RegisterShaderNoMip( "gfx/limbo/medal_back" ); + + cgs.media.limboNumber_roll = trap_R_RegisterShaderNoMip( "gfx/limbo/number_roll" ); + cgs.media.limboNumber_back = trap_R_RegisterShaderNoMip( "gfx/limbo/number_back" ); + cgs.media.limboStar_roll = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_roll" ); + cgs.media.limboStar_back = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_back" ); + cgs.media.limboLight_on = trap_R_RegisterShaderNoMip( "gfx/limbo/redlight_on" ); + cgs.media.limboLight_on2 = trap_R_RegisterShaderNoMip( "gfx/limbo/redlight_on02" ); + cgs.media.limboLight_off = trap_R_RegisterShaderNoMip( "gfx/limbo/redlight_off" ); + + cgs.media.limboWeaponNumber_off = trap_R_RegisterShaderNoMip( "gfx/limbo/but_weap_off" ); + cgs.media.limboWeaponNumber_on = trap_R_RegisterShaderNoMip( "gfx/limbo/but_weap_on" ); + cgs.media.limboWeaponCard = trap_R_RegisterShaderNoMip( "gfx/limbo/weap_card" ); + + cgs.media.limboWeaponCardSurroundH = trap_R_RegisterShaderNoMip( "gfx/limbo/butsur_hor" ); + cgs.media.limboWeaponCardSurroundV = trap_R_RegisterShaderNoMip( "gfx/limbo/butsur_vert" ); + cgs.media.limboWeaponCardSurroundC = trap_R_RegisterShaderNoMip( "gfx/limbo/butsur_corn" ); + + cgs.media.limboWeaponCardOOS = trap_R_RegisterShaderNoMip( "gfx/limbo/outofstock" ); + + cgs.media.limboClassButtons[PC_ENGINEER] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_engineer" ); + cgs.media.limboClassButtons[PC_SOLDIER] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_soldier" ); + cgs.media.limboClassButtons[PC_COVERTOPS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_covertops" ); + cgs.media.limboClassButtons[PC_FIELDOPS] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_fieldops" ); + cgs.media.limboClassButtons[PC_MEDIC] = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_medic" ); + cgs.media.limboSkillsBS = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_battlesense" ); + cgs.media.limboSkillsLW = trap_R_RegisterShaderNoMip( "gfx/limbo/ic_lightweap" ); + //cgs.media.limboClassButtonBack = trap_R_RegisterShaderNoMip( "gfx/limbo/but_class" ); + + cgs.media.limboClassButton2Back_on = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_back_on" ); + cgs.media.limboClassButton2Back_off = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_back_off" ); + cgs.media.limboClassButton2Wedge_off = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_4pieces_off" ); + cgs.media.limboClassButton2Wedge_on = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_4pieces_on" ); + cgs.media.limboClassButtons2[PC_ENGINEER] = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_engineer" ); + cgs.media.limboClassButtons2[PC_SOLDIER] = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_soldier" ); + cgs.media.limboClassButtons2[PC_COVERTOPS] = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_covops" ); + cgs.media.limboClassButtons2[PC_FIELDOPS] = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_fieldops" ); + cgs.media.limboClassButtons2[PC_MEDIC] = trap_R_RegisterShaderNoMip( "gfx/limbo/skill_medic" ); + + + cgs.media.limboTeamButtonBack_on = trap_R_RegisterShaderNoMip( "gfx/limbo/but_team_on" ); + cgs.media.limboTeamButtonBack_off = trap_R_RegisterShaderNoMip( "gfx/limbo/but_team_off" ); + cgs.media.limboTeamButtonAllies = trap_R_RegisterShaderNoMip( "gfx/limbo/but_team_allied" ); + cgs.media.limboTeamButtonAxis = trap_R_RegisterShaderNoMip( "gfx/limbo/but_team_axis" ); + cgs.media.limboTeamButtonSpec = trap_R_RegisterShaderNoMip( "gfx/limbo/but_team_spec" ); + + + cgs.media.limboBlendThingy = trap_R_RegisterShaderNoMip( "gfx/limbo/cc_blend" ); + cgs.media.limboWeaponBlendThingy = trap_R_RegisterShaderNoMip( "gfx/limbo/weap_blend" ); + + //cgs.media.limboCursor_on = trap_R_RegisterShaderNoMip( "gfx/limbo/et_cursor_on" ); + //cgs.media.limboCursor_off = trap_R_RegisterShaderNoMip( "gfx/limbo/et_cursor_off" ); + + cgs.media.limboCounterBorder = trap_R_RegisterShaderNoMip( "gfx/limbo/number_border" ); + + cgs.media.hudPowerIcon = trap_R_RegisterShaderNoMip( "gfx/hud/ic_power" ); + cgs.media.hudSprintIcon = trap_R_RegisterShaderNoMip( "gfx/hud/ic_stamina" ); + cgs.media.hudHealthIcon = trap_R_RegisterShaderNoMip( "gfx/hud/ic_health" ); + + cgs.media.limboWeaponCard1 = trap_R_RegisterShaderNoMip( "gfx/limbo/weaponcard01" ); + cgs.media.limboWeaponCard2 = trap_R_RegisterShaderNoMip( "gfx/limbo/weaponcard02" ); + cgs.media.limboWeaponCardArrow = trap_R_RegisterShaderNoMip( "gfx/limbo/weap_dnarrow.tga" ); + + + cgs.media.limboObjectiveBack[0] = trap_R_RegisterShaderNoMip( "gfx/limbo/objective_back_axis" ); + cgs.media.limboObjectiveBack[1] = trap_R_RegisterShaderNoMip( "gfx/limbo/objective_back_allied" ); + cgs.media.limboObjectiveBack[2] = trap_R_RegisterShaderNoMip( "gfx/limbo/objective_back" ); + + cgs.media.limboClassBar = trap_R_RegisterShaderNoMip( "gfx/limbo/lightup_bar" ); + + cgs.media.limboBriefingButtonOn = trap_R_RegisterShaderNoMip( "gfx/limbo/but_play_on" ); + cgs.media.limboBriefingButtonOff = trap_R_RegisterShaderNoMip( "gfx/limbo/but_play_off" ); + cgs.media.limboBriefingButtonStopOn = trap_R_RegisterShaderNoMip( "gfx/limbo/but_stop_on" ); + cgs.media.limboBriefingButtonStopOff = trap_R_RegisterShaderNoMip( "gfx/limbo/but_stop_off" ); + + cgs.media.limboSpectator = trap_R_RegisterShaderNoMip( "gfx/limbo/spectator" ); + cgs.media.limboRadioBroadcast = trap_R_RegisterShaderNoMip( "ui/assets/radio_tower" ); + + cgs.media.cursorIcon = trap_R_RegisterShaderNoMip( "ui/assets/3_cursor3" ); + + cgs.media.hudDamagedStates[0] = trap_R_RegisterSkin( "models/players/hud/damagedskins/blood01.skin" ); + cgs.media.hudDamagedStates[1] = trap_R_RegisterSkin( "models/players/hud/damagedskins/blood02.skin" ); + cgs.media.hudDamagedStates[2] = trap_R_RegisterSkin( "models/players/hud/damagedskins/blood03.skin" ); + cgs.media.hudDamagedStates[3] = trap_R_RegisterSkin( "models/players/hud/damagedskins/blood04.skin" ); + + cgs.media.browningIcon = trap_R_RegisterShaderNoMip( "icons/iconw_browning_1_select" ); + + cgs.media.axisFlag = trap_R_RegisterShaderNoMip( "gfx/limbo/flag_axis" ); + cgs.media.alliedFlag = trap_R_RegisterShaderNoMip( "gfx/limbo/flag_allied" ); + cgs.media.disconnectIcon = trap_R_RegisterShaderNoMip( "gfx/2d/net" ); + + for ( i = 0; i < 6; i++ ) { + cgs.media.fireteamicons[i] = trap_R_RegisterShaderNoMip( va( "gfx/hud/fireteam/fireteam%i", i + 1 ) ); + } + + CG_LoadingString( " - game media done" ); +} + +/* +=================== +CG_RegisterClients + +=================== +*/ +static void CG_RegisterClients( void ) { + int i; + + for ( i = 0 ; i < MAX_CLIENTS ; i++ ) { + const char *clientInfo; + + clientInfo = CG_ConfigString( CS_PLAYERS + i ); + if ( !clientInfo[0] ) { + continue; + } +// CG_LoadingClient( i ); + CG_NewClientInfo( i ); + } +} + +//=========================================================================== + +/* +================= +CG_ConfigString +================= +*/ + +const char *CG_ConfigString( int index ) { + if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { + CG_Error( "CG_ConfigString: bad index: %i", index ); + } + return cgs.gameState.stringData + cgs.gameState.stringOffsets[ index ]; +} + +int CG_ConfigStringCopy( int index, char* buff, int buffsize ) { + Q_strncpyz( buff, CG_ConfigString( index ), buffsize ); + return strlen( buff ); +} + + +//================================================================== + +/* +====================== +CG_StartMusic + +====================== +*/ +void CG_StartMusic( void ) { + char *s; + char parm1[MAX_QPATH], parm2[MAX_QPATH]; + + // start the background music + s = (char *)CG_ConfigString( CS_MUSIC ); + Q_strncpyz( parm1, COM_Parse( &s ), sizeof( parm1 ) ); + Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) ); + + if ( strlen( parm1 ) ) { + trap_S_StartBackgroundTrack( parm1, parm2, 0 ); + } +} + +/* +============== +CG_QueueMusic +============== +*/ +void CG_QueueMusic( void ) { + char *s; + char parm[MAX_QPATH]; + + // prepare the next background track + s = (char *)CG_ConfigString( CS_MUSIC_QUEUE ); + Q_strncpyz( parm, COM_Parse( &s ), sizeof( parm ) ); + + // even if no strlen(parm). we want to be able to clear the queue + + // TODO: \/ the values stored in here will be made accessable so + // it doesn't have to go through startbackgroundtrack() (which is stupid) + trap_S_StartBackgroundTrack( parm, "", -2 ); // '-2' for 'queue looping track' (QUEUED_PLAY_LOOPED) +} + +#if 0 //DAJ unused +char *CG_GetMenuBuffer( const char *filename ) { + int len; + fileHandle_t f; + static char buf[MAX_MENUFILE]; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) ); + return NULL; + } + if ( len >= MAX_MENUFILE ) { + trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE ) ); + trap_FS_FCloseFile( f ); + return NULL; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + return buf; +} +#endif +// +// ============================== +// new hud stuff ( mission pack ) +// ============================== +// +qboolean CG_Asset_Parse( int handle ) { + pc_token_t token; + const char *tempStr; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + return qfalse; + } + if ( Q_stricmp( token.string, "{" ) != 0 ) { + return qfalse; + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + return qfalse; + } + + if ( Q_stricmp( token.string, "}" ) == 0 ) { + return qtrue; + } + + // font + if ( Q_stricmp( token.string, "font" ) == 0 ) { + int pointSize, fontIndex; + if ( !PC_Int_Parse( handle, &fontIndex ) || !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) ) { + return qfalse; + } + if ( fontIndex < 0 || fontIndex >= 6 ) { + return qfalse; + } + cgDC.registerFont( tempStr, pointSize, &cgDC.Assets.fonts[fontIndex] ); + continue; + } + + // gradientbar + if ( Q_stricmp( token.string, "gradientbar" ) == 0 ) { + if ( !PC_String_Parse( handle, &tempStr ) ) { + return qfalse; + } + cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( tempStr ); + continue; + } + + // enterMenuSound + if ( Q_stricmp( token.string, "menuEnterSound" ) == 0 ) { + if ( !PC_String_Parse( handle, &tempStr ) ) { + return qfalse; + } + cgDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qtrue ); + continue; + } + + // exitMenuSound + if ( Q_stricmp( token.string, "menuExitSound" ) == 0 ) { + if ( !PC_String_Parse( handle, &tempStr ) ) { + return qfalse; + } + cgDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qtrue ); + continue; + } + + // itemFocusSound + if ( Q_stricmp( token.string, "itemFocusSound" ) == 0 ) { + if ( !PC_String_Parse( handle, &tempStr ) ) { + return qfalse; + } + cgDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qtrue ); + continue; + } + + // menuBuzzSound + if ( Q_stricmp( token.string, "menuBuzzSound" ) == 0 ) { + if ( !PC_String_Parse( handle, &tempStr ) ) { + return qfalse; + } + cgDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qtrue ); + continue; + } + + if ( Q_stricmp( token.string, "cursor" ) == 0 ) { + if ( !PC_String_Parse( handle, &cgDC.Assets.cursorStr ) ) { + return qfalse; + } + cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr ); + continue; + } + + if ( Q_stricmp( token.string, "fadeClamp" ) == 0 ) { + if ( !PC_Float_Parse( handle, &cgDC.Assets.fadeClamp ) ) { + return qfalse; + } + continue; + } + + if ( Q_stricmp( token.string, "fadeCycle" ) == 0 ) { + if ( !PC_Int_Parse( handle, &cgDC.Assets.fadeCycle ) ) { + return qfalse; + } + continue; + } + + if ( Q_stricmp( token.string, "fadeAmount" ) == 0 ) { + if ( !PC_Float_Parse( handle, &cgDC.Assets.fadeAmount ) ) { + return qfalse; + } + continue; + } + + if ( Q_stricmp( token.string, "shadowX" ) == 0 ) { + if ( !PC_Float_Parse( handle, &cgDC.Assets.shadowX ) ) { + return qfalse; + } + continue; + } + + if ( Q_stricmp( token.string, "shadowY" ) == 0 ) { + if ( !PC_Float_Parse( handle, &cgDC.Assets.shadowY ) ) { + return qfalse; + } + continue; + } + + if ( Q_stricmp( token.string, "shadowColor" ) == 0 ) { + if ( !PC_Color_Parse( handle, &cgDC.Assets.shadowColor ) ) { + return qfalse; + } + cgDC.Assets.shadowFadeClamp = cgDC.Assets.shadowColor[3]; + continue; + } + } + //return qfalse; +} + +void CG_ParseMenu( const char *menuFile ) { + pc_token_t token; + int handle; + + handle = trap_PC_LoadSource( menuFile ); + if ( !handle ) { + handle = trap_PC_LoadSource( "ui/testhud.menu" ); + } + if ( !handle ) { + return; + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + //if ( Q_stricmp( token, "{" ) ) { + // Com_Printf( "Missing { in menu file\n" ); + // break; + //} + + //if ( menuCount == MAX_MENUS ) { + // Com_Printf( "Too many menus!\n" ); + // break; + //} + + if ( token.string[0] == '}' ) { + break; + } + + if ( Q_stricmp( token.string, "assetGlobalDef" ) == 0 ) { + if ( CG_Asset_Parse( handle ) ) { + continue; + } else { + break; + } + } + + + if ( Q_stricmp( token.string, "menudef" ) == 0 ) { + // start a new menu + Menu_New( handle ); + } + } + trap_PC_FreeSource( handle ); +} + +qboolean CG_Load_Menu( char **p ) { + char *token; + + token = COM_ParseExt( p, qtrue ); + + if ( token[0] != '{' ) { + return qfalse; + } + + while ( 1 ) { + + token = COM_ParseExt( p, qtrue ); + + if ( Q_stricmp( token, "}" ) == 0 ) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + CG_ParseMenu( token ); + } + return qfalse; +} + + + +void CG_LoadMenus( const char *menuFile ) { + char *token; + char *p; + int len, start; + fileHandle_t f; + static char buf[MAX_MENUDEFFILE]; + + start = trap_Milliseconds(); + + len = trap_FS_FOpenFile( menuFile, &f, FS_READ ); + if ( !f ) { + trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); + len = trap_FS_FOpenFile( "ui/hud.txt", &f, FS_READ ); + if ( !f ) { + trap_Error( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!\n" ); + } + } + + if ( len >= MAX_MENUDEFFILE ) { + trap_Error( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", menuFile, len, MAX_MENUDEFFILE ) ); + trap_FS_FCloseFile( f ); + return; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + COM_Compress( buf ); + + Menu_Reset(); + + p = buf; + + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if ( !token || token[0] == 0 || token[0] == '}' ) { + break; + } + + //if ( Q_stricmp( token, "{" ) ) { + // Com_Printf( "Missing { in menu file\n" ); + // break; + //} + + //if ( menuCount == MAX_MENUS ) { + // Com_Printf( "Too many menus!\n" ); + // break; + //} + + if ( Q_stricmp( token, "}" ) == 0 ) { + break; + } + + if ( Q_stricmp( token, "loadmenu" ) == 0 ) { + if ( CG_Load_Menu( &p ) ) { + continue; + } else { + break; + } + } + } + + Com_Printf( "UI menu load time = %d milli seconds\n", trap_Milliseconds() - start ); + +} + + + +static qboolean CG_OwnerDrawHandleKey( int ownerDraw, int flags, float *special, int key ) { + return qfalse; +} + + +static int CG_FeederCount( float feederID ) { + int i, count; + count = 0; + if ( feederID == FEEDER_REDTEAM_LIST ) { + for ( i = 0; i < cg.numScores; i++ ) { + if ( cg.scores[i].team == TEAM_AXIS ) { + count++; + } + } + } else if ( feederID == FEEDER_BLUETEAM_LIST ) { + for ( i = 0; i < cg.numScores; i++ ) { + if ( cg.scores[i].team == TEAM_ALLIES ) { + count++; + } + } + } else if ( feederID == FEEDER_SCOREBOARD ) { + return cg.numScores; + } + return count; +} + + + + +/////////////////////////// +/////////////////////////// + +static clientInfo_t * CG_InfoFromScoreIndex( int index, int team, int *scoreIndex ) { + int i, count; + + count = 0; + for ( i = 0; i < cg.numScores; i++ ) { + if ( cg.scores[i].team == team ) { + if ( count == index ) { + *scoreIndex = i; + return &cgs.clientinfo[cg.scores[i].client]; + } + count++; + } + } + + *scoreIndex = index; + return &cgs.clientinfo[ cg.scores[index].client ]; +} + +static const char *CG_FeederItemText( float feederID, int index, int column, qhandle_t *handle, int *numhandles ) { + int scoreIndex = 0; + clientInfo_t *info = NULL; + int team = -1; + score_t *sp = NULL; + + *handle = -1; + + if ( feederID == FEEDER_REDTEAM_LIST ) { + team = TEAM_AXIS; + } else if ( feederID == FEEDER_BLUETEAM_LIST ) { + team = TEAM_ALLIES; + } + + info = CG_InfoFromScoreIndex( index, team, &scoreIndex ); + sp = &cg.scores[scoreIndex]; + + if ( info && info->infoValid ) { + switch ( column ) { + case 0: + break; + case 3: + return info->name; + break; + case 4: + return va( "%i", info->score ); + break; + case 5: + return va( "%4i", sp->time ); + break; + case 6: + if ( sp->ping == -1 ) { + return "connecting"; + } + return va( "%4i", sp->ping ); + break; + } + } + + return ""; +} + +static qhandle_t CG_FeederItemImage( float feederID, int index ) { + return 0; +} + +static void CG_FeederSelection( float feederID, int index ) { + int i, count; + int team = ( feederID == FEEDER_REDTEAM_LIST ) ? TEAM_AXIS : TEAM_ALLIES; + count = 0; + for ( i = 0; i < cg.numScores; i++ ) { + if ( cg.scores[i].team == team ) { + if ( index == count ) { + cg.selectedScore = i; + } + count++; + } + } +} + +float CG_Cvar_Get( const char *cvar ) { + char buff[128]; + memset( buff, 0, sizeof( buff ) ); + trap_Cvar_VariableStringBuffer( cvar, buff, sizeof( buff ) ); + return atof( buff ); +} + +void CG_Text_PaintWithCursor( float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style ) { + CG_Text_Paint( x, y, scale, color, text, 0, limit, style ); +} + +static int CG_OwnerDrawWidth( int ownerDraw, float scale ) { + switch ( ownerDraw ) { + default: + break; + } + return 0; +} + +static int CG_PlayCinematic( const char *name, float x, float y, float w, float h ) { + return trap_CIN_PlayCinematic( name, x, y, w, h, CIN_loop ); +} + +static void CG_StopCinematic( int handle ) { + trap_CIN_StopCinematic( handle ); +} + +static void CG_DrawCinematic( int handle, float x, float y, float w, float h ) { + trap_CIN_SetExtents( handle, x, y, w, h ); + trap_CIN_DrawCinematic( handle ); +} + +static void CG_RunCinematicFrame( int handle ) { + trap_CIN_RunCinematic( handle ); +} + + + + +/* +================= +CG_LoadHudMenu(); + +================= +*/ +void CG_LoadHudMenu() { + cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip; + cgDC.setColor = &trap_R_SetColor; + cgDC.drawHandlePic = &CG_DrawPic; + cgDC.drawStretchPic = &trap_R_DrawStretchPic; + cgDC.drawText = &CG_Text_Paint; + cgDC.drawTextExt = &CG_Text_Paint_Ext; + cgDC.textWidth = &CG_Text_Width; + cgDC.textWidthExt = &CG_Text_Width_Ext; + cgDC.textHeight = &CG_Text_Height; + cgDC.textHeightExt = &CG_Text_Height_Ext; + cgDC.textFont = &CG_Text_SetActiveFont; + cgDC.registerModel = &trap_R_RegisterModel; + cgDC.modelBounds = &trap_R_ModelBounds; + cgDC.fillRect = &CG_FillRect; + cgDC.drawRect = &CG_DrawRect; + cgDC.drawSides = &CG_DrawSides; + cgDC.drawTopBottom = &CG_DrawTopBottom; + cgDC.clearScene = &trap_R_ClearScene; + cgDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; + cgDC.renderScene = &trap_R_RenderScene; + cgDC.registerFont = &trap_R_RegisterFont; + cgDC.ownerDrawItem = &CG_OwnerDraw; + cgDC.getValue = &CG_GetValue; + cgDC.ownerDrawVisible = &CG_OwnerDrawVisible; + cgDC.runScript = &CG_RunMenuScript; + cgDC.getTeamColor = &CG_GetTeamColor; + cgDC.setCVar = trap_Cvar_Set; + cgDC.getCVarString = trap_Cvar_VariableStringBuffer; + cgDC.getCVarValue = CG_Cvar_Get; + cgDC.drawTextWithCursor = &CG_Text_PaintWithCursor; + cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode; + cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode; + cgDC.startLocalSound = &trap_S_StartLocalSound; + cgDC.ownerDrawHandleKey = &CG_OwnerDrawHandleKey; + cgDC.feederCount = &CG_FeederCount; + cgDC.feederItemImage = &CG_FeederItemImage; + cgDC.feederItemText = &CG_FeederItemText; + cgDC.feederSelection = &CG_FeederSelection; + cgDC.setBinding = &trap_Key_SetBinding; // NERVE - SMF + cgDC.getBindingBuf = &trap_Key_GetBindingBuf; // NERVE - SMF + cgDC.getKeysForBinding = &trap_Key_KeysForBinding; + cgDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf; // NERVE - SMF + cgDC.translateString = &CG_TranslateString; // NERVE - SMF + //cgDC.executeText = &trap_Cmd_ExecuteText; + cgDC.Error = &Com_Error; + cgDC.Print = &Com_Printf; + cgDC.ownerDrawWidth = &CG_OwnerDrawWidth; + //cgDC.Pause = &CG_Pause; + cgDC.registerSound = &trap_S_RegisterSound; + cgDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; + cgDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; + cgDC.playCinematic = &CG_PlayCinematic; + cgDC.stopCinematic = &CG_StopCinematic; + cgDC.drawCinematic = &CG_DrawCinematic; + cgDC.runCinematicFrame = &CG_RunCinematicFrame; + cgDC.descriptionForCampaign = &CG_DescriptionForCampaign; + cgDC.nameForCampaign = &CG_NameForCampaign; + cgDC.add2dPolys = &trap_R_Add2dPolys; + cgDC.updateScreen = &trap_UpdateScreen; + cgDC.getHunkData = &trap_GetHunkData; + cgDC.getConfigString = &CG_ConfigStringCopy; + + cgDC.xscale = cgs.screenXScale; + cgDC.yscale = cgs.screenYScale; + + Init_Display( &cgDC ); + + Menu_Reset(); + +// CG_LoadMenus("ui/hud.txt"); + + CG_Text_SetActiveFont( 0 ); +} + +void CG_AssetCache() { + cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); + cgDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); + cgDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); + cgDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); + cgDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); + cgDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); + cgDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); + cgDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); + cgDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); + cgDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR ); + cgDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN ); + cgDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP ); + cgDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT ); + cgDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT ); + cgDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB ); + cgDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR ); + cgDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB ); +} + + +extern qboolean initTrails; +void CG_ClearTrails( void ); +extern qboolean initparticles; +void CG_ClearParticles( void ); + +/* +================= +CG_Init + +Called after every level change or subsystem restart +Will perform callbacks to make the loading info screen update. +================= +*/ +#ifdef _DEBUG +#define DEBUG_INITPROFILE_INIT int elapsed, dbgTime = trap_Milliseconds(); +#define DEBUG_INITPROFILE_EXEC( f ) if ( developer.integer ) { CG_Printf( "^5%s passed in %i msec\n", f, elapsed = trap_Milliseconds() - dbgTime ); dbgTime += elapsed; } +#endif // _DEBUG +void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum, qboolean demoPlayback ) { + const char *s; + int i; +#ifdef _DEBUG + DEBUG_INITPROFILE_INIT +#endif // _DEBUG + +// int startat = trap_Milliseconds(); + + // clear everything + memset( &cgs, 0, sizeof( cgs ) ); + memset( &cg, 0, sizeof( cg ) ); + memset( cg_entities, 0, sizeof( cg_entities ) ); + memset( cg_weapons, 0, sizeof( cg_weapons ) ); + memset( cg_items, 0, sizeof( cg_items ) ); + + cgs.initing = qtrue; + + for ( i = 0; i < MAX_CLIENTS; i++ ) { + cg.artilleryRequestTime[i] = -99999; + } + + CG_InitStatsDebug(); + + cgs.ccZoomFactor = 1.f; + + // OSP - sync to main refdef + cg.refdef_current = &cg.refdef; + + // get the rendering configuration from the client system + trap_GetGlconfig( &cgs.glconfig ); + cgs.screenXScale = cgs.glconfig.vidWidth / 640.0; + cgs.screenYScale = cgs.glconfig.vidHeight / 480.0; + + // RF, init the anim scripting + cgs.animScriptData.soundIndex = CG_SoundScriptPrecache; + cgs.animScriptData.playSound = CG_SoundPlayIndexedScript; + + cg.clientNum = clientNum; // NERVE - SMF - TA merge + + cgs.processedSnapshotNum = serverMessageNum; + cgs.serverCommandSequence = serverCommandSequence; + + cgs.ccRequestedObjective = -1; + cgs.ccCurrentCamObjective = -2; + + // load a few needed things before we do any screen updates + cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/hudchars" ); //trap_R_RegisterShader( "gfx/2d/bigchars" ); + // JOSEPH 4-17-00 + cgs.media.menucharsetShader = trap_R_RegisterShader( "gfx/2d/hudchars" ); + // END JOSEPH + cgs.media.whiteShader = trap_R_RegisterShader( "white" ); + cgs.media.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" ); + cgs.media.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" ); + cgs.media.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" ); + + CG_RegisterCvars(); + + CG_InitConsoleCommands(); + + // Gordon: moved this up so it's initialized for the loading screen + CG_LoadHudMenu(); // load new hud stuff + CG_AssetCache(); + + // get the gamestate from the client system + trap_GetGameState( &cgs.gameState ); + + cg.warmupCount = -1; + + CG_ParseServerinfo(); + CG_ParseWolfinfo(); // NERVE - SMF + + cgs.campaignInfoLoaded = qfalse; + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + CG_LocateCampaign(); + } else if ( cgs.gametype == GT_WOLF_STOPWATCH || cgs.gametype == GT_WOLF_LMS || cgs.gametype == GT_WOLF ) { + CG_LocateArena(); + } + + CG_ClearTrails(); + CG_ClearParticles(); + + InitSmokeSprites(); + + // check version + s = CG_ConfigString( CS_GAME_VERSION ); + if ( strcmp( s, GAME_VERSION ) ) { + CG_Error( "Client/Server game mismatch: '%s/%s'", GAME_VERSION, s ); + } + trap_Cvar_Set( "cg_etVersion", GAME_VERSION_DATED ); // So server can check + + s = CG_ConfigString( CS_LEVEL_START_TIME ); + cgs.levelStartTime = atoi( s ); + + s = CG_ConfigString( CS_INTERMISSION_START_TIME ); + cgs.intermissionStartTime = atoi( s ); + + + // OSP + CG_ParseServerVersionInfo( CG_ConfigString( CS_VERSIONINFO ) ); + CG_ParseReinforcementTimes( CG_ConfigString( CS_REINFSEEDS ) ); + + CG_initStrings(); + CG_windowInit(); + + cgs.smokeWindDir = crandom(); + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "initialization" ) +#endif // DEBUG + + // load the new map + CG_LoadingString( "collision map" ); + + trap_CM_LoadMap( cgs.mapname ); + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "loadmap" ) +#endif // DEBUG + + String_Init(); + + cg.loading = qtrue; // force players to load instead of defer + + CG_LoadingString( "sounds" ); + + CG_RegisterSounds(); + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "sounds" ) +#endif // DEBUG + + CG_LoadingString( "graphics" ); + + CG_RegisterGraphics(); + + CG_LoadingString( "flamechunks" ); + + CG_InitFlameChunks(); // RF, register and clear all flamethrower resources + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "graphics" ) +#endif // DEBUG + + CG_LoadingString( "clients" ); + + CG_RegisterClients(); // if low on memory, some clients will be deferred + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "clients" ) +#endif // DEBUG + + cg.loading = qfalse; // future players will be deferred + + CG_InitLocalEntities(); + + BG_BuildSplinePaths(); + + CG_InitMarkPolys(); + + // remove the last loading update + cg.infoScreenText[0] = 0; + + // Make sure we have update values (scores) + CG_SetConfigValues(); + + CG_StartMusic(); + + cg.lightstylesInited = qfalse; + + CG_LoadingString( "" ); + + CG_ShaderStateChanged(); + + CG_ChargeTimesChanged(); + + trap_S_ClearLoopingSounds(); + trap_S_ClearSounds( qfalse ); + + cg.teamWonRounds[1] = atoi( CG_ConfigString( CS_ROUNDSCORES1 ) ); + cg.teamWonRounds[0] = atoi( CG_ConfigString( CS_ROUNDSCORES2 ) ); + + cg.filtercams = atoi( CG_ConfigString( CS_FILTERCAMS ) ) ? qtrue : qfalse; + + CG_ParseFireteams(); + + CG_ParseOIDInfos(); + + CG_InitPM(); + + CG_ParseSpawns(); + + CG_ParseTagConnects(); + +#ifdef _DEBUG + DEBUG_INITPROFILE_EXEC( "misc" ) +#endif // DEBUG + + CG_ParseSkyBox(); + + CG_SetupCabinets(); + + if ( !CG_IsSinglePlayer() ) { + trap_S_FadeAllSound( 1.0f, 0, qfalse ); // fade sound up + } + + // OSP + cgs.dumpStatsFile = 0; + cgs.dumpStatsTime = 0; +// CG_Printf("Time taken: %i\n", trap_Milliseconds() - startat); +} + +/* +================= +CG_Shutdown + +Called before every level change or subsystem restart +================= +*/ +void CG_Shutdown( void ) { + // some mods may need to do cleanup work here, + // like closing files or archiving session data + + CG_EventHandling( CGAME_EVENT_NONE, qtrue ); + if ( cg.demoPlayback ) { + trap_Cvar_Set( "timescale", "1" ); + } +} + +// returns true if game is single player (or coop) +qboolean CG_IsSinglePlayer( void ) { + if ( cg_gameType.integer == GT_SINGLE_PLAYER || cgs.gametype == GT_COOP ) { + return qtrue; + } + + return qfalse; +} + + +qboolean CG_CheckExecKey( int key ) { + if ( !cg.showFireteamMenu ) { + return qfalse; + } + + return CG_FireteamCheckExecKey( key, qfalse ); +} diff --git a/src/cgame/cg_marks.c b/src/cgame/cg_marks.c new file mode 100644 index 0000000..f4eb990 --- /dev/null +++ b/src/cgame/cg_marks.c @@ -0,0 +1,386 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_marks.c -- wall marks + +#include "cg_local.h" + +/* +=================================================================== + +MARK POLYS + +=================================================================== +*/ + + +markPoly_t cg_activeMarkPolys; // double linked list +markPoly_t *cg_freeMarkPolys; // single linked list +markPoly_t cg_markPolys[MAX_MARK_POLYS]; + +/* +=================== +CG_InitMarkPolys + +This is called at startup and for tournement restarts +=================== +*/ +void CG_InitMarkPolys( void ) { + int i; + markPoly_t *trav, *lasttrav; + + memset( cg_markPolys, 0, sizeof( cg_markPolys ) ); + + cg_activeMarkPolys.nextMark = &cg_activeMarkPolys; + cg_activeMarkPolys.prevMark = &cg_activeMarkPolys; + cg_freeMarkPolys = cg_markPolys; + for ( i = 0, trav = cg_markPolys + 1, lasttrav = cg_markPolys ; i < MAX_MARK_POLYS - 1 ; i++, trav++ ) { + lasttrav->nextMark = trav; + lasttrav = trav; + } +} + + +/* +================== +CG_FreeMarkPoly +================== +*/ +void CG_FreeMarkPoly( markPoly_t *le ) { + if ( !le->prevMark ) { + CG_Error( "CG_FreeLocalEntity: not active" ); + } + + // remove from the doubly linked active list + le->prevMark->nextMark = le->nextMark; + le->nextMark->prevMark = le->prevMark; + + // the free list is only singly linked + le->nextMark = cg_freeMarkPolys; + cg_freeMarkPolys = le; +} + +/* +=================== +CG_AllocMark + +Will allways succeed, even if it requires freeing an old active mark +=================== +*/ +markPoly_t *CG_AllocMark( int endTime ) { + markPoly_t *le; //, *trav, *lastTrav; + int time; + + if ( !cg_freeMarkPolys ) { + // no free entities, so free the one at the end of the chain + // remove the oldest active entity + time = cg_activeMarkPolys.prevMark->time; + while ( cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time ) { + CG_FreeMarkPoly( cg_activeMarkPolys.prevMark ); + } + } + + le = cg_freeMarkPolys; + cg_freeMarkPolys = cg_freeMarkPolys->nextMark; + + memset( le, 0, sizeof( *le ) ); + + // Ridah, TODO: sort this, so the list is always sorted by longest duration -> shortest duration, + // this way the shortest duration mark will always get overwritten first + //for (trav = cg_activeMarkPolys.nextMark; (trav->duration + trav->time > endTime) && (trav != cg_activeMarkPolys.prevMark) ; lastTrav = trav, trav++ ) { + // Respect the FOR loop + //} + + // link into the active list + le->nextMark = cg_activeMarkPolys.nextMark; + le->prevMark = &cg_activeMarkPolys; + cg_activeMarkPolys.nextMark->prevMark = le; + cg_activeMarkPolys.nextMark = le; + return le; +} + + + +/* +CG_ImpactMark() +projection is a normal and distance (not a plane, but rather how far to project) +it MUST be normalized! +if lifeTime < 0, then generate a temporary mark +*/ + +// Ridah, increased this since we leave them around for longer +#define MAX_MARK_FRAGMENTS 384 +#define MAX_MARK_POINTS 1024 +//#define MAX_MARK_FRAGMENTS 128 +//#define MAX_MARK_POINTS 384 + +// these are ignored now for the most part +//#define MARK_TOTAL_TIME 20000 // (SA) made this a cvar: cg_markTime (we could cap the time or remove marks quicker if too long a time starts to cause new marks to not appear) +#define MARK_FADE_TIME 10000 + +// comment out to use old-style mark code +#define YDNAR_DECAL_MARKS + +void CG_ImpactMark( qhandle_t markShader, vec3_t origin, vec4_t projection, float radius, float orientation, float r, float g, float b, float a, int lifeTime ) +#ifdef YDNAR_DECAL_MARKS +{ + int i; + vec3_t pushedOrigin, axis[ 3 ]; + vec4_t color; + int fadeTime; + vec3_t points[ 4 ]; + + + /* early out */ + if ( lifeTime == 0 ) { + return; + } + + /* set projection (inverse of dir) */ + //% VectorSubtract( vec3_origin, dir, projection ); + //% VectorNormalize( projection ); + //% projection[ 3 ] = radius * 8; + + /* make rotated polygon axis */ + VectorCopy( projection, axis[ 0 ] ); + PerpendicularVector( axis[ 1 ], axis[ 0 ] ); + RotatePointAroundVector( axis[ 2 ], axis[ 0 ], axis[ 1 ], -orientation ); + CrossProduct( axis[ 0 ], axis[ 2 ], axis[ 1 ] ); + + /* push the origin out a bit */ + VectorMA( origin, -1.0f, axis[ 0 ], pushedOrigin ); + + /* create the full polygon */ + for ( i = 0; i < 3; i++ ) + { + /* old */ + //% points[ 0 ][ i ] = pushedOrigin[ i ] - radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ]; + //% points[ 1 ][ i ] = pushedOrigin[ i ] + radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ]; + //% points[ 2 ][ i ] = pushedOrigin[ i ] + radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ]; + //% points[ 3 ][ i ] = pushedOrigin[ i ] - radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ]; + + /* new */ + points[ 0 ][ i ] = pushedOrigin[ i ] - radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ]; + points[ 1 ][ i ] = pushedOrigin[ i ] - radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ]; + points[ 2 ][ i ] = pushedOrigin[ i ] + radius * axis[ 1 ][ i ] + radius * axis[ 2 ][ i ]; + points[ 3 ][ i ] = pushedOrigin[ i ] + radius * axis[ 1 ][ i ] - radius * axis[ 2 ][ i ]; + } + + /* debug code */ + #if 0 + VectorSet( points[ 0 ], origin[ 0 ] - radius, origin[ 1 ] - radius, origin[ 2 ] ); + VectorSet( points[ 1 ], origin[ 0 ] - radius, origin[ 1 ] + radius, origin[ 2 ] ); + VectorSet( points[ 2 ], origin[ 0 ] + radius, origin[ 1 ] + radius, origin[ 2 ] ); + VectorSet( points[ 3 ], origin[ 0 ] + radius, origin[ 1 ] - radius, origin[ 2 ] ); + CG_Printf( "Dir: %f %f %f\n", dir[ 0 ], dir[ 1 ], dir[ 2 ] ); + #endif + + /* set color */ + color[ 0 ] = r; + color[ 1 ] = g; + color[ 2 ] = b; + color[ 3 ] = a; + + /* set decal times (in seconds) */ + fadeTime = lifeTime >> 4; + + /* add the decal */ + trap_R_ProjectDecal( markShader, 4, points, projection, color, lifeTime, fadeTime ); +} +#else +{ + vec3_t axis[3]; + float texCoordScale; + vec3_t originalPoints[4]; + byte colors[4]; + int i, j; + int numFragments; + markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf; + vec5_t markPoints[MAX_MARK_POINTS]; // Ridah, made it vec5_t so it includes S/T + vec3_t projection; + int multMaxFragments = 1; + + if ( !cg_markTime.integer ) { + return; + } + + if ( radius <= 0 ) { + return; + } + + if ( temporary ) { + if ( CG_CullPointAndRadius( origin, radius ) ) { + return; + } + } + + // Ridah, if no duration, use the default + if ( duration < 0 ) { + if ( duration == -2 ) { + multMaxFragments = -1; // use original mapping + } + +// duration = MARK_TOTAL_TIME; + duration = cg_markTime.integer; + } + + // create the texture axis + VectorNormalize2( dir, axis[0] ); + PerpendicularVector( axis[1], axis[0] ); + RotatePointAroundVector( axis[2], axis[0], axis[1], orientation ); + CrossProduct( axis[0], axis[2], axis[1] ); + + texCoordScale = 0.5 * 1.0 / radius; + + // create the full polygon + for ( i = 0 ; i < 3 ; i++ ) { + originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i]; + originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i]; + originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i]; + originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i]; + } + + // get the fragments + //VectorScale( dir, -20, projection ); + VectorScale( dir, radius * 2, projection ); + numFragments = trap_CM_MarkFragments( (int)orientation, (void *)originalPoints, + projection, MAX_MARK_POINTS, (float *)&markPoints[0], + MAX_MARK_FRAGMENTS * multMaxFragments, markFragments ); + + colors[0] = red * 255; + colors[1] = green * 255; + colors[2] = blue * 255; + colors[3] = alpha * 255; + + for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) { + polyVert_t *v; + polyVert_t verts[MAX_VERTS_ON_POLY]; + markPoly_t *mark; + qboolean hasST; + + // we have an upper limit on the complexity of polygons + // that we store persistantly + if ( mf->numPoints > MAX_VERTS_ON_POLY ) { + mf->numPoints = MAX_VERTS_ON_POLY; + } + if ( mf->numPoints < 0 ) { + hasST = qtrue; + mf->numPoints *= -1; + } else { + hasST = qfalse; + } + for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) { + vec3_t delta; + + VectorCopy( markPoints[mf->firstPoint + j], v->xyz ); + + if ( !hasST ) { + VectorSubtract( v->xyz, origin, delta ); + v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale; + v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale; + } else { + v->st[0] = markPoints[mf->firstPoint + j][3]; + v->st[1] = markPoints[mf->firstPoint + j][4]; + } + + *(int *)v->modulate = *(int *)colors; + } + + // if it is a temporary (shadow) mark, add it immediately and forget about it + if ( temporary ) { + trap_R_AddPolyToScene( markShader, mf->numPoints, verts ); + continue; + } + + // otherwise save it persistantly + mark = CG_AllocMark( cg.time + duration ); + mark->time = cg.time; + mark->alphaFade = alphaFade; + mark->markShader = markShader; + mark->poly.numVerts = mf->numPoints; + mark->color[0] = red; + mark->color[1] = green; + mark->color[2] = blue; + mark->color[3] = alpha; + mark->duration = duration; + memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) ); + } +} +#endif + + + +/* +=============== +CG_AddMarks +=============== +*/ + +void CG_AddMarks( void ) { + int j; + markPoly_t *mp, *next; + int t; + int fade; + + if ( !cg_markTime.integer ) { + return; + } + + mp = cg_activeMarkPolys.nextMark; + for ( ; mp != &cg_activeMarkPolys ; mp = next ) { + // grab next now, so if the local entity is freed we + // still have it + next = mp->nextMark; + + // see if it is time to completely remove it + if ( cg.time > mp->time + mp->duration ) { + CG_FreeMarkPoly( mp ); + continue; + } + + // fade all marks out with time + t = mp->time + mp->duration - cg.time; + if ( t < (float)mp->duration / 2.0 ) { + fade = (int)( 255.0 * (float)t / ( (float)mp->duration / 2.0 ) ); + if ( mp->alphaFade ) { + for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { + mp->verts[j].modulate[3] = fade; + } + } else { + for ( j = 0 ; j < mp->poly.numVerts ; j++ ) { + mp->verts[j].modulate[0] = mp->color[0] * fade; + mp->verts[j].modulate[1] = mp->color[1] * fade; + mp->verts[j].modulate[2] = mp->color[2] * fade; + } + } + } + + trap_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts ); + } +} + diff --git a/src/cgame/cg_missionbriefing.c b/src/cgame/cg_missionbriefing.c new file mode 100644 index 0000000..147c57e --- /dev/null +++ b/src/cgame/cg_missionbriefing.c @@ -0,0 +1,401 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +const char* CG_DescriptionForCampaign( void ) { + return cgs.campaignInfoLoaded ? cgs.campaignData.campaignDescription : NULL; +} + +const char* CG_NameForCampaign( void ) { + return cgs.campaignInfoLoaded ? cgs.campaignData.campaignName : NULL; +} + +qboolean CG_FindCampaignInFile( char *filename, char *campaignShortName, cg_campaignInfo_t *info ) { + int handle; + pc_token_t token; +// char* dummy; + qboolean campaignFound = qfalse; + + info->mapCount = 0; + + handle = trap_PC_LoadSource( filename ); + + if ( !handle ) { + trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); + return qfalse; + } + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_PC_FreeSource( handle ); + return qfalse; + } + + if ( *token.string != '{' ) { + trap_PC_FreeSource( handle ); + return qfalse; + } + + while ( trap_PC_ReadToken( handle, &token ) ) { + if ( *token.string == '}' ) { + if ( campaignFound ) { + trap_PC_FreeSource( handle ); + return qtrue; + } + + if ( !trap_PC_ReadToken( handle, &token ) ) { + // eof + trap_PC_FreeSource( handle ); + return qfalse; + } + + if ( *token.string != '{' ) { + trap_Print( va( S_COLOR_RED "unexpected token '%s' inside: %s\n", token.string, filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + info->mapCount = 0; + } else if ( !Q_stricmp( token.string, "shortname" ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { // don't do a stringparse due to memory constraints + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + if ( !Q_stricmp( token.string, campaignShortName ) ) { + campaignFound = qtrue; + } + } else if ( !Q_stricmp( token.string, "next" ) || + !Q_stricmp( token.string, "image" ) ) { + //if( !PC_String_Parse( handle, &dummy ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { // don't do a stringparse due to memory constraints + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + } else if ( !Q_stricmp( token.string, "description" ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + Q_strncpyz( info->campaignDescription, token.string, sizeof( info->campaignDescription ) ); + } else if ( !Q_stricmp( token.string, "name" ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + Q_strncpyz( info->campaignName, token.string, sizeof( info->campaignName ) ); + } else if ( !Q_stricmp( token.string, "maps" ) ) { + char *ptr, mapname[128], *mapnameptr; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + ptr = token.string; + while ( *ptr ) { + mapnameptr = mapname; + while ( *ptr && *ptr != ';' ) { + *mapnameptr++ = *ptr++; + } + if ( *ptr ) { + ptr++; + } + *mapnameptr = '\0'; + + if ( info->mapCount >= MAX_MAPS_PER_CAMPAIGN ) { + trap_Print( va( S_COLOR_RED "too many maps for a campaign inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + break; + } + + Q_strncpyz( info->mapnames[info->mapCount++], mapname, MAX_QPATH ); + } + } else if ( !Q_stricmp( token.string, "maptc" ) ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + info->mapTC[0][0] = token.floatvalue; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + info->mapTC[0][1] = token.floatvalue; + + info->mapTC[1][0] = 650 + info->mapTC[0][0]; + info->mapTC[1][1] = 650 + info->mapTC[0][1]; + } + } + + trap_PC_FreeSource( handle ); + return qfalse; +} + +qboolean CG_FindArenaInfo( char* filename, char* mapname, arenaInfo_t* info ) { + int handle; + pc_token_t token; + const char* dummy; + qboolean found = qfalse; + + handle = trap_PC_LoadSource( filename ); + + if ( !handle ) { + trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); + return qfalse; + } + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_PC_FreeSource( handle ); + return qfalse; + } + + if ( *token.string != '{' ) { + trap_PC_FreeSource( handle ); + return qfalse; + } + + while ( trap_PC_ReadToken( handle, &token ) ) { + if ( *token.string == '}' ) { + if ( found ) { +// info->image = trap_R_RegisterShaderNoMip(va("levelshots/%s", mapname)); + trap_PC_FreeSource( handle ); + return qtrue; + } + found = qfalse; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + // eof + trap_PC_FreeSource( handle ); + return qfalse; + } + + if ( *token.string != '{' ) { + trap_Print( va( S_COLOR_RED "unexpected token '%s' inside: %s\n", token.string, filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + } else if ( !Q_stricmp( token.string, "objectives" ) || !Q_stricmp( token.string, "description" ) || !Q_stricmp( token.string, "type" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + } else if ( !Q_stricmp( token.string, "longname" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + //char* p = info->longname; + + Q_strncpyz( info->longname, dummy, 128 ); + // Gordon: removing cuz, er, no-one knows why it's here!... +/* while(*p) { + *p = toupper(*p); + p++; + }*/ + } + } else if ( !Q_stricmp( token.string, "map" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + if ( !Q_stricmp( dummy, mapname ) ) { + found = qtrue; + } + } + } else if ( !Q_stricmp( token.string, "Timelimit" ) || !Q_stricmp( token.string, "AxisRespawnTime" ) || !Q_stricmp( token.string, "AlliedRespawnTime" ) ) { + if ( !PC_Int_Parse( handle, (int*)&dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + } else if ( !Q_stricmp( token.string, "lmsbriefing" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + Q_strncpyz( info->lmsdescription, dummy, sizeof( info->lmsdescription ) ); + } + } else if ( !Q_stricmp( token.string, "briefing" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + Q_strncpyz( info->description, dummy, sizeof( info->description ) ); + } + } else if ( !Q_stricmp( token.string, "alliedwintext" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + Q_strncpyz( info->alliedwintext, dummy, sizeof( info->description ) ); + } + } else if ( !Q_stricmp( token.string, "axiswintext" ) ) { + if ( !PC_String_Parse( handle, &dummy ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } else { + Q_strncpyz( info->axiswintext, dummy, sizeof( info->description ) ); + } + } else if ( !Q_stricmp( token.string, "mapposition_x" ) ) { + vec_t x; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + x = token.floatvalue; + + info->mappos[0] = x; + } else if ( !Q_stricmp( token.string, "mapposition_y" ) ) { + vec_t y; + + if ( !trap_PC_ReadToken( handle, &token ) ) { + trap_Print( va( S_COLOR_RED "unexpected end of file inside: %s\n", filename ) ); + trap_PC_FreeSource( handle ); + return qfalse; + } + + y = token.floatvalue; + + info->mappos[1] = y; + } + } + + trap_PC_FreeSource( handle ); + return qfalse; +} + +void CG_LocateCampaign( void ) { +/* int numdirs; + char filename[MAX_QPATH]; + char dirlist[1024]; + char* dirptr; + int i, dirlen; + qboolean found = qfalse; + + // get all campaigns from .campaign files + numdirs = trap_FS_GetFileList( "scripts", ".campaign", dirlist, 1024 ); + dirptr = dirlist; + for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { + dirlen = strlen(dirptr); + strcpy(filename, "scripts/"); + strcat(filename, dirptr); + if(CG_FindCurrentCampaignInFile(filename, &cgs.campaignData)) { + found = qtrue; + break; + } + } + + if(!found) { + return; + } + + for(i = 0; i < cgs.campaignData.mapCount; i++ ) { + Com_sprintf( filename, sizeof(filename), "scripts/%s.arena", cgs.campaignData.mapnames[i] ); + // Gordon: horrible hack, but i dont plan to parse EVERY .arena to get a map briefing... + if( !CG_FindArenaInfo( "scripts/wolfmp.arena", cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] ) && + !CG_FindArenaInfo( "scripts/wolfxp.arena", cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] ) && + !CG_FindArenaInfo( filename, cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] )) { + return; + } + } + + cgs.campaignInfoLoaded = qtrue;*/ + + int numdirs; + char filename[MAX_QPATH]; + char dirlist[1024]; + char* dirptr; + int i, dirlen; + qboolean found = qfalse; + + // get all campaigns from .campaign files + numdirs = trap_FS_GetFileList( "scripts", ".campaign", dirlist, 1024 ); + dirptr = dirlist; + for ( i = 0; i < numdirs; i++, dirptr += dirlen + 1 ) { + dirlen = strlen( dirptr ); + Q_strncpyz( filename, "scripts/", MAX_QPATH ); + Q_strcat( filename, MAX_QPATH, dirptr ); + if ( CG_FindCampaignInFile( filename, cgs.currentCampaign, &cgs.campaignData ) ) { + found = qtrue; + break; + } + } + + if ( !found ) { + return; + } + + for ( i = 0; i < cgs.campaignData.mapCount; i++ ) { + Com_sprintf( filename, sizeof( filename ), "scripts/%s.arena", cgs.campaignData.mapnames[i] ); + // Gordon: horrible hack, but i dont plan to parse EVERY .arena to get a map briefing... + if ( /*!CG_FindArenaInfo( "scripts/wolfmp.arena", cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] ) && + !CG_FindArenaInfo( "scripts/wolfxp.arena", cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] ) &&*/ + !CG_FindArenaInfo( filename, cgs.campaignData.mapnames[i], &cgs.campaignData.arenas[i] ) ) { + return; + } + } + + cgs.campaignInfoLoaded = qtrue; +} + +void CG_LocateArena( void ) { + char filename[MAX_QPATH]; + + Com_sprintf( filename, sizeof( filename ), "scripts/%s.arena", cgs.rawmapname ); + + if ( /*!CG_FindArenaInfo( "scripts/wolfmp.arena", cgs.rawmapname, &cgs.arenaData ) && + !CG_FindArenaInfo( "scripts/wolfxp.arena", cgs.rawmapname, &cgs.arenaData ) &&*/ + !CG_FindArenaInfo( filename, cgs.rawmapname, &cgs.arenaData ) ) { + return; + } + + cgs.arenaInfoLoaded = qtrue; +} diff --git a/src/cgame/cg_multiview.c b/src/cgame/cg_multiview.c new file mode 100644 index 0000000..4e7b4a0 --- /dev/null +++ b/src/cgame/cg_multiview.c @@ -0,0 +1,883 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_multiview.c: Multiview handling +// ---------------------------------- +// 02 Sep 02 +// rhea@OrangeSmoothie.org +// +#include "cg_local.h" +#include "../ui/ui_shared.h" +#include "../game/bg_local.h" + +void CG_CalcVrect( void ); +void CG_DrawPlayerWeaponIcon( rectDef_t *rect, qboolean drawHighlighted, int align, vec4_t *refcolor ); + + +// Explicit server command to add a view to the client's snapshot +void CG_mvNew_f( void ) { + if ( cg.demoPlayback || trap_Argc() < 2 ) { + return; + } else { + int pID; + char aName[64]; + + trap_Args( aName, sizeof( aName ) ); + pID = CG_findClientNum( aName ); + + if ( pID >= 0 && !CG_mvMergedClientLocate( pID ) ) { + trap_SendClientCommand( va( "mvadd %d\n", pID ) ); + } + } +} + +// Explicit server command to remove a view from the client's snapshot +void CG_mvDelete_f( void ) { + if ( cg.demoPlayback ) { + return; + } else { + int pID = -1; + + if ( trap_Argc() > 1 ) { + char aName[64]; + + trap_Args( aName, sizeof( aName ) ); + pID = CG_findClientNum( aName ); + } else { + cg_window_t *w = cg.mvCurrentActive; + if ( w != NULL ) { + pID = ( w->mvInfo & MV_PID ); + } + } + + if ( pID >= 0 && CG_mvMergedClientLocate( pID ) ) { + trap_SendClientCommand( va( "mvdel %d\n", pID ) ); + } + } +} + + +// Swap highlighted window with main view +void CG_mvSwapViews_f( void ) { + if ( cg.mv_cnt >= 2 && cg.mvCurrentActive != cg.mvCurrentMainview ) { + CG_mvMainviewSwap( cg.mvCurrentActive ); + } +} + + +// Shut down a window view for a particular MV client +void CG_mvHideView_f( void ) { + if ( cg.mvCurrentActive == NULL || cg.mvCurrentMainview == cg.mvCurrentActive ) { + return; + } + + CG_mvFree( cg.mvCurrentActive->mvInfo & MV_PID ); +} + + +// Activate a window view for a particular MV client +void CG_mvShowView_f( void ) { + int i; + + for ( i = 0; i < cg.mvTotalClients; i++ ) { + if ( cg.mvOverlay[i].fActive ) { + if ( cg.mvOverlay[i].w == NULL ) { + CG_mvCreate( cg.mvOverlay[i].pID ); + CG_mvOverlayUpdate(); + } + return; + } + } +} + +// Toggle a view window on/off +void CG_mvToggleView_f( void ) { + int i; + + for ( i = 0; i < cg.mvTotalClients; i++ ) { + if ( cg.mvOverlay[i].fActive ) { + if ( cg.mvOverlay[i].w != NULL ) { + CG_mvHideView_f(); + } else { + CG_mvCreate( cg.mvOverlay[i].pID ); + CG_mvOverlayUpdate(); + } + return; + } + } +} + +// Toggle all views +void CG_mvToggleAll_f( void ) { + if ( !cg.demoPlayback ) { + trap_SendClientCommand( ( cg.mvTotalClients > 0 ) ? "mvnone\n" : "mvall\n" ); + } +} + + + +//////////////////////////////////////////////// +// +// Multiview Primitives +// +// + +/////////////////////////////// +// Create a new view window +// +void CG_mvCreate( int pID ) { + cg_window_t *w; + + if ( CG_mvClientLocate( pID ) != NULL ) { + return; + } + + w = CG_windowAlloc( WFX_MULTIVIEW, 100 ); + if ( w == NULL ) { + return; + } + + // Window specific + w->id = WID_NONE; + w->x = ( cg.mv_cnt == 0 ) ? 0 : 30 + ( 12 * pID ); + w->y = ( cg.mv_cnt == 0 ) ? 0 : 300 + ( 5 * pID ); + w->w = ( cg.mv_cnt == 0 ) ? 640 : 128; + w->h = ( cg.mv_cnt == 0 ) ? 480 : 96; + w->mvInfo = ( pID & MV_PID ) | MV_SELECTED; + w->state = ( cg.mv_cnt == 0 ) ? WSTATE_COMPLETE : WSTATE_START; + + if ( cg.mv_cnt == 0 ) { + cg.mvCurrentMainview = w; + cg.mvCurrentActive = cg.mvCurrentMainview; + + if ( cg_specHelp.integer > 0 && !cg.demoPlayback ) { + CG_ShowHelp_On( &cg.spechelpWindow ); + } + } + + cg.mv_cnt++; +} + +/////////////////////////// +// Delete a view window +// +void CG_mvFree( int pID ) { + cg_window_t *w = CG_mvClientLocate( pID ); + + if ( w != NULL ) { + // Free it in mvDraw() + w->targetTime = 100; + w->time = trap_Milliseconds(); + w->state = WSTATE_SHUTDOWN; + } +} + +cg_window_t *CG_mvClientLocate( int pID ) { + int i; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + + for ( i = 0; i < wh->numActiveWindows; i++ ) { + w = &wh->window[wh->activeWindows[i]]; + if ( ( w->effects & WFX_MULTIVIEW ) && ( w->mvInfo & MV_PID ) == pID ) { + return( w ); + } + } + + return( NULL ); +} + +// Swap a window-view with the main view +void CG_mvMainviewSwap( cg_window_t *av ) { + int swap_pID = ( cg.mvCurrentMainview->mvInfo & MV_PID ); + + cg.mvCurrentMainview->mvInfo = ( cg.mvCurrentMainview->mvInfo & ~MV_PID ) | ( av->mvInfo & MV_PID ); + av->mvInfo = ( av->mvInfo & ~MV_PID ) | swap_pID; + + CG_mvOverlayUpdate(); +} + + +///////////////////////////////////////////// +// Track our list of merged clients +// +void CG_mvProcessClientList( void ) { + int i, bit, newList = cg.snap->ps.powerups[PW_MVCLIENTLIST]; + + cg.mvTotalClients = 0; + + for ( i = 0; i < MAX_MVCLIENTS; i++ ) { + bit = 1 << i; + if ( ( cg.mvClientList & bit ) != ( newList & bit ) ) { + if ( ( newList & bit ) == 0 ) { + CG_mvFree( i ); + } + // If no active views at all, start up with the first one + else if ( cg.mvCurrentMainview == NULL ) { + CG_mvCreate( i ); + } + } + + if ( newList & bit ) { + cg.mvTotalClients++; + } + } + + cg.mvClientList = newList; + CG_mvOverlayUpdate(); +} + + +// Give handle to the current selected MV window +cg_window_t *CG_mvCurrent( void ) { + int i; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + + for ( i = 0; i < wh->numActiveWindows; i++ ) { + w = &wh->window[wh->activeWindows[i]]; + if ( ( w->effects & WFX_MULTIVIEW ) && ( w->mvInfo & MV_SELECTED ) ) { + return( w ); + } + } + + return( NULL ); +} + +// Give handle to any MV window that isnt the mainview +cg_window_t *CG_mvFindNonMainview( void ) { + int i; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + + // First, find a non-windowed client + for ( i = 0; i < cg.mvTotalClients; i++ ) { + if ( cg.mvOverlay[i].w == NULL ) { + cg.mvCurrentMainview->mvInfo = ( cg.mvCurrentMainview->mvInfo & ~MV_PID ) | + ( cg.mvOverlay[i].pID & MV_PID ); + + CG_mvOverlayClientUpdate( cg.mvOverlay[i].pID, i ); + return( cg.mvCurrentMainview ); + } + } + + // If none available, pull one from a window + for ( i = 0; i < wh->numActiveWindows; i++ ) { + w = &wh->window[wh->activeWindows[i]]; + if ( ( w->effects & WFX_MULTIVIEW ) && w != cg.mvCurrentMainview ) { + CG_mvMainviewSwap( w ); + return( w ); + } + } + + return( cg.mvCurrentMainview ); +} + + + + +////////////////////////////////////////////// +// +// Rendering/Display Management +// +////////////////////////////////////////////// + + +/////////////////////////////////////////////////// +// Update all info for a merged client +// +void CG_mvUpdateClientInfo( int pID ) { + + if ( pID >= 0 && pID < MAX_MVCLIENTS && ( cg.mvClientList & ( 1 << pID ) ) ) { + int weap = cg_entities[pID].currentState.weapon; + int id = MAX_WEAPONS - 1 - ( pID * 2 ); + clientInfo_t *ci = &cgs.clientinfo[pID]; + playerState_t *ps = &cg.snap->ps; + + ci->health = ( ps->ammo[id] ) & 0xFF; + ci->hintTime = ( ps->ammo[id] >> 8 ) & 0x0F; + ci->weapHeat = ( ps->ammo[id] >> 12 ) & 0x0F; + + ci->ammo = ( ps->ammo[id - 1] ) & 0x3FF; + ci->weaponState = ( ps->ammo[id - 1] >> 11 ) & 0x03; + ci->fCrewgun = ( ps->ammo[id - 1] >> 13 ) & 0x01; + ci->cursorHint = ( ps->ammo[id - 1] >> 14 ) & 0x03; + + ci->ammoclip = ( ps->ammoclip[id - 1] ) & 0x1FF; + ci->chargeTime = ( ps->ammoclip[id - 1] >> 9 ) & 0x0F; + ci->sprintTime = ( ps->ammoclip[id - 1] >> 13 ) & 0x07; + + ci->weapHeat = (int)( 100.0f * (float)ci->weapHeat / 15.0f ); + ci->chargeTime = ( ci->chargeTime == 0 ) ? -1 : (int)( 100.0f * (float)( ci->chargeTime - 1 ) / 15.0f ); + ci->hintTime = ( ci->hintTime == 0 ) ? -1 : (int)( 100.0f * (float)( ci->hintTime - 1 ) / 15.0f ); + ci->sprintTime = ( ci->sprintTime == 0 ) ? -1 : (int)( 100.0f * (float)( ci->sprintTime - 1 ) / 7.0f ); + + if ( ci->health == 0 ) { + ci->weaponState = WSTATE_IDLE; + } + + // Handle grenade pulsing for the main view + if ( ci->weaponState != ci->weaponState_last ) { + ci->weaponState_last = ci->weaponState; + ci->grenadeTimeStart = ( ci->weaponState == WSTATE_FIRE && ( weap == WP_GRENADE_LAUNCHER || weap == WP_GRENADE_PINEAPPLE ) ) ? 4000 + cg.time : 0; + } + + if ( ci->weaponState == WSTATE_FIRE && ( weap == WP_GRENADE_LAUNCHER || weap == WP_GRENADE_PINEAPPLE ) ) { + ci->grenadeTimeLeft = ci->grenadeTimeStart - cg.time; + if ( ci->grenadeTimeLeft < 0 ) { + ci->grenadeTimeLeft = 0; + } + } else {ci->grenadeTimeLeft = 0;} + } +} + + +//////////////////////////////// +// Updates for main view +// +void CG_mvTransitionPlayerState( playerState_t* ps ) { + int x, mult, pID = ( cg.mvCurrentMainview->mvInfo & MV_PID ); + centity_t* cent = &cg_entities[pID]; + clientInfo_t *ci = &cgs.clientinfo[pID]; + + cg.predictedPlayerEntity.currentState = cent->currentState; + ps->clientNum = pID; + ps->weapon = cent->currentState.weapon; + cg.weaponSelect = ps->weapon; + + cent->currentState.eType = ET_PLAYER; + ps->eFlags = cent->currentState.eFlags; + cg.predictedPlayerState.eFlags = cent->currentState.eFlags; + cg.zoomedBinoc = ( ( cent->currentState.eFlags & EF_ZOOMING ) != 0 && ci->health > 0 ); + + x = cent->currentState.teamNum; + if ( x == PC_MEDIC ) { + mult = cg.medicChargeTime[ci->team - 1]; + } else if ( x == PC_ENGINEER ) { + mult = cg.engineerChargeTime[ci->team - 1]; + } else if ( x == PC_FIELDOPS ) { + mult = cg.ltChargeTime[ci->team - 1]; + } else if ( x == PC_COVERTOPS ) { + mult = cg.covertopsChargeTime[ci->team - 1]; + } else { mult = cg.soldierChargeTime[ci->team - 1];} + + ps->curWeapHeat = (int)( (float)ci->weapHeat * 255.0f / 100.0f ); + ps->classWeaponTime = ( ci->chargeTime < 0 ) ? -1 : cg.time - (int)( (float)( mult * ci->chargeTime ) / 100.0f ); + + // FIXME: moved to pmext +// ps->sprintTime = (ci->sprintTime < 0) ? 20000 : (int)((float)ci->sprintTime / 100.0f * 20000.0f); + + ps->serverCursorHintVal = ( ci->hintTime < 0 ) ? 0 : ci->hintTime * 255 / 100; + ps->serverCursorHint = BG_simpleHintsExpand( ci->cursorHint, ( ( x == 2 ) ? ci->hintTime : -1 ) ); + + ps->stats[STAT_HEALTH] = ci->health; + ps->stats[STAT_PLAYER_CLASS] = x; + + // Grenade ticks + ps->grenadeTimeLeft = ci->grenadeTimeLeft; + + // Safe as we've already pull data before clobbering + ps->ammo[BG_FindAmmoForWeapon( ps->weapon )] = ci->ammo; + ps->ammoclip[BG_FindClipForWeapon( ps->weapon )] = ci->ammoclip; + + ps->persistant[PERS_SCORE] = ci->score; + ps->persistant[PERS_TEAM] = ci->team; + + VectorCopy( cent->lerpOrigin, ps->origin ); + VectorCopy( cent->lerpAngles, ps->viewangles ); +} + +void CG_OffsetThirdPersonView( void ); + +/////////////////////////////////// +// Draw the client view window +// +void CG_mvDraw( cg_window_t *sw ) { + int pID = ( sw->mvInfo & MV_PID ); + int x, base_pID = cg.snap->ps.clientNum; + refdef_t refdef; + float rd_x, rd_y, rd_w, rd_h; + float b_x, b_y, b_w, b_h; + float s = 1.0f; + centity_t *cent = &cg_entities[pID]; + + + memset( &refdef, 0, sizeof( refdef_t ) ); + memcpy( refdef.areamask, cg.snap->areamask, sizeof( refdef.areamask ) ); + + CG_mvUpdateClientInfo( pID ); + cg.snap->ps.clientNum = pID; + + rd_x = sw->x; + rd_y = sw->y; + rd_w = sw->w; + rd_h = sw->h; + + // Cool zoomin/out effect + if ( sw->state != WSTATE_COMPLETE ) { + int tmp = trap_Milliseconds() - sw->time; + + if ( sw->state == WSTATE_START ) { + if ( tmp < sw->targetTime ) { + s = (float)tmp / (float)sw->targetTime; + rd_x += rd_w * 0.5f * ( 1.0f - s ); + rd_y += rd_h * 0.5f * ( 1.0f - s ); + rd_w *= s; + rd_h *= s; + } else { + sw->state = WSTATE_COMPLETE; + } + } else if ( sw->state == WSTATE_SHUTDOWN ) { + if ( tmp < sw->targetTime ) { + s = (float)tmp / (float)sw->targetTime; + rd_x += rd_w * 0.5f * s; + rd_y += rd_h * 0.5f * s; + s = 1.0f - s; + rd_w *= s; + rd_h *= s; + if ( sw == cg.mvCurrentMainview ) { + trap_R_ClearScene(); + } + } else { + // Main window is shutting down. + // Try to swap it with another MV client, if available + if ( sw == cg.mvCurrentMainview ) { + sw = CG_mvFindNonMainview(); + if ( cg.mvTotalClients > 0 ) { + cg.mvCurrentMainview->targetTime = 100; + cg.mvCurrentMainview->time = trap_Milliseconds(); + cg.mvCurrentMainview->state = WSTATE_START; + } + + // If we swap with a window, hang around so we can delete the window + // Otherwise, if there are still active MV clients, don't close the mainview + if ( sw == cg.mvCurrentMainview && cg.mvTotalClients > 0 ) { + return; + } + } + + CG_windowFree( sw ); + + // We were on the last viewed client when the shutdown was issued, + // go ahead and shut down the mainview window *sniff* + if ( --cg.mv_cnt <= 0 ) { + cg.mv_cnt = 0; + cg.mvCurrentMainview = NULL; + + if ( cg.spechelpWindow == SHOW_ON ) { + CG_ShowHelp_Off( &cg.spechelpWindow ); + } + } + + CG_mvOverlayUpdate(); + return; + } + } + } + + b_x = rd_x; + b_y = rd_y; + b_w = rd_w; + b_h = rd_h; + + CG_AdjustFrom640( &rd_x, &rd_y, &rd_w, &rd_h ); + + refdef.x = rd_x; + refdef.y = rd_y; + refdef.width = rd_w; + refdef.height = rd_h; + + refdef.fov_x = ( cgs.clientinfo[pID].health > 0 && + ( /*cent->currentState.weapon == WP_SNIPERRIFLE ||*/ // ARNOUT: this needs updating? + ( cent->currentState.eFlags & EF_ZOOMING ) ) ) ? + cg_zoomDefaultSniper.value : + ( cgs.clientinfo[pID].fCrewgun ) ? + 55 : cg_fov.integer; + + x = refdef.width / tan( refdef.fov_x / 360 * M_PI ); + refdef.fov_y = atan2( refdef.height, x ) * 360 / M_PI; + + refdef.rdflags = cg.refdef.rdflags; + refdef.time = cg.time; + + AnglesToAxis( cent->lerpAngles, refdef.viewaxis ); + VectorCopy( cent->lerpOrigin, refdef.vieworg ); + VectorCopy( cent->lerpAngles, cg.refdefViewAngles ); + + cg.refdef_current = &refdef; + + trap_R_ClearScene(); + + if ( sw == cg.mvCurrentMainview && cg.renderingThirdPerson ) { + cg.renderingThirdPerson = qtrue; +// VectorCopy(cent->lerpOrigin, refdef.vieworg); + CG_OffsetThirdPersonView(); + AnglesToAxis( cg.refdefViewAngles, refdef.viewaxis ); + } else { + cg.renderingThirdPerson = qfalse; + refdef.vieworg[2] += ( cent->currentState.eFlags & EF_CROUCHING ) ? CROUCH_VIEWHEIGHT : DEFAULT_VIEWHEIGHT; + } + + CG_SetupFrustum(); + CG_DrawSkyBoxPortal( qfalse ); + + if ( !cg.hyperspace ) { + CG_AddPacketEntities(); + CG_AddMarks(); + CG_AddParticles(); + CG_AddLocalEntities(); + + CG_AddSmokeSprites(); + CG_AddAtmosphericEffects(); + + CG_AddFlameChunks(); + CG_AddTrails(); // this must come last, so the trails dropped this frame get drawn + } + + if ( sw == cg.mvCurrentMainview ) { + CG_DrawActive( STEREO_CENTER ); + if ( cg.mvCurrentActive == cg.mvCurrentMainview ) { + trap_S_Respatialize( cg.clientNum, refdef.vieworg, refdef.viewaxis, qfalse ); + } + + cg.snap->ps.clientNum = base_pID; + cg.refdef_current = &cg.refdef; + cg.renderingThirdPerson = qfalse; + return; + } + + memcpy( refdef.areamask, cg.snap->areamask, sizeof( refdef.areamask ) ); + refdef.time = cg.time; + trap_SetClientLerpOrigin( refdef.vieworg[0], refdef.vieworg[1], refdef.vieworg[2] ); + + trap_R_RenderScene( &refdef ); + + cg.refdef_current = &cg.refdef; + + +#if 0 + cg.refdef_current = &refdef; + CG_DrawStringExt( 1, 1, ci->name, colorWhite, qtrue, qtrue, 8, 8, 0 ); + cg.refdef_current = &cg.refdef; +#endif + + CG_mvWindowOverlay( pID, b_x, b_y, b_w, b_h, s, sw->state, ( sw == cg.mvCurrentActive ) ); + if ( sw == cg.mvCurrentActive ) { + trap_S_Respatialize( cg.clientNum, refdef.vieworg, refdef.viewaxis, qfalse ); + } + + cg.snap->ps.clientNum = base_pID; +} + + +//////////////////////////////////////////// +// Simpler overlay for windows +// +void CG_mvWindowOverlay( int pID, float b_x, float b_y, float b_w, float b_h, float s, int wState, qboolean fSelected ) { + int x; + rectDef_t rect; + float fw = 8.0f, fh = 8.0f; + centity_t *cent = &cg_entities[pID]; + clientInfo_t *ci = &cgs.clientinfo[pID]; + const char *p_class = "?"; + vec4_t *noSelectBorder = &colorDkGrey; + + + // Overlays for zoomed views + if ( ci->health > 0 ) { + /*if(cent->currentState.weapon == WP_SNIPERRIFLE) CG_mvZoomSniper(b_x, b_y, b_w, b_h); // ARNOUT: this needs updating? + else */if ( cent->currentState.eFlags & EF_ZOOMING ) { + CG_mvZoomBinoc( b_x, b_y, b_w, b_h ); + } + } + + // Text info + fw *= s; + fh *= s; + x = cent->currentState.teamNum; + if ( x == PC_SOLDIER ) { + p_class = "^1S"; noSelectBorder = &colorMdRed; + } else if ( x == PC_MEDIC ) { + p_class = "^7M"; noSelectBorder = &colorMdGrey; + } else if ( x == PC_ENGINEER ) { + p_class = "^5E"; noSelectBorder = &colorMdBlue; + } else if ( x == PC_FIELDOPS ) { + p_class = "^2F"; noSelectBorder = &colorMdGreen; + } else if ( x == PC_COVERTOPS ) { + p_class = "^3C"; noSelectBorder = &colorMdYellow; + } + + CG_DrawStringExt( b_x + 1, b_y + b_h - ( fh * 2 + 1 + 2 ), ci->name, colorWhite, qfalse, qtrue, fw, fh, 0 ); + CG_DrawStringExt( b_x + 1, b_y + b_h - ( fh + 2 ), va( "%s^7%d", CG_TranslateString( p_class ), ci->health ), colorWhite, qfalse, qtrue, fw, fh, 0 ); + + p_class = va( "%d^1/^7%d", ci->ammoclip, ci->ammo ); + x = CG_DrawStrlen( p_class ); + CG_DrawStringExt( b_x + b_w - ( x * fw + 1 ), b_y + b_h - ( fh + 2 ), p_class, colorWhite, qfalse, qtrue, fw, fh, 0 ); + + + // Weapon icon + rect.x = b_x + b_w - ( 50 + 1 ); + rect.y = b_y + b_h - ( 25 + fh + 1 + 2 ); + rect.w = 50; + rect.h = 25; + cg.predictedPlayerState.grenadeTimeLeft = 0; + cg.predictedPlayerState.weapon = cent->currentState.weapon; + CG_DrawPlayerWeaponIcon( &rect, ( ci->weaponState > WSTATE_IDLE ), ITEM_ALIGN_RIGHT, + ( ( ci->weaponState == WSTATE_SWITCH ) ? &colorWhite : + ( ci->weaponState == WSTATE_FIRE ) ? &colorRed : + &colorYellow ) ); + + // Sprint charge info + if ( ci->sprintTime >= 0 ) { + p_class = va( "^2S^7%d%%", ci->sprintTime ); + rect.y -= ( fh + 1 ); + x = CG_DrawStrlen( p_class ); + CG_DrawStringExt( b_x + b_w - ( x * fw + 1 ), rect.y, p_class, colorWhite, qfalse, qtrue, fw, fh, 0 ); + } + + // Weapon charge info + if ( ci->chargeTime >= 0 ) { + p_class = va( "^1C^7%d%%", ci->chargeTime ); + rect.y -= ( fh + 1 ); + x = CG_DrawStrlen( p_class ); + CG_DrawStringExt( b_x + b_w - ( x * fw + 1 ), rect.y, p_class, colorWhite, qfalse, qtrue, fw, fh, 0 ); + } + + // Cursorhint work + if ( ci->hintTime >= 0 ) { + p_class = va( "^3W:^7%d%%", ci->hintTime ); + rect.y -= ( fh + 1 ); + x = CG_DrawStrlen( p_class ); + CG_DrawStringExt( b_x + ( b_w - ( x * ( fw - 1 ) ) ) / 2, b_y + b_h - ( fh + 2 ), p_class, colorWhite, qfalse, qtrue, fw - 1, fh - 1, 0 ); + } + + // Finally, the window border + if ( fSelected && wState == WSTATE_COMPLETE ) { + int t = trap_Milliseconds() & 0x07FF; // 2047 + float scale; + vec4_t borderColor; + + if ( t > 1024 ) { + t = 2047 - t; + } + memcpy( &borderColor, *noSelectBorder, sizeof( vec4_t ) ); + scale = ( (float)t / 1137.38f ) + 0.5f; + if ( scale <= 1.0 ) { + borderColor[0] *= scale; + borderColor[1] *= scale; + borderColor[2] *= scale; + } else { + scale -= 1.0; + borderColor[0] = ( borderColor[0] + scale > 1.0 ) ? 1.0 : borderColor[0] + scale; + borderColor[1] = ( borderColor[1] + scale > 1.0 ) ? 1.0 : borderColor[1] + scale; + borderColor[2] = ( borderColor[2] + scale > 1.0 ) ? 1.0 : borderColor[2] + scale; + } + CG_DrawRect( b_x - 1, b_y - 1, b_w + 2, b_h + 2, 2, borderColor ); + } else { + CG_DrawRect( b_x - 1, b_y - 1, b_w + 2, b_h + 2, 2, *noSelectBorder ); + } +} + + + + +//////////////////////////////////////////////////// +// +// MV Text Overlay Handling +// +//////////////////////////////////////////////////// + +char *strClassHighlights[] = +{ + S_COLOR_RED, S_COLOR_MDRED, // Soldier + S_COLOR_WHITE, S_COLOR_MDGREY, // Medic + S_COLOR_BLUE, S_COLOR_MDBLUE, // Engineer + S_COLOR_GREEN, S_COLOR_MDGREEN, // Lt. + S_COLOR_YELLOW, S_COLOR_MDYELLOW // CovertOps +}; + + +// Update a particular client's info +void CG_mvOverlayClientUpdate( int pID, int index ) { + cg_window_t *w; + + cg.mvOverlay[index].pID = pID; + cg.mvOverlay[index].classID = cg_entities[pID].currentState.teamNum; + w = CG_mvClientLocate( pID ); + cg.mvOverlay[index].w = w; + if ( w != NULL ) { + strcpy( cg.mvOverlay[index].info, va( "%s%s%2d", + strClassHighlights[cg.mvOverlay[index].classID * 2], + ( w == cg.mvCurrentMainview ) ? "*" : "", + pID ) + ); + } else { + strcpy( cg.mvOverlay[index].info, va( "%s%2d", + strClassHighlights[( cg.mvOverlay[index].classID * 2 ) + 1], + pID ) + ); + } + + cg.mvOverlay[index].width = CG_DrawStrlen( cg.mvOverlay[index].info ) * MVINFO_TEXTSIZE; +} + +// Update info on all clients received for display/cursor interaction +void CG_mvOverlayUpdate( void ) { + int i, cnt; + + for ( i = 0, cnt = 0; i < MAX_MVCLIENTS && cnt < cg.mvTotalClients; i++ ) { + if ( cg.mvClientList & ( 1 << i ) ) { + CG_mvOverlayClientUpdate( i, cnt++ ); + } + } +} + +// See if we have the client in our snapshot +qboolean CG_mvMergedClientLocate( int pID ) { + int i; + + for ( i = 0; i < cg.mvTotalClients; i++ ) { + if ( cg.mvOverlay[i].pID == pID ) { + return( qtrue ); + } + } + + return( qfalse ); +} + +// Display available client info +void CG_mvOverlayDisplay( void ) { + int j, i, x, y, pID; + cg_mvinfo_t *o; + + + if ( cg.mvTotalClients < 1 ) { + return; + } + + y = MVINFO_TOP - ( 2 * ( MVINFO_TEXTSIZE + 1 ) ); + + for ( j = TEAM_AXIS; j <= TEAM_ALLIES; j++ ) { + cg.mvTotalTeam[j] = 0; + for ( i = 0; i < cg.mvTotalClients; i++ ) { + o = &cg.mvOverlay[i]; + pID = o->pID; + + if ( cgs.clientinfo[pID].team != j ) { + continue; + } + + if ( cg.mvTotalTeam[j] == 0 ) { + char *flag = ( j == TEAM_AXIS ) ? "ui/assets/ger_flag.tga" : "ui/assets/usa_flag.tga"; + y += 2 * ( MVINFO_TEXTSIZE + 1 ); + CG_DrawPic( MVINFO_RIGHT - ( 2 * MVINFO_TEXTSIZE ), y, 2 * MVINFO_TEXTSIZE, MVINFO_TEXTSIZE, trap_R_RegisterShaderNoMip( flag ) ); + } + + // Update team list info for mouse detection + cg.mvTeamList[j][( cg.mvTotalTeam[j] )] = i; + cg.mvTotalTeam[j]++; + + // Update any class changes + if ( o->classID != cg_entities[pID].currentState.teamNum ) { + CG_mvOverlayClientUpdate( o->pID, i ); + } + + x = MVINFO_RIGHT - o->width; + y += MVINFO_TEXTSIZE + 1; + + if ( o->fActive ) { + CG_FillRect( x - 1, y, o->width + 2, MVINFO_TEXTSIZE + 2, colorMdYellow ); + + // Draw name info only if we're hovering over the text element + if ( !( cg.mvCurrentActive->mvInfo & MV_SELECTED ) || cg.mvCurrentActive == cg.mvCurrentMainview ) { + int w = CG_DrawStrlen( cgs.clientinfo[pID].name ) * ( MVINFO_TEXTSIZE - 1 ); + + CG_FillRect( x - 1 - w - 6, y + 1, w + 2, MVINFO_TEXTSIZE - 1 + 2, colorMdGrey ); + CG_DrawStringExt( x - w - 6, y + 1, + cgs.clientinfo[pID].name, + colorYellow, qtrue, qtrue, + MVINFO_TEXTSIZE - 1, + MVINFO_TEXTSIZE - 1, 0 ); + } + } + + CG_DrawStringExt( x, y, o->info, + colorWhite, qfalse, qtrue, + MVINFO_TEXTSIZE, + MVINFO_TEXTSIZE, 0 ); + } + } +} + + + +////////////////////////////////////// +// +// Wolf-specific utilities +// +////////////////////////////////////// +void CG_mvZoomSniper( float x, float y, float w, float h ) { + float ws = w / 640; + float hs = h / 480; + + // sides + CG_FillRect( x, y, 80.0f * ws, 480.0f * hs, colorBlack ); + CG_FillRect( x + 560.0f * ws, y, 80.0f * ws, 480.0f * hs, colorBlack ); + + // center + if ( cgs.media.reticleShaderSimple ) { + CG_DrawPic( x + 80.0f * ws, y, 480.0f * ws, 480.0f * hs, cgs.media.reticleShaderSimple ); + } + + // hairs + CG_FillRect( x + 84.0f * ws, y + 239.0f * hs, 177.0f * ws, 2.0f, colorBlack ); // left + CG_FillRect( x + 320.0f * ws, y + 242.0f * hs, 1.0f, 58.0f * hs, colorBlack ); // center top + CG_FillRect( x + 319.0f * ws, y + 300.0f * hs, 2.0f, 178.0f * hs, colorBlack ); // center bot + CG_FillRect( x + 380.0f * ws, y + 239.0f * hs, 177.0f * ws, 2.0f, colorBlack ); // right +} + + +void CG_mvZoomBinoc( float x, float y, float w, float h ) { + float ws = w / 640; + float hs = h / 480; + + // an alternative. This gives nice sharp lines at the expense of a few extra polys + if ( cgs.media.binocShaderSimple ) { + CG_DrawPic( x, y, 640.0f * ws, 480.0f * ws, cgs.media.binocShaderSimple ); + } + + CG_FillRect( x + 146.0f * ws, y + 239.0f * hs, 348.0f * ws, 1, colorBlack ); + + CG_FillRect( x + 188.0f * ws, y + 234.0f * hs, 1, 13.0f * hs, colorBlack ); // ll + CG_FillRect( x + 234.0f * ws, y + 226.0f * hs, 1, 29.0f * hs, colorBlack ); // l + CG_FillRect( x + 274.0f * ws, y + 234.0f * hs, 1, 13.0f * hs, colorBlack ); // lr + CG_FillRect( x + 320.0f * ws, y + 213.0f * hs, 1, 55.0f * hs, colorBlack ); // center + CG_FillRect( x + 360.0f * ws, y + 234.0f * hs, 1, 13.0f * hs, colorBlack ); // rl + CG_FillRect( x + 406.0f * ws, y + 226.0f * hs, 1, 29.0f * hs, colorBlack ); // r + CG_FillRect( x + 452.0f * ws, y + 234.0f * hs, 1, 13.0f * hs, colorBlack ); // rr +} diff --git a/src/cgame/cg_newDraw.c b/src/cgame/cg_newDraw.c new file mode 100644 index 0000000..608deb0 --- /dev/null +++ b/src/cgame/cg_newDraw.c @@ -0,0 +1,941 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "cg_local.h" + +int CG_DrawField( int x, int y, int width, int value, int charWidth, int charHeight, qboolean dodrawpic, qboolean leftAlign ); // NERVE - SMF + +/*void CG_FitTextToWidth( char* instr, int w, int size) { + char buffer[1024]; + char *s, *p, *c, *ls; + int l; + + strcpy(buffer, instr); + memset(instr, 0, size); + + c = s = instr; + p = buffer; + ls = NULL; + l = 0; + while(*p) { + *c = *p++; + l++; + + if(*c == ' ') { + ls = c; + } // store last space, to try not to break mid word + + c++; + + if(*p == '\n') { + s = c+1; + l = 0; + } else if(l > w) { + if(ls) { + *ls = '\n'; + l = strlen(ls+1); + } else { + *c = *(c-1); + *(c-1) = '\n'; + s = c++; + l = 0; + } + + ls = NULL; + } + } + + if(c != buffer && (*(c-1) != '\n')) { + *c++ = '\n'; + } + + *c = '\0'; +}*/ + +int CG_TrimLeftPixels( char* instr, float scale, float w, int size ) { + char buffer[1024]; + char *p, *s; + int tw; + int i; + + Q_strncpyz( buffer, instr, 1024 ); + memset( instr, 0, size ); + + for ( i = 0, p = buffer; *p; p++, i++ ) { + instr[i] = *p; + tw = CG_Text_Width( instr, scale, 0 ); + if ( tw >= w ) { + memset( instr, 0, size ); + for ( s = instr, p = &buffer[i + 1]; *p && ( ( s - instr ) < size ); p++, s++ ) { + *s = *p; + } + return tw - w; + } + } + + return -1; +} + + +void CG_FitTextToWidth_Ext( char* instr, float scale, float w, int size, fontInfo_t* font ) { + char buffer[1024]; + char *s, *p, *c, *ls; + int l; + + Q_strncpyz( buffer, instr, 1024 ); + memset( instr, 0, size ); + + c = s = instr; + p = buffer; + ls = NULL; + l = 0; + while ( *p ) { + *c = *p++; + l++; + + if ( *c == ' ' ) { + ls = c; + } // store last space, to try not to break mid word + + c++; + + if ( *p == '\n' ) { + s = c + 1; + l = 0; + } else if ( CG_Text_Width_Ext( s, scale, 0, font ) > w ) { + if ( ls ) { + *ls = '\n'; + s = ls + 1; + } else { + *c = *( c - 1 ); + *( c - 1 ) = '\n'; + s = c++; + } + + ls = NULL; + l = 0; + } + } + + if ( c != buffer && ( *( c - 1 ) != '\n' ) ) { + *c++ = '\n'; + } + + *c = '\0'; +} + +void CG_FitTextToWidth2( char* instr, float scale, float w, int size ) { + char buffer[1024]; + char *s, *p, *c, *ls; + int l; + + Q_strncpyz( buffer, instr, 1024 ); + memset( instr, 0, size ); + + c = s = instr; + p = buffer; + ls = NULL; + l = 0; + while ( *p ) { + *c = *p++; + l++; + + if ( *c == ' ' ) { + ls = c; + } // store last space, to try not to break mid word + + c++; + + if ( *p == '\n' ) { + s = c + 1; + l = 0; + } else if ( CG_Text_Width( s, scale, 0 ) > w ) { + if ( ls ) { + *ls = '\n'; + s = ls + 1; + } else { + *c = *( c - 1 ); + *( c - 1 ) = '\n'; + s = c++; + } + + ls = NULL; + l = 0; + } + } + + if ( c != buffer && ( *( c - 1 ) != '\n' ) ) { + *c++ = '\n'; + } + + *c = '\0'; +} + +void CG_FitTextToWidth_SingleLine( char* instr, float scale, float w, int size ) { + char *s, *p; + char buffer[1024]; + + Q_strncpyz( buffer, instr, 1024 ); + memset( instr, 0, size ); + p = instr; + + for ( s = buffer; *s; s++, p++ ) { + *p = *s; + if ( CG_Text_Width( instr, scale, 0 ) > w ) { + *p = '\0'; + return; + } + } +} + +/* +============== +weapIconDrawSize +============== +*/ +static int weapIconDrawSize( int weap ) { + switch ( weap ) { + + // weapons to not draw +// case WP_KNIFE: +// return 0; + + // weapons with 'wide' icons + case WP_THOMPSON: + case WP_MP40: + case WP_STEN: + case WP_PANZERFAUST: + case WP_FLAMETHROWER: +// case WP_SPEARGUN: + case WP_GARAND: + case WP_FG42: + case WP_FG42SCOPE: + case WP_KAR98: + case WP_GPG40: + case WP_CARBINE: + case WP_M7: + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + case WP_K43: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + case WP_MORTAR: + case WP_MORTAR_SET: + return 2; + } + + return 1; +} + + +/* +============== +CG_DrawPlayerWeaponIcon +============== +*/ +void CG_DrawPlayerWeaponIcon( rectDef_t *rect, qboolean drawHighlighted, int align, vec4_t *refcolor ) { + int size; + int realweap; // DHM - Nerve + qhandle_t icon; + float scale,halfScale; + vec4_t hcolor; + + VectorCopy( *refcolor, hcolor ); + hcolor[3] = 1.f; + + if ( cg.predictedPlayerEntity.currentState.eFlags & EF_MG42_ACTIVE || + cg.predictedPlayerEntity.currentState.eFlags & EF_MOUNTEDTANK ) { + realweap = WP_MOBILE_MG42; + } else { + realweap = cg.predictedPlayerState.weapon; + } + + size = weapIconDrawSize( realweap ); + + if ( !size ) { + return; + } + + if ( cg.predictedPlayerEntity.currentState.eFlags & EF_MOUNTEDTANK && cg_entities[cg_entities[ cg_entities[ cg.snap->ps.clientNum ].tagParent ].tankparent].currentState.density & 8 ) { + icon = cgs.media.browningIcon; + } else { + if ( drawHighlighted ) { + //icon = cg_weapons[ realweap ].weaponIcon[1]; + icon = cg_weapons[ realweap ].weaponIcon[1]; // we don't have icon[0]; + } else { + icon = cg_weapons[ realweap ].weaponIcon[1]; + } + } + + + + // pulsing grenade icon to help the player 'count' in their head + if ( cg.predictedPlayerState.grenadeTimeLeft ) { // grenades and dynamite set this + + // these time differently + if ( realweap == WP_DYNAMITE ) { + if ( ( ( cg.grenLastTime ) % 1000 ) > ( ( cg.predictedPlayerState.grenadeTimeLeft ) % 1000 ) ) { + trap_S_StartLocalSound( cgs.media.grenadePulseSound4, CHAN_LOCAL_SOUND ); + } + } else { + if ( ( ( cg.grenLastTime ) % 1000 ) < ( ( cg.predictedPlayerState.grenadeTimeLeft ) % 1000 ) ) { + switch ( cg.predictedPlayerState.grenadeTimeLeft / 1000 ) { + case 3: + trap_S_StartLocalSound( cgs.media.grenadePulseSound4, CHAN_LOCAL_SOUND ); + break; + case 2: + trap_S_StartLocalSound( cgs.media.grenadePulseSound3, CHAN_LOCAL_SOUND ); + break; + case 1: + trap_S_StartLocalSound( cgs.media.grenadePulseSound2, CHAN_LOCAL_SOUND ); + break; + case 0: + trap_S_StartLocalSound( cgs.media.grenadePulseSound1, CHAN_LOCAL_SOUND ); + break; + } + } + } + + scale = (float)( ( cg.predictedPlayerState.grenadeTimeLeft ) % 1000 ) / 100.0f; + halfScale = scale * 0.5f; + + cg.grenLastTime = cg.predictedPlayerState.grenadeTimeLeft; + } else { + scale = halfScale = 0; + } + + + if ( icon ) { + float x, y, w, h; + + if ( size == 1 ) { // draw half width to match the icon asset + + // start at left + x = rect->x - halfScale; + y = rect->y - halfScale; + w = rect->w / 2 + scale; + h = rect->h + scale; + + switch ( align ) { + case ITEM_ALIGN_CENTER: + x += rect->w / 4; + break; + case ITEM_ALIGN_RIGHT: + x += rect->w / 2; + break; + case ITEM_ALIGN_LEFT: + default: + break; + } + + } else { + x = rect->x - halfScale; + y = rect->y - halfScale; + w = rect->w + scale; + h = rect->h + scale; + } + + + trap_R_SetColor( hcolor ); // JPW NERVE + CG_DrawPic( x, y, w, h, icon ); + } +} + +#define CURSORHINT_SCALE 10 + +/* +============== +CG_DrawCursorHints + + cg_cursorHints.integer == + 0: no hints + 1: sin size pulse + 2: one way size pulse + 3: alpha pulse + 4+: static image + +============== +*/ +void CG_DrawCursorhint( rectDef_t *rect ) { + float *color; + qhandle_t icon, icon2 = 0; + float scale, halfscale; + //qboolean redbar = qfalse; + qboolean yellowbar = qfalse; + + if ( !cg_cursorHints.integer ) { + return; + } + + CG_CheckForCursorHints(); + + switch ( cg.cursorHintIcon ) { + + case HINT_NONE: + case HINT_FORCENONE: + icon = 0; + break; + case HINT_DOOR: + icon = cgs.media.doorHintShader; + break; + case HINT_DOOR_ROTATING: + icon = cgs.media.doorRotateHintShader; + break; + case HINT_DOOR_LOCKED: + icon = cgs.media.doorLockHintShader; + break; + case HINT_DOOR_ROTATING_LOCKED: + icon = cgs.media.doorRotateLockHintShader; + break; + case HINT_MG42: + icon = cgs.media.mg42HintShader; + break; + case HINT_BREAKABLE: + icon = cgs.media.breakableHintShader; + break; + case HINT_BREAKABLE_DYNAMITE: + icon = cgs.media.dynamiteHintShader; + break; + case HINT_TANK: + icon = cgs.media.tankHintShader; + break; + case HINT_SATCHELCHARGE: + icon = cgs.media.satchelchargeHintShader; + break; + case HINT_CONSTRUCTIBLE: + icon = cgs.media.buildHintShader; + break; + case HINT_UNIFORM: + icon = cgs.media.uniformHintShader; + break; + case HINT_LANDMINE: + icon = cgs.media.landmineHintShader; + break; + case HINT_CHAIR: + icon = cgs.media.notUsableHintShader; + + // only show 'pickupable' if you're not armed, or are armed with a single handed weapon + + // rain - WEAPS_ONE_HANDED isn't valid anymore, because + // WP_SILENCED_COLT uses a bit >31 (and, therefore, is too large + // to be shifted in the way WEAPS_ONE_HANDED does on a 32-bit + // system.) If you want to use HINT_CHAIR, you'll need to fix + // this. +#if 0 + if ( !( cg.predictedPlayerState.weapon ) || + WEAPS_ONE_HANDED & ( 1 << ( cg.predictedPlayerState.weapon ) ) + ) { + icon = cgs.media.chairHintShader; + } +#endif + break; + case HINT_ALARM: + icon = cgs.media.alarmHintShader; + break; + case HINT_HEALTH: + icon = cgs.media.healthHintShader; + break; + case HINT_TREASURE: + icon = cgs.media.treasureHintShader; + break; + case HINT_KNIFE: + icon = cgs.media.knifeHintShader; + break; + case HINT_LADDER: + icon = cgs.media.ladderHintShader; + break; + case HINT_BUTTON: + icon = cgs.media.buttonHintShader; + break; + case HINT_WATER: + icon = cgs.media.waterHintShader; + break; + case HINT_CAUTION: + icon = cgs.media.cautionHintShader; + break; + case HINT_DANGER: + icon = cgs.media.dangerHintShader; + break; + case HINT_SECRET: + icon = cgs.media.secretHintShader; + break; + case HINT_QUESTION: + icon = cgs.media.qeustionHintShader; + break; + case HINT_EXCLAMATION: + icon = cgs.media.exclamationHintShader; + break; + case HINT_CLIPBOARD: + icon = cgs.media.clipboardHintShader; + break; + case HINT_WEAPON: + icon = cgs.media.weaponHintShader; + break; + case HINT_AMMO: + icon = cgs.media.ammoHintShader; + break; + case HINT_ARMOR: + icon = cgs.media.armorHintShader; + break; + case HINT_POWERUP: + icon = cgs.media.powerupHintShader; + break; + case HINT_HOLDABLE: + icon = cgs.media.holdableHintShader; + break; + case HINT_INVENTORY: + icon = cgs.media.inventoryHintShader; + break; + case HINT_PLYR_FRIEND: + icon = cgs.media.hintPlrFriendShader; + break; + case HINT_PLYR_NEUTRAL: + icon = cgs.media.hintPlrNeutralShader; + break; + case HINT_PLYR_ENEMY: + icon = cgs.media.hintPlrEnemyShader; + break; + case HINT_PLYR_UNKNOWN: + icon = cgs.media.hintPlrUnknownShader; + break; + + // DHM - Nerve :: multiplayer hints + case HINT_BUILD: + icon = cgs.media.buildHintShader; + break; + case HINT_DISARM: + icon = cgs.media.disarmHintShader; + break; + case HINT_REVIVE: + icon = cgs.media.reviveHintShader; + break; + case HINT_DYNAMITE: + icon = cgs.media.dynamiteHintShader; + break; + // dhm - end + + // Mad Doc - TDF + case HINT_LOCKPICK: + icon = cgs.media.doorLockHintShader; // TAT 1/30/2003 - use the locked door hint cursor + yellowbar = qtrue; // draw the status bar in yellow so it shows up better + break; + + case HINT_ACTIVATE: + case HINT_PLAYER: + default: + icon = cgs.media.usableHintShader; + break; + } + + + if ( !icon ) { + return; + } + + + // color + color = CG_FadeColor( cg.cursorHintTime, cg.cursorHintFade ); + if ( !color ) { + trap_R_SetColor( NULL ); + return; + } + + if ( cg_cursorHints.integer == 3 ) { + color[3] *= 0.5 + 0.5 * sin( (float)cg.time / 150.0 ); + } + + + // size + if ( cg_cursorHints.integer >= 3 ) { // no size pulsing + scale = halfscale = 0; + } else { + if ( cg_cursorHints.integer == 2 ) { + scale = (float)( ( cg.cursorHintTime ) % 1000 ) / 100.0f; // one way size pulse + } else { + scale = CURSORHINT_SCALE * ( 0.5 + 0.5 * sin( (float)cg.time / 150.0 ) ); // sin pulse + + } + halfscale = scale * 0.5f; + } + + // set color and draw the hint + trap_R_SetColor( color ); + CG_DrawPic( rect->x - halfscale, rect->y - halfscale, rect->w + scale, rect->h + scale, icon ); + + if ( icon2 ) { + CG_DrawPic( rect->x - halfscale, rect->y - halfscale, rect->w + scale, rect->h + scale, icon2 ); + } + + trap_R_SetColor( NULL ); + + // draw status bar under the cursor hint + if ( cg.cursorHintValue ) { + if ( yellowbar ) { + Vector4Set( color, 1, 1, 0, 1.0f ); + } else { + Vector4Set( color, 0, 0, 1, 0.5f ); + } + CG_FilledBar( rect->x, rect->y + rect->h + 4, rect->w, 8, color, NULL, NULL, (float)cg.cursorHintValue / 255.0f, 0 ); + } + +} + +float CG_GetValue( int ownerDraw, int type ) { + switch ( ownerDraw ) { + default: + break; + } + return -1; +} + +qboolean CG_OtherTeamHasFlag() { + return qfalse; +} + +qboolean CG_YourTeamHasFlag() { + return qfalse; +} + +// THINKABOUTME: should these be exclusive or inclusive.. +// +qboolean CG_OwnerDrawVisible( int flags ) { + return qfalse; +} + +#define PIC_WIDTH 12 + +/* +============== +CG_DrawWeapStability + draw a bar showing current stability level (0-255), max at current weapon/ability, and 'perfect' reference mark + + probably only drawn for scoped weapons +============== +*/ +void CG_DrawWeapStability( rectDef_t *rect ) { + vec4_t goodColor = {0, 1, 0, 0.5f}, badColor = {1, 0, 0, 0.5f}; + + if ( !cg_drawSpreadScale.integer ) { + return; + } + + if ( cg_drawSpreadScale.integer == 1 && !BG_IsScopedWeapon( cg.predictedPlayerState.weapon ) ) { + // cg_drawSpreadScale of '1' means only draw for scoped weapons, '2' means draw all the time (for debugging) + return; + } + + if ( cg.predictedPlayerState.weaponstate != WEAPON_READY ) { + return; + } + + if ( !( cg.snap->ps.aimSpreadScale ) ) { + return; + } + + if ( cg.renderingThirdPerson ) { + return; + } + + CG_FilledBar( rect->x, rect->y, rect->w, rect->h, goodColor, badColor, NULL, (float)cg.snap->ps.aimSpreadScale / 255.0f, 2 | 4 | 256 ); // flags (BAR_CENTER|BAR_VERT|BAR_LERP_COLOR) +} + + +/* +============== +CG_DrawWeapHeat +============== +*/ +void CG_DrawWeapHeat( rectDef_t *rect, int align ) { + vec4_t color = {1, 0, 0, 0.2f}, color2 = {1, 0, 0, 0.5f}; + int flags = 0; + + if ( !( cg.snap->ps.curWeapHeat ) ) { + return; + } + + if ( align != HUD_HORIZONTAL ) { + flags |= 4; // BAR_VERT + } + + flags |= 1; // BAR_LEFT - this is hardcoded now, but will be decided by the menu script + flags |= 16; // BAR_BG - draw the filled contrast box +// flags|=32; // BAR_BGSPACING_X0Y5 - different style + + flags |= 256; // BAR_COLOR_LERP + + CG_FilledBar( rect->x, rect->y, rect->w, rect->h, color, color2, NULL, (float)cg.snap->ps.curWeapHeat / 255.0f, flags ); +} + +/* +============== +CG_OwnerDraw +============== +*/ +void CG_OwnerDraw( float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle ) { + rectDef_t rect; + + if ( cg_drawStatus.integer == 0 ) { + return; + } + + rect.x = x; + rect.y = y; + rect.w = w; + rect.h = h; + + switch ( ownerDraw ) { + default: + break; + } +} + +void CG_MouseEvent( int x, int y ) { + switch ( cgs.eventHandling ) { + case CGAME_EVENT_SPEAKEREDITOR: + case CGAME_EVENT_GAMEVIEW: + case CGAME_EVENT_CAMPAIGNBREIFING: + case CGAME_EVENT_FIRETEAMMSG: + cgs.cursorX += x; + if ( cgs.cursorX < 0 ) { + cgs.cursorX = 0; + } else if ( cgs.cursorX > 640 ) { + cgs.cursorX = 640; + } + + cgs.cursorY += y; + if ( cgs.cursorY < 0 ) { + cgs.cursorY = 0; + } else if ( cgs.cursorY > 480 ) { + cgs.cursorY = 480; + } + + if ( cgs.eventHandling == CGAME_EVENT_SPEAKEREDITOR ) { + CG_SpeakerEditorMouseMove_Handling( x, y ); + } + + break; + case CGAME_EVENT_DEMO: + cgs.cursorX += x; + if ( cgs.cursorX < 0 ) { + cgs.cursorX = 0; + } else if ( cgs.cursorX > 640 ) { + cgs.cursorX = 640; + } + + cgs.cursorY += y; + if ( cgs.cursorY < 0 ) { + cgs.cursorY = 0; + } else if ( cgs.cursorY > 480 ) { + cgs.cursorY = 480; + } + + if ( x != 0 || y != 0 ) { + cgs.cursorUpdate = cg.time + 5000; + } + break; + + + default: + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + CG_Debriefing_MouseEvent( x, y ); + return; + } + + // default handling + if ( ( cg.predictedPlayerState.pm_type == PM_NORMAL || + cg.predictedPlayerState.pm_type == PM_SPECTATOR ) && + cg.showScores == qfalse ) { + trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_CGAME ); + return; + } + break; + } +} + +/* +================== +CG_EventHandling +================== +*/ +void CG_EventHandling( int type, qboolean fForced ) { + if ( cg.demoPlayback && type == CGAME_EVENT_NONE && !fForced ) { + type = CGAME_EVENT_DEMO; + } + + if ( type != CGAME_EVENT_NONE ) { + trap_Cvar_Set( "cl_bypassMouseInput", 0 ); + } + + switch ( type ) { + // OSP - Demo support + case CGAME_EVENT_DEMO: + cgs.fResize = qfalse; + cgs.fSelect = qfalse; + cgs.cursorUpdate = cg.time + 10000; + cgs.timescaleUpdate = cg.time + 4000; + CG_ScoresUp_f(); + break; + + case CGAME_EVENT_SPEAKEREDITOR: + case CGAME_EVENT_GAMEVIEW: + case CGAME_EVENT_NONE: + case CGAME_EVENT_CAMPAIGNBREIFING: + case CGAME_EVENT_FIRETEAMMSG: + default: + // default handling (cleanup mostly) + if ( cgs.eventHandling == CGAME_EVENT_GAMEVIEW ) { + cg.showGameView = qfalse; + trap_S_FadeBackgroundTrack( 0.0f, 500, 0 ); + + trap_S_StopStreamingSound( -1 ); + cg.limboEndCinematicTime = 0; + + if ( fForced ) { + if ( cgs.limboLoadoutModified ) { + trap_SendClientCommand( "rs" ); + + cgs.limboLoadoutSelected = qfalse; + } + } + } else if ( cgs.eventHandling == CGAME_EVENT_SPEAKEREDITOR ) { + if ( type == -CGAME_EVENT_SPEAKEREDITOR ) { + type = CGAME_EVENT_NONE; + } else { + trap_Key_SetCatcher( KEYCATCH_CGAME ); + return; + } + } else if ( cgs.eventHandling == CGAME_EVENT_CAMPAIGNBREIFING ) { + type = CGAME_EVENT_GAMEVIEW; + } else if ( cgs.eventHandling == CGAME_EVENT_FIRETEAMMSG ) { + cg.showFireteamMenu = qfalse; + trap_Cvar_Set( "cl_bypassmouseinput", "0" ); + } else if ( cg.snap && cg.snap->ps.pm_type == PM_INTERMISSION && fForced ) { + trap_UI_Popup( UIMENU_INGAME ); + } + + + break; + } + + + cgs.eventHandling = type; + + if ( type == CGAME_EVENT_NONE ) { + trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_CGAME ); + ccInitial = qfalse; + if ( cg.demoPlayback && cg.demohelpWindow != SHOW_OFF ) { + CG_ShowHelp_Off( &cg.demohelpWindow ); + } + } else if ( type == CGAME_EVENT_GAMEVIEW ) { + cg.showGameView = qtrue; + CG_LimboPanel_Setup(); + trap_Key_SetCatcher( KEYCATCH_CGAME ); + } else if ( type == CGAME_EVENT_FIRETEAMMSG ) { + cgs.ftMenuPos = -1; + cgs.ftMenuMode = 0; + cg.showFireteamMenu = qtrue; + trap_Cvar_Set( "cl_bypassmouseinput", "1" ); + trap_Key_SetCatcher( KEYCATCH_CGAME ); + } else { + trap_Key_SetCatcher( KEYCATCH_CGAME ); + } +} + +void CG_KeyEvent( int key, qboolean down ) { + switch ( cgs.eventHandling ) { + // Demos get their own keys + case CGAME_EVENT_DEMO: + CG_DemoClick( key, down ); + return; + + case CGAME_EVENT_CAMPAIGNBREIFING: + CG_LoadPanel_KeyHandling( key, down ); + break; + + case CGAME_EVENT_FIRETEAMMSG: + CG_Fireteams_KeyHandling( key, down ); + break; + + case CGAME_EVENT_GAMEVIEW: + CG_LimboPanel_KeyHandling( key, down ); + break; + + case CGAME_EVENT_SPEAKEREDITOR: + CG_SpeakerEditor_KeyHandling( key, down ); + break; + + default: + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + CG_Debriefing_KeyEvent( key, down ); + return; + } + + // default handling + if ( !down ) { + return; + } + + if ( ( cg.predictedPlayerState.pm_type == PM_NORMAL || + ( cg.predictedPlayerState.pm_type == PM_SPECTATOR && cg.showScores == qfalse ) ) ) { + + CG_EventHandling( CGAME_EVENT_NONE, qfalse ); + return; + } + break; + } +} + +int CG_ClientNumFromName( const char *p ) { + int i; + for ( i = 0; i < cgs.maxclients; i++ ) { + if ( cgs.clientinfo[i].infoValid && Q_stricmp( cgs.clientinfo[i].name, p ) == 0 ) { + return i; + } + } + return -1; +} + +void CG_GetTeamColor( vec4_t *color ) { + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS ) { + ( *color )[0] = 1; + ( *color )[3] = .25f; + ( *color )[1] = ( *color )[2] = 0; + } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + ( *color )[0] = ( *color )[1] = 0; + ( *color )[2] = 1; + ( *color )[3] = .25f; + } else { + ( *color )[0] = ( *color )[2] = 0; + ( *color )[1] = .17f; + ( *color )[3] = .25f; + } +} + +void CG_RunMenuScript( char **args ) { +} diff --git a/src/cgame/cg_panelhandling.c b/src/cgame/cg_panelhandling.c new file mode 100644 index 0000000..7b9c7c9 --- /dev/null +++ b/src/cgame/cg_panelhandling.c @@ -0,0 +1,310 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +// digibob +// Panel Handling +// ====================================================== +panel_button_t* cg_focusButton; + +void CG_PanelButton_RenderEdit( panel_button_t* button ) { + qboolean useCvar = button->data[0] ? qfalse : qtrue; + int offset = -1; + + if ( useCvar ) { + char buffer[256 + 1]; + trap_Cvar_VariableStringBuffer( button->text, buffer, sizeof( buffer ) ); + + if ( cg_focusButton == button && ( ( cg.time / 1000 ) % 2 ) ) { + if ( trap_Key_GetOverstrikeMode() ) { + Q_strcat( buffer, sizeof( buffer ), "^0|" ); + } else { + Q_strcat( buffer, sizeof( buffer ), "^0_" ); + } + } else { + Q_strcat( buffer, sizeof( buffer ), " " ); + } + + do { + offset++; + if ( buffer + offset == '\0' ) { + break; + } + } while ( CG_Text_Width_Ext( buffer + offset, button->font->scalex, 0, button->font->font ) > button->rect.w ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->rect.h, button->font->scalex, button->font->scaley, button->font->colour, va( "^7%s", buffer + offset ), 0, 0, button->font->style, button->font->font ); + + // CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + } else { + int maxlen = button->data[0]; + char *s; + + if ( cg_focusButton == button && ( ( cg.time / 1000 ) % 2 ) ) { + if ( trap_Key_GetOverstrikeMode() ) { + s = va( "^7%s^0|", button->text ); + } else { + s = va( "^7%s^0_", button->text ); + } + } else { + s = va( "^7%s ", button->text ); // space hack to make the text not blink + } + + do { + offset++; + if ( s + offset == '\0' ) { + break; + } + } while ( CG_Text_Width_Ext( s + offset, button->font->scalex, 0, button->font->font ) > button->rect.w ); + + CG_Text_Paint_Ext( button->rect.x, button->rect.y + button->rect.h, button->font->scalex, button->font->scaley, button->font->colour, s + offset, 0, 0, button->font->style, button->font->font ); + + // CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colorRed ); + } +} + +qboolean CG_PanelButton_EditClick( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( !CG_CursorInRect( &button->rect ) && cg_focusButton == button ) { + CG_PanelButtons_SetFocusButton( NULL ); + if ( button->onFinish ) { + button->onFinish( button ); + } + return qfalse; + } else { + CG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + } else { + char buffer[256]; + char *s; + int len, maxlen; + qboolean useCvar = button->data[0] ? qfalse : qtrue; + + if ( useCvar ) { + maxlen = sizeof( buffer ); + trap_Cvar_VariableStringBuffer( button->text, buffer, sizeof( buffer ) ); + len = strlen( buffer ); + } else { + maxlen = button->data[0]; + s = (char *)button->text; + len = strlen( s ); + } + + if ( key & K_CHAR_FLAG ) { + key &= ~K_CHAR_FLAG; + + if ( key == 'h' - 'a' + 1 ) { // ctrl-h is backspace + if ( len ) { + if ( useCvar ) { + buffer[len - 1] = '\0'; + trap_Cvar_Set( button->text, buffer ); + } else { + s[len - 1] = '\0'; + } + } + return qtrue; + } + + if ( key < 32 ) { + return qtrue; + } + + if ( len >= maxlen - 1 ) { + return qtrue; + } + + if ( useCvar ) { + buffer[len] = key; + buffer[len + 1] = '\0'; + trap_Cvar_Set( button->text, buffer ); + } else { + s[len] = key; + s[len + 1] = '\0'; + } + return qtrue; + } else { + // Gordon: FIXME: have this work with all our stuff (use data[x] to store cursorpos etc) +/* if ( key == K_DEL || key == K_KP_DEL ) { + if ( item->cursorPos < len ) { + memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos); + DC->setCVar(item->cvar, buff); + } + return qtrue; + }*/ + +/* if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) + { + if (editPtr->maxPaintChars && item->cursorPos >= editPtr->paintOffset + editPtr->maxPaintChars && item->cursorPos < len) { + item->cursorPos++; + editPtr->paintOffset++; + return qtrue; + } + if (item->cursorPos < len) { + item->cursorPos++; + } + return qtrue; + } + + if ( key == K_LEFTARROW || key == K_KP_LEFTARROW ) + { + if ( item->cursorPos > 0 ) { + item->cursorPos--; + } + if (item->cursorPos < editPtr->paintOffset) { + editPtr->paintOffset--; + } + return qtrue; + } + + if ( key == K_HOME || key == K_KP_HOME) {// || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) { + item->cursorPos = 0; + editPtr->paintOffset = 0; + return qtrue; + } + + if ( key == K_END || key == K_KP_END) {// ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) { + item->cursorPos = len; + if(item->cursorPos > editPtr->maxPaintChars) { + editPtr->paintOffset = len - editPtr->maxPaintChars; + } + return qtrue; + } + + if ( key == K_INS || key == K_KP_INS ) { + DC->setOverstrikeMode(!DC->getOverstrikeMode()); + return qtrue; + }*/ + + if ( key == K_ENTER || key == K_KP_ENTER ) { + if ( button->onFinish ) { + button->onFinish( button ); + } + } + } + } + + return qtrue; +} + +qboolean CG_PanelButtonsKeyEvent( int key, qboolean down, panel_button_t** buttons ) { + panel_button_t* button; + + if ( cg_focusButton ) { + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button == cg_focusButton ) { + if ( button->onKeyDown && down ) { + if ( !button->onKeyDown( button, key ) ) { + if ( cg_focusButton ) { + return qfalse; + } + } + } + if ( button->onKeyUp && !down ) { + if ( !button->onKeyUp( button, key ) ) { + if ( cg_focusButton ) { + return qfalse; + } + } + } + } + } + } + + if ( down ) { + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button->onKeyDown ) { + if ( CG_CursorInRect( &button->rect ) ) { + if ( button->onKeyDown( button, key ) ) { + return qtrue; + } + } + } + } + } else { + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button->onKeyUp && CG_CursorInRect( &button->rect ) ) { + if ( button->onKeyUp( button, key ) ) { + return qtrue; + } + } + } + } + + return qfalse; +} + +void CG_PanelButtonsSetup( panel_button_t** buttons ) { + panel_button_t* button; + + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button->shaderNormal ) { + button->hShaderNormal = trap_R_RegisterShaderNoMip( button->shaderNormal ); + } + } +} + +void CG_PanelButtonsRender( panel_button_t** buttons ) { + panel_button_t* button; + + for ( ; *buttons; buttons++ ) { + button = ( *buttons ); + + if ( button->onDraw ) { + button->onDraw( button ); + } + } +} + +void CG_PanelButtonsRender_Text( panel_button_t* button ) { + if ( !button->font ) { + return; + } + + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, button->text, 0, 0, button->font->style, button->font->font ); +} + +void CG_PanelButtonsRender_Img( panel_button_t* button ) { + CG_DrawPic( button->rect.x, button->rect.y, button->rect.w, button->rect.h, button->hShaderNormal ); +} + +panel_button_t* CG_PanelButtons_GetFocusButton( void ) { + return cg_focusButton; +} + +void CG_PanelButtons_SetFocusButton( panel_button_t* button ) { + cg_focusButton = button; +} \ No newline at end of file diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c new file mode 100644 index 0000000..a656d6f --- /dev/null +++ b/src/cgame/cg_particles.c @@ -0,0 +1,2318 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// Rafael particles +// cg_particles.c + +#include "cg_local.h" + +#define MUSTARD 1 +#define BLOODRED 2 +#define EMISIVEFADE 3 +#define GREY75 4 +#define ZOMBIE 5 + +typedef struct particle_s +{ + struct particle_s *next; + + float time; + float endtime; + + vec3_t org; + vec3_t vel; + vec3_t accel; + int color; + float colorvel; + float alpha; + float alphavel; + int type; + qhandle_t pshader; + + float height; + float width; + + float endheight; + float endwidth; + + float start; + float end; + + float startfade; + qboolean rotate; + int snum; + + qboolean link; + + // Ridah + int shaderAnim; + int roll; + + int accumroll; + +} cparticle_t; + +typedef enum +{ + P_NONE, + P_WEATHER, + P_FLAT, + P_SMOKE, + P_ROTATE, + P_WEATHER_TURBULENT, + P_ANIM, // Ridah + P_DLIGHT_ANIM, // ydnar + P_BLEED, + P_FLAT_SCALEUP, + P_FLAT_SCALEUP_FADE, + P_WEATHER_FLURRY, + P_SMOKE_IMPACT, + P_BUBBLE, + P_BUBBLE_TURBULENT, + P_SPRITE +} particle_type_t; + +#define MAX_SHADER_ANIMS 8 +#define MAX_SHADER_ANIM_FRAMES 64 +static char *shaderAnimNames[MAX_SHADER_ANIMS] = { + "explode1", + "blacksmokeanim", + "twiltb2", + "blacksmokeanimc", +// "expblue", +// "blacksmokeanimb", // uses 'explode1' sequence // JPW NERVE pulled +// "blood", + NULL +}; +static qhandle_t shaderAnims[MAX_SHADER_ANIMS][MAX_SHADER_ANIM_FRAMES]; +static int shaderAnimCounts[MAX_SHADER_ANIMS] = { + 23, + 23, // (SA) removing warning messages from startup + 45, + 23, + 25, + 23, + 5, +}; +static float shaderAnimSTRatio[MAX_SHADER_ANIMS] = { + 1, // NERVE - SMF - changed from 1.405 to 1 + 1, + 1, + 1, + 1, + 1, + 1, +}; +static int numShaderAnims; +// done. + +#define PARTICLE_GRAVITY 40 +#define MAX_PARTICLES 1024 * 8 + +cparticle_t *active_particles, *free_particles; +cparticle_t particles[MAX_PARTICLES]; +int cl_numparticles = MAX_PARTICLES; + +qboolean initparticles = qfalse; +vec3_t vforward, vright, vup; +vec3_t rforward, rright, rup; + +float oldtime; + +/* +=============== +CL_ClearParticles +=============== +*/ +void CG_ClearParticles( void ) { + int i; + + memset( particles, 0, sizeof( particles ) ); + + free_particles = &particles[0]; + active_particles = NULL; + + for ( i = 0 ; i < cl_numparticles ; i++ ) + { + particles[i].next = &particles[i + 1]; + particles[i].type = 0; + } + particles[cl_numparticles - 1].next = NULL; + + oldtime = cg.time; + + // Ridah, init the shaderAnims + for ( i = 0; shaderAnimNames[i]; i++ ) { + int j; + + for ( j = 0; j < shaderAnimCounts[i]; j++ ) { + shaderAnims[i][j] = trap_R_RegisterShader( va( "%s%i", shaderAnimNames[i], j + 1 ) ); + } + } + numShaderAnims = i; + // done. + + initparticles = qtrue; +} + + +/* +===================== +CG_AddParticleToScene +===================== +*/ +#define ROOT_2 1.414213562373f + +void CG_AddParticleToScene( cparticle_t *p, vec3_t org, float alpha ) { + + vec3_t point; + polyVert_t verts[4]; + float width; + float height; + float time, time2; + float ratio; + float invratio; + vec3_t color; + polyVert_t TRIverts[3]; + vec3_t rright2, rup2; + + if ( p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY + || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT ) { // create a front facing polygon + + if ( p->type != P_WEATHER_FLURRY ) { + if ( p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT ) { + if ( org[2] > p->end ) { + p->time = cg.time; + VectorCopy( org, p->org ); // Ridah, fixes rare snow flakes that flicker on the ground + + p->org[2] = ( p->start + crandom() * 4 ); + + + if ( p->type == P_BUBBLE_TURBULENT ) { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + } + } else + { + if ( org[2] < p->end ) { + p->time = cg.time; + VectorCopy( org, p->org ); // Ridah, fixes rare snow flakes that flicker on the ground + + while ( p->org[2] < p->end ) + { + p->org[2] += ( p->start - p->end ); + } + + + if ( p->type == P_WEATHER_TURBULENT ) { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + } + } + + + // Rafael snow pvs check + if ( !p->link ) { + return; + } + + p->alpha = 1; + } + + // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp + if ( VectorDistanceSquared( cg.snap->ps.origin, org ) > SQR( 1024 ) ) { + return; + } + // done. + + if ( p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT ) { + VectorMA( org, -p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + VectorCopy( point, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255 * p->alpha; + + VectorMA( org, -p->height, vup, point ); + VectorMA( point, p->width, vright, point ); + VectorCopy( point, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255 * p->alpha; + + VectorMA( org, p->height, vup, point ); + VectorMA( point, p->width, vright, point ); + VectorCopy( point, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255 * p->alpha; + + VectorMA( org, p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + VectorCopy( point, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255 * p->alpha; + } else + { + VectorMA( org, -p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + VectorCopy( point, TRIverts[0].xyz ); + TRIverts[0].st[0] = 1; + TRIverts[0].st[1] = 0; + TRIverts[0].modulate[0] = 255; + TRIverts[0].modulate[1] = 255; + TRIverts[0].modulate[2] = 255; + TRIverts[0].modulate[3] = 255 * p->alpha; + + VectorMA( org, p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + VectorCopy( point, TRIverts[1].xyz ); + TRIverts[1].st[0] = 0; + TRIverts[1].st[1] = 0; + TRIverts[1].modulate[0] = 255; + TRIverts[1].modulate[1] = 255; + TRIverts[1].modulate[2] = 255; + TRIverts[1].modulate[3] = 255 * p->alpha; + + VectorMA( org, p->height, vup, point ); + VectorMA( point, p->width, vright, point ); + VectorCopy( point, TRIverts[2].xyz ); + TRIverts[2].st[0] = 0; + TRIverts[2].st[1] = 1; + TRIverts[2].modulate[0] = 255; + TRIverts[2].modulate[1] = 255; + TRIverts[2].modulate[2] = 255; + TRIverts[2].modulate[3] = 255 * p->alpha; + } + + } else if ( p->type == P_SPRITE ) { + vec3_t rr, ru; + vec3_t rotate_ang; + + VectorSet( color, 1.0, 1.0, 1.0 ); + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width ) ); + height = p->height + ( ratio * ( p->endheight - p->height ) ); + + if ( p->roll ) { + vectoangles( cg.refdef_current->viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors( rotate_ang, NULL, rr, ru ); + } + + if ( p->roll ) { + VectorMA( org, -height, ru, point ); + VectorMA( point, -width, rr, point ); + } else { + VectorMA( org, -height, vup, point ); + VectorMA( point, -width, vright, point ); + } + VectorCopy( point, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, 2 * height, ru, point ); + } else { + VectorMA( point, 2 * height, vup, point ); + } + VectorCopy( point, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, 2 * width, rr, point ); + } else { + VectorMA( point, 2 * width, vright, point ); + } + VectorCopy( point, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, -2 * height, ru, point ); + } else { + VectorMA( point, -2 * height, vup, point ); + } + VectorCopy( point, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } else if ( p->type == P_SMOKE || p->type == P_SMOKE_IMPACT ) { // create a front rotating facing polygon + + if ( p->type == P_SMOKE_IMPACT && VectorDistanceSquared( cg.snap->ps.origin, org ) > SQR( 1024 ) ) { + return; + } + + if ( p->color == MUSTARD ) { + VectorSet( color, 0.42, 0.33, 0.19 ); + } else if ( p->color == BLOODRED ) { + VectorSet( color, 0.22, 0, 0 ); + } else if ( p->color == ZOMBIE ) { + VectorSet( color, 0.4, 0.28, 0.23 ); + } else if ( p->color == GREY75 ) { + float len; + float greyit; + float val; + len = Distance( cg.snap->ps.origin, org ); + if ( !len ) { + len = 1; + } + + val = 4096 / len; + greyit = 0.25 * val; + if ( greyit > 0.5 ) { + greyit = 0.5; + } + + VectorSet( color, greyit, greyit, greyit ); + } else { + VectorSet( color, 1.0, 1.0, 1.0 ); + } + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + if ( cg.time > p->startfade ) { + invratio = 1 - ( ( cg.time - p->startfade ) / ( p->endtime - p->startfade ) ); + + if ( p->color == EMISIVEFADE ) { + float fval; + fval = ( invratio * invratio ); + if ( fval < 0 ) { + fval = 0; + } + VectorSet( color, fval, fval, fval ); + } + invratio *= p->alpha; + } else { + invratio = 1 * p->alpha; + } + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + invratio = 1; + } + + if ( invratio > 1 ) { + invratio = 1; + } + + width = p->width + ( ratio * ( p->endwidth - p->width ) ); + height = p->height + ( ratio * ( p->endheight - p->height ) ); + +// if (p->type != P_SMOKE_IMPACT) + { + vec3_t temp; + + vectoangles( rforward, temp ); + p->accumroll += p->roll; + temp[ROLL] += p->accumroll * 0.1; +// temp[ROLL] += p->roll * 0.1; + AngleVectors( temp, NULL, rright2, rup2 ); + } +// else +// { +// VectorCopy (rright, rright2); +// VectorCopy (rup, rup2); +// } + + if ( p->rotate ) { + VectorMA( org, -height, rup2, point ); + VectorMA( point, -width, rright2, point ); + } else + { + VectorMA( org, -p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + } + VectorCopy( point, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255 * invratio; + + if ( p->rotate ) { + VectorMA( org, -height, rup2, point ); + VectorMA( point, width, rright2, point ); + } else + { + VectorMA( org, -p->height, vup, point ); + VectorMA( point, p->width, vright, point ); + } + VectorCopy( point, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255 * color[0]; + verts[1].modulate[1] = 255 * color[1]; + verts[1].modulate[2] = 255 * color[2]; + verts[1].modulate[3] = 255 * invratio; + + if ( p->rotate ) { + VectorMA( org, height, rup2, point ); + VectorMA( point, width, rright2, point ); + } else + { + VectorMA( org, p->height, vup, point ); + VectorMA( point, p->width, vright, point ); + } + VectorCopy( point, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255 * color[0]; + verts[2].modulate[1] = 255 * color[1]; + verts[2].modulate[2] = 255 * color[2]; + verts[2].modulate[3] = 255 * invratio; + + if ( p->rotate ) { + VectorMA( org, height, rup2, point ); + VectorMA( point, -width, rright2, point ); + } else + { + VectorMA( org, p->height, vup, point ); + VectorMA( point, -p->width, vright, point ); + } + VectorCopy( point, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255 * color[0]; + verts[3].modulate[1] = 255 * color[1]; + verts[3].modulate[2] = 255 * color[2]; + verts[3].modulate[3] = 255 * invratio; + + } else if ( p->type == P_BLEED ) { + vec3_t rr, ru; + vec3_t rotate_ang; + float alpha; + + alpha = p->alpha; + + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + alpha = 1; + } + + if ( p->roll ) { + vectoangles( cg.refdef_current->viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors( rotate_ang, NULL, rr, ru ); + } else + { + VectorCopy( vup, ru ); + VectorCopy( vright, rr ); + } + + VectorMA( org, -p->height, ru, point ); + VectorMA( point, -p->width, rr, point ); + VectorCopy( point, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 111; + verts[0].modulate[1] = 19; + verts[0].modulate[2] = 9; + verts[0].modulate[3] = 255 * alpha; + + VectorMA( org, -p->height, ru, point ); + VectorMA( point, p->width, rr, point ); + VectorCopy( point, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 111; + verts[1].modulate[1] = 19; + verts[1].modulate[2] = 9; + verts[1].modulate[3] = 255 * alpha; + + VectorMA( org, p->height, ru, point ); + VectorMA( point, p->width, rr, point ); + VectorCopy( point, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 111; + verts[2].modulate[1] = 19; + verts[2].modulate[2] = 9; + verts[2].modulate[3] = 255 * alpha; + + VectorMA( org, p->height, ru, point ); + VectorMA( point, -p->width, rr, point ); + VectorCopy( point, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 111; + verts[3].modulate[1] = 19; + verts[3].modulate[2] = 9; + verts[3].modulate[3] = 255 * alpha; + + } else if ( p->type == P_FLAT_SCALEUP ) { + float width, height; + float sinR, cosR; + + if ( p->color == BLOODRED ) { + VectorSet( color, 1, 1, 1 ); + } else { + VectorSet( color, 0.5, 0.5, 0.5 ); + } + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + + width = p->width + ( ratio * ( p->endwidth - p->width ) ); + height = p->height + ( ratio * ( p->endheight - p->height ) ); + + if ( width > p->endwidth ) { + width = p->endwidth; + } + + if ( height > p->endheight ) { + height = p->endheight; + } + + sinR = height * sin( DEG2RAD( p->roll ) ) * ROOT_2; + cosR = width * cos( DEG2RAD( p->roll ) ) * ROOT_2; + + VectorCopy( org, verts[0].xyz ); + verts[0].xyz[0] -= sinR; + verts[0].xyz[1] -= cosR; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255 * color[0]; + verts[0].modulate[1] = 255 * color[1]; + verts[0].modulate[2] = 255 * color[2]; + verts[0].modulate[3] = 255; + + VectorCopy( org, verts[1].xyz ); + verts[1].xyz[0] -= cosR; + verts[1].xyz[1] += sinR; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = verts[0].modulate[0]; + verts[1].modulate[1] = verts[0].modulate[1]; + verts[1].modulate[2] = verts[0].modulate[2]; + verts[1].modulate[3] = verts[0].modulate[3]; + + VectorCopy( org, verts[2].xyz ); + verts[2].xyz[0] += sinR; + verts[2].xyz[1] += cosR; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = verts[0].modulate[0]; + verts[2].modulate[1] = verts[0].modulate[1]; + verts[2].modulate[2] = verts[0].modulate[2]; + verts[2].modulate[3] = verts[0].modulate[3]; + + VectorCopy( org, verts[3].xyz ); + verts[3].xyz[0] += cosR; + verts[3].xyz[1] -= sinR; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = verts[0].modulate[0]; + verts[3].modulate[1] = verts[0].modulate[1]; + verts[3].modulate[2] = verts[0].modulate[2]; + verts[3].modulate[3] = verts[0].modulate[3]; + } else if ( p->type == P_FLAT ) { + + VectorCopy( org, verts[0].xyz ); + verts[0].xyz[0] -= p->height; + verts[0].xyz[1] -= p->width; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorCopy( org, verts[1].xyz ); + verts[1].xyz[0] -= p->height; + verts[1].xyz[1] += p->width; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorCopy( org, verts[2].xyz ); + verts[2].xyz[0] += p->height; + verts[2].xyz[1] += p->width; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorCopy( org, verts[3].xyz ); + verts[3].xyz[0] += p->height; + verts[3].xyz[1] -= p->width; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + } + // Ridah + else if ( p->type == P_ANIM || p->type == P_DLIGHT_ANIM ) { // ydnar + vec3_t rr, ru; + vec3_t rotate_ang; + int i, j; + + time = cg.time - p->time; + time2 = p->endtime - p->time; + ratio = time / time2; + if ( ratio >= 1.0 ) { + ratio = 0.9999; + } else if ( ratio < 0.0 ) { + // rain - make sure that ratio isn't negative or + // we'll walk out of bounds when j is calculated below + ratio = 0.0001; + } + + width = p->width + ( ratio * ( p->endwidth - p->width ) ); + height = p->height + ( ratio * ( p->endheight - p->height ) ); + + // ydnar: add dlight if necessary + if ( p->type == P_DLIGHT_ANIM ) { + // fixme: support arbitrary color + trap_R_AddLightToScene( org, 320, //% 1.5 * (width > height ? width : height), + 1.25 * ( 1.0 - ratio ), 1.0, 0.95, 0.85, 0, 0 ); + } + + // if we are "inside" this sprite, don't draw + if ( VectorDistanceSquared( cg.snap->ps.origin, org ) < SQR( width / 1.5f ) ) { + return; + } + + i = p->shaderAnim; + j = (int)floor( ratio * shaderAnimCounts[p->shaderAnim] ); + p->pshader = shaderAnims[i][j]; + + // JPW NERVE more particle testing + if ( cg_fxflags & 1 ) { + p->roll = 0; + p->pshader = getTestShader(); + rotate_ang[ROLL] = 90; + } + // jpw + + if ( p->roll ) { + vectoangles( cg.refdef_current->viewaxis[0], rotate_ang ); + rotate_ang[ROLL] += p->roll; + AngleVectors( rotate_ang, NULL, rr, ru ); + } + + if ( p->roll ) { + VectorMA( org, -height, ru, point ); + VectorMA( point, -width, rr, point ); + } else { + VectorMA( org, -height, vup, point ); + VectorMA( point, -width, vright, point ); + } + VectorCopy( point, verts[0].xyz ); + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, 2 * height, ru, point ); + } else { + VectorMA( point, 2 * height, vup, point ); + } + VectorCopy( point, verts[1].xyz ); + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, 2 * width, rr, point ); + } else { + VectorMA( point, 2 * width, vright, point ); + } + VectorCopy( point, verts[2].xyz ); + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + if ( p->roll ) { + VectorMA( point, -2 * height, ru, point ); + } else { + VectorMA( point, -2 * height, vup, point ); + } + VectorCopy( point, verts[3].xyz ); + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + } + // done. + + if ( !cg_wolfparticles.integer ) { + return; + } + + if ( !p->pshader ) { +// (SA) temp commented out for DM again. FIXME: TODO: this needs to be addressed +// CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); + return; + } + + if ( p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY ) { + trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); + } else { + trap_R_AddPolyToScene( p->pshader, 4, verts ); + } + +} + +// Ridah, made this static so it doesn't interfere with other files +static float roll = 0.0; + +/* +=============== +CG_AddParticles +=============== +*/ +void CG_AddParticles( void ) { + cparticle_t *p, *next; + float alpha; + float time, time2; + vec3_t org; + int color; + cparticle_t *active, *tail; + int type; + vec3_t rotate_ang; + + if ( !initparticles ) { + CG_ClearParticles(); + } + + VectorCopy( cg.refdef_current->viewaxis[0], vforward ); + VectorCopy( cg.refdef_current->viewaxis[1], vright ); + VectorCopy( cg.refdef_current->viewaxis[2], vup ); + + vectoangles( cg.refdef_current->viewaxis[0], rotate_ang ); + roll += ( ( cg.time - oldtime ) * 0.1 ) ; + rotate_ang[ROLL] += ( roll * 0.9 ); + AngleVectors( rotate_ang, rforward, rright, rup ); + + oldtime = cg.time; + + active = NULL; + tail = NULL; + + for ( p = active_particles ; p ; p = next ) + { + + next = p->next; + + time = ( cg.time - p->time ) * 0.001; + + alpha = p->alpha + time * p->alphavel; + if ( alpha <= 0 ) { // faded out + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + if ( p->type == P_SMOKE || p->type == P_ANIM || p->type == P_DLIGHT_ANIM || p->type == P_BLEED || p->type == P_SMOKE_IMPACT ) { + if ( cg.time > p->endtime ) { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + + } + + if ( p->type == P_WEATHER_FLURRY ) { + if ( cg.time > p->endtime ) { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + + continue; + } + } + + + if ( p->type == P_FLAT_SCALEUP_FADE ) { + if ( cg.time > p->endtime ) { + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + } + + if ( p->type == P_SPRITE && p->endtime < 0 ) { + // temporary sprite + CG_AddParticleToScene( p, p->org, alpha ); + p->next = free_particles; + free_particles = p; + p->type = 0; + p->color = 0; + p->alpha = 0; + continue; + } + + p->next = NULL; + if ( !tail ) { + active = tail = p; + } else + { + tail->next = p; + tail = p; + } + + if ( alpha > 1.0 ) { + alpha = 1; + } + + color = p->color; + + time2 = time * time; + + org[0] = p->org[0] + p->vel[0] * time + p->accel[0] * time2; + org[1] = p->org[1] + p->vel[1] * time + p->accel[1] * time2; + org[2] = p->org[2] + p->vel[2] * time + p->accel[2] * time2; + + type = p->type; + + CG_AddParticleToScene( p, org, alpha ); + } + + active_particles = active; +} + +/* +====================== +CG_AddParticles +====================== +*/ +void CG_ParticleSnowFlurry( qhandle_t pshader, centity_t *cent ) { + cparticle_t *p; + qboolean turb = qtrue; + + if ( !pshader ) { + CG_Printf( "CG_ParticleSnowFlurry pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.90; + p->alphavel = 0; + + p->start = cent->currentState.origin2[0]; + p->end = cent->currentState.origin2[1]; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->pshader = pshader; + + if ( rand() % 100 > 90 ) { + p->height = 32; + p->width = 32; + p->alpha = 0.10; + } else + { + p->height = 1; + p->width = 1; + } + + p->vel[2] = -20; + + p->type = P_WEATHER_FLURRY; + + if ( turb ) { + p->vel[2] = -10; + } + + VectorCopy( cent->currentState.origin, p->org ); + + p->org[0] = p->org[0]; + p->org[1] = p->org[1]; + p->org[2] = p->org[2]; + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += cent->currentState.angles[0] * 32 + ( crandom() * 16 ); + p->vel[1] += cent->currentState.angles[1] * 32 + ( crandom() * 16 ); + p->vel[2] += cent->currentState.angles[2]; + + if ( turb ) { + p->accel[0] = crandom() * 16; + p->accel[1] = crandom() * 16; + } + +} + +void CG_ParticleSnow( qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum ) { + cparticle_t *p; + + if ( !pshader ) { + CG_Printf( "CG_ParticleSnow pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + p->height = 1; + p->width = 1; + + p->vel[2] = -50; + + if ( turb ) { + p->type = P_WEATHER_TURBULENT; + p->vel[2] = -50 * 1.3; + } else + { + p->type = P_WEATHER; + } + + VectorCopy( origin, p->org ); + + p->org[0] = p->org[0] + ( crandom() * range ); + p->org[1] = p->org[1] + ( crandom() * range ); + p->org[2] = p->org[2] + ( crandom() * ( p->start - p->end ) ); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if ( turb ) { + p->vel[0] = crandom() * 16; + p->vel[1] = crandom() * 16; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleBubble( qhandle_t pshader, vec3_t origin, vec3_t origin2, int turb, float range, int snum ) { + cparticle_t *p; + float randsize; + + if ( !pshader ) { + CG_Printf( "CG_ParticleSnow pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->color = 0; + p->alpha = 0.40; + p->alphavel = 0; + p->start = origin[2]; + p->end = origin2[2]; + p->pshader = pshader; + + randsize = 1 + ( crandom() * 0.5 ); + + p->height = randsize; + p->width = randsize; + + p->vel[2] = 50 + ( crandom() * 10 ); + + if ( turb ) { + p->type = P_BUBBLE_TURBULENT; + p->vel[2] = 50 * 1.3; + } else + { + p->type = P_BUBBLE; + } + + VectorCopy( origin, p->org ); + + p->org[0] = p->org[0] + ( crandom() * range ); + p->org[1] = p->org[1] + ( crandom() * range ); + p->org[2] = p->org[2] + ( crandom() * ( p->start - p->end ) ); + + p->vel[0] = p->vel[1] = 0; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if ( turb ) { + p->vel[0] = crandom() * 4; + p->vel[1] = crandom() * 4; + } + + // Rafael snow pvs check + p->snum = snum; + p->link = qtrue; + +} + +void CG_ParticleSmoke( qhandle_t pshader, centity_t *cent ) { + + // using cent->density = enttime + // cent->frame = startfade + cparticle_t *p; + vec3_t dir; + + if ( !pshader ) { + CG_Printf( "CG_ParticleSmoke == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + cent->currentState.time; + p->startfade = cg.time + cent->currentState.time2; + + p->color = 0; + p->alpha = 1.0; + p->alphavel = 0; + p->start = cent->currentState.origin[2]; + p->end = cent->currentState.origin2[2]; + p->pshader = pshader; + if ( cent->currentState.density == 1 || cent->currentState.modelindex2 ) { + p->rotate = qfalse; + p->height = 8; + p->width = 8; + p->endheight = 32; + p->endwidth = 32; + } else if ( cent->currentState.density == 2 ) { + p->rotate = qtrue; + p->height = 4; + p->width = 4; + p->endheight = 8; + p->endwidth = 8; + } else if ( cent->currentState.density == 3 ) { + p->rotate = qfalse; + { + float scale; + + scale = 16 + ( crandom() * 8 ); + p->height = 24 + scale; + p->width = 24 + scale; + p->endheight = 64 + scale; + p->endwidth = 64 + scale; + } + } else if ( cent->currentState.density == 4 ) { // white smoke + p->rotate = qtrue; + p->height = cent->currentState.angles2[0]; + p->width = cent->currentState.angles2[0]; + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + p->color = GREY75; + } else if ( cent->currentState.density == 5 ) { // mustard gas + p->rotate = qtrue; + p->height = cent->currentState.angles2[0]; + p->width = cent->currentState.angles2[0]; + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + p->color = MUSTARD; + p->alpha = 0.75; + } else // black smoke + { + p->rotate = qtrue; + p->height = cent->currentState.angles2[0]; + p->width = cent->currentState.angles2[0]; + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + + { + int rval; + rval = rand() % 6; + if ( rval == 1 ) { + p->pshader = cgs.media.smokePuffShaderb1; + } else if ( rval == 2 ) { + p->pshader = cgs.media.smokePuffShaderb2; + } else if ( rval == 3 ) { + p->pshader = cgs.media.smokePuffShaderb3; + } else if ( rval == 4 ) { + p->pshader = cgs.media.smokePuffShaderb4; + } else { + p->pshader = cgs.media.smokePuffShaderb5; + } + } + } + + + p->type = P_SMOKE; + + //VectorCopy(cent->currentState.origin, p->org); + VectorCopy( cent->lerpOrigin, p->org ); + + p->vel[0] = p->vel[1] = 0; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + if ( cent->currentState.density == 1 ) { + p->vel[2] = 5; + } else if ( cent->currentState.density == 2 ) { + p->vel[2] = 5; + } else if ( cent->currentState.density == 3 ) { // cannon + VectorCopy( cent->currentState.origin2, dir ); + p->vel[0] = dir[0] * 128 + ( crandom() * 64 ); + p->vel[1] = dir[1] * 128 + ( crandom() * 64 ); + p->vel[2] = 15 + ( crandom() * 16 ); + } else if ( cent->currentState.density == 5 ) { // gas or cover smoke + VectorCopy( cent->currentState.origin2, dir ); + p->vel[0] = dir[0] * 32 + ( crandom() * 16 ); + p->vel[1] = dir[1] * 32 + ( crandom() * 16 ); + p->vel[2] = 4 + ( crandom() * 2 ); + } else // smoke + { + VectorCopy( cent->currentState.origin2, dir ); + p->vel[0] = dir[0] + ( crandom() * p->height ); + p->vel[1] = dir[1] + ( crandom() * p->height ); + p->vel[2] = cent->currentState.angles2[2]; + } + + if ( cent->currentState.frame == 1 ) { // reverse gravity + p->vel[2] *= -1; + } + + p->roll = 8 + ( crandom() * 4 ); +} + + +void CG_ParticleBulletDebris( vec3_t org, vec3_t vel, int duration ) { + + cparticle_t *p; + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration / 2; + + p->color = EMISIVEFADE; + p->alpha = 1.0; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy( org, p->org ); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->accel[2] = -60; + p->vel[2] += -20; + +} + +// DHM - Nerve :: bullets hitting dirt + +void CG_ParticleDirtBulletDebris( vec3_t org, vec3_t vel, int duration ) { + int r = rand() % 3; + cparticle_t *p; + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration / 2; + + p->color = EMISIVEFADE; + p->alpha = 1.0; + p->alphavel = 0; + + p->height = 1.2; + p->width = 1.2; + p->endheight = 4.5; + p->endwidth = 4.5; + + if ( r == 0 ) { + p->pshader = cgs.media.dirtParticle1Shader; + } else if ( r == 1 ) { + p->pshader = cgs.media.dirtParticle2Shader; + } else { + p->pshader = cgs.media.dirtParticle3Shader; + } + + p->type = P_SMOKE; + + VectorCopy( org, p->org ); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->accel[2] = -330; + p->vel[2] += -20; +} + +// NERVE - SMF :: the core of the dirt explosion +void CG_ParticleDirtBulletDebris_Core( vec3_t org, vec3_t vel, int duration, float width, float height, float alpha, qhandle_t shader ) { + cparticle_t *p; + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->endtime = cg.time + duration; + p->startfade = cg.time + duration / 2; + + p->color = EMISIVEFADE; + p->alpha = alpha; + p->alphavel = 0; + + p->height = width; + p->width = height; + p->endheight = p->height; + p->endwidth = p->width; + + p->rotate = 0; + + p->type = P_SMOKE; + + p->pshader = shader; + if ( cg_fxflags & 1 ) { + p->pshader = getTestShader(); + p->rotate = 0; + p->roll = 0; + p->type = P_SPRITE; + } + + VectorCopy( org, p->org ); + VectorCopy( vel, p->vel ); + VectorSet( p->accel, 0, 0, -330 ); +} + +// DHM - Nerve :: end + +/* +====================== +CG_ParticleExplosion +====================== +*/ + +void CG_ParticleExplosion( char *animStr, vec3_t origin, vec3_t vel, int duration, int sizeStart, int sizeEnd, qboolean dlight ) { + cparticle_t *p; + int anim; + +#if 0 // rain - this is arguably not legal... it seems to mostly be a + // debugging thing anyway, so I'm killing it for now + if ( animStr < (char *)10 ) { + CG_Error( "CG_ParticleExplosion: animStr is probably an index rather than a string" ); + } +#endif + + // find the animation string + for ( anim = 0; shaderAnimNames[anim]; anim++ ) { + if ( !Q_stricmp( animStr, shaderAnimNames[anim] ) ) { + break; + } + } + if ( !shaderAnimNames[anim] ) { + CG_Error( "CG_ParticleExplosion: unknown animation string: %s\n", animStr ); + return; + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + + if ( duration < 0 ) { + duration *= -1; + p->roll = 0; + } else { + p->roll = crandom() * 179; + } + + p->shaderAnim = anim; + + p->width = sizeStart; + p->height = sizeStart * shaderAnimSTRatio[anim]; // for sprites that are stretch in either direction + + p->endheight = sizeEnd; + p->endwidth = sizeEnd * shaderAnimSTRatio[anim]; + + p->endtime = cg.time + duration; + + // ydnar + if ( dlight ) { + p->type = P_DLIGHT_ANIM; + } else { + p->type = P_ANIM; + } + + VectorCopy( origin, p->org ); + VectorCopy( vel, p->vel ); + VectorClear( p->accel ); + +} + +// Rafael Shrapnel +void CG_AddParticleShrapnel( localEntity_t *le ) { + return; +} +// done. + +int CG_NewParticleArea( int num ) { + // const char *str; + char *str; + char *token; + int type; + vec3_t origin, origin2; + int i; + float range = 0; + int turb; + int numparticles; + int snum; + + str = (char *) CG_ConfigString( num ); + if ( !str[0] ) { + return ( 0 ); + } + + // returns type 128 64 or 32 + token = COM_Parse( &str ); + type = atoi( token ); + + if ( type == 1 ) { + range = 128; + } else if ( type == 2 ) { + range = 64; + } else if ( type == 3 ) { + range = 32; + } else if ( type == 0 ) { + range = 256; + } else if ( type == 4 ) { + range = 8; + } else if ( type == 5 ) { + range = 16; + } else if ( type == 6 ) { + range = 32; + } else if ( type == 7 ) { + range = 64; + } + + + for ( i = 0; i < 3; i++ ) + { + token = COM_Parse( &str ); + origin[i] = atof( token ); + } + + for ( i = 0; i < 3; i++ ) + { + token = COM_Parse( &str ); + origin2[i] = atof( token ); + } + + token = COM_Parse( &str ); + numparticles = atoi( token ); + + token = COM_Parse( &str ); + turb = atoi( token ); + + token = COM_Parse( &str ); + snum = atoi( token ); + + for ( i = 0; i < numparticles; i++ ) + { + if ( type >= 4 ) { + CG_ParticleBubble( cgs.media.waterBubbleShader, origin, origin2, turb, range, snum ); + } else { + CG_ParticleSnow( cgs.media.snowShader, origin, origin2, turb, range, snum ); + } + } + + return ( 1 ); +} + +void CG_SnowLink( centity_t *cent, qboolean particleOn ) { + cparticle_t *p, *next; + int id; + + id = cent->currentState.frame; + + for ( p = active_particles ; p ; p = next ) + { + next = p->next; + + if ( p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT ) { + if ( p->snum == id ) { + if ( particleOn ) { + p->link = qtrue; + } else { + p->link = qfalse; + } + } + } + + } +} + +void CG_ParticleImpactSmokePuffExtended( qhandle_t pshader, vec3_t origin, int lifetime, int vel, int acc, int maxroll, float alpha, float size ) { + cparticle_t *p; + + if ( !pshader ) { + CG_Printf( "CG_ParticleImpactSmokePuff pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = alpha; + p->alphavel = 0; + + // (SA) roll either direction + p->roll = rand() % ( 2 * maxroll ); +// p->roll = crandom()*(float)(maxroll*2); + p->roll -= maxroll; + + p->pshader = pshader; + + p->endtime = cg.time + lifetime; + p->startfade = cg.time + 100; + + // xkan, 1/10/2003 - changed calculation to prevent division by 0 for small size + p->width = size * ( 1.0 + random() * 0.5 ); // rand()%(int)(size * .5f) + size; + p->height = size * ( 1.0 + random() * 0.5 ); // rand()%(int)(size * .5f) + size; + + p->endheight = p->height * 2; + p->endwidth = p->width * 2; + + p->type = P_SMOKE_IMPACT; + + VectorCopy( origin, p->org ); + VectorSet( p->vel, 0, 0, vel ); + VectorSet( p->accel, 0, 0, acc ); + + p->rotate = qtrue; +} + +void CG_ParticleImpactSmokePuff( qhandle_t pshader, vec3_t origin ) { + CG_ParticleImpactSmokePuffExtended( pshader, origin, 500, 20, 20, 30, 0.25f, 8.f ); +} + + +void CG_Particle_Bleed( qhandle_t pshader, vec3_t start, vec3_t dir, int fleshEntityNum, int duration ) { + cparticle_t *p; + + if ( !pshader ) { + CG_Printf( "CG_Particle_Bleed pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + if ( fleshEntityNum ) { + p->startfade = cg.time; + } else { + p->startfade = cg.time + 100; + } + + p->width = 4; + p->height = 4; + + p->endheight = 4 + rand() % 3; + p->endwidth = p->endheight; + + p->type = P_SMOKE; + + VectorCopy( start, p->org ); + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -20; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand() % 179; + + if ( fleshEntityNum ) { + p->color = MUSTARD; + } else { + p->color = BLOODRED; + } + p->alpha = 0.75; + +} + +//void CG_Particle_OilParticle (qhandle_t pshader, centity_t *cent) +void CG_Particle_OilParticle( qhandle_t pshader, vec3_t origin, vec3_t dir, int ptime, int snum ) { // snum is parent ent number? + cparticle_t *p; + + int time; + int time2; + float ratio; + +// float duration = 1500; + float duration = 2000; + + time = cg.time; + time2 = cg.time + ptime; + + ratio = (float)1 - ( (float)time / (float)time2 ); + + if ( !pshader ) { + CG_Printf( "CG_Particle_OilParticle == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + p->endtime = cg.time + duration; + + p->startfade = p->endtime; + + p->width = 2; + p->height = 2; + + p->endwidth = 1; + p->endheight = 1; + + p->type = P_SMOKE; + + VectorCopy( origin, p->org ); + + p->vel[0] = ( dir[0] * ( 16 * ratio ) ); + p->vel[1] = ( dir[1] * ( 16 * ratio ) ); + p->vel[2] = ( dir[2] * ( 16 * ratio ) ); +// p->vel[2] = (dir[2]); + + p->snum = snum; + + VectorClear( p->accel ); + + p->accel[2] = -20; + + p->rotate = qfalse; + + p->roll = rand() % 179; + + p->alpha = 0.5; + + p->color = BLOODRED; + +} + + +void CG_Particle_OilSlick( qhandle_t pshader, centity_t *cent ) { + cparticle_t *p; + + if ( !pshader ) { + CG_Printf( "CG_Particle_OilSlick == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + if ( cent->currentState.angles2[2] ) { + p->endtime = cg.time + cent->currentState.angles2[2]; + } else { + p->endtime = cg.time + 60000; + } + + p->startfade = p->endtime; + + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = pshader; + + if ( cent->currentState.angles2[0] || cent->currentState.angles2[1] ) { + p->width = cent->currentState.angles2[0]; + p->height = cent->currentState.angles2[0]; + + p->endheight = cent->currentState.angles2[1]; + p->endwidth = cent->currentState.angles2[1]; + } else + { + p->width = 8; + p->height = 8; + + p->endheight = 16; + p->endwidth = 16; + } + + p->type = P_FLAT_SCALEUP; + + p->snum = cent->currentState.density; + + VectorCopy( cent->currentState.origin, p->org ); + + p->org[2] += 0.55 + ( crandom() * 0.5 ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = 0; + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand() % 179; + + p->alpha = 0.75; + +} + +void CG_OilSlickRemove( centity_t *cent ) { + cparticle_t *p, *next; + int id; + + id = cent->currentState.density; + + if ( !id ) { + CG_Printf( "CG_OilSlickRevove NULL id\n" ); + } + + for ( p = active_particles ; p ; p = next ) + { + next = p->next; + + if ( p->type == P_FLAT_SCALEUP ) { + if ( p->snum == id ) { + p->endtime = cg.time + 100; + p->startfade = p->endtime; + p->type = P_FLAT_SCALEUP_FADE; + + } + } + + } +} + +qboolean ValidBloodPool( vec3_t start ) { +#define EXTRUDE_DIST 0.5 + + vec3_t angles; + vec3_t right, up; + vec3_t this_pos, x_pos, center_pos, end_pos; + float x, y; + float fwidth, fheight; + trace_t trace; + vec3_t normal; + + fwidth = 16; + fheight = 16; + + VectorSet( normal, 0, 0, 1 ); + + vectoangles( normal, angles ); + AngleVectors( angles, NULL, right, up ); + + VectorMA( start, EXTRUDE_DIST, normal, center_pos ); + + for ( x = -fwidth / 2; x < fwidth; x += fwidth ) + { + VectorMA( center_pos, x, right, x_pos ); + + for ( y = -fheight / 2; y < fheight; y += fheight ) + { + VectorMA( x_pos, y, up, this_pos ); + VectorMA( this_pos, -EXTRUDE_DIST * 2, normal, end_pos ); + + CG_Trace( &trace, this_pos, NULL, NULL, end_pos, -1, CONTENTS_SOLID ); + + + if ( trace.entityNum < ( MAX_ENTITIES - 1 ) ) { // may only land on world + return qfalse; + } + + if ( !( !trace.startsolid && trace.fraction < 1 ) ) { + return qfalse; + } + + } + } + + return qtrue; +} + +#define NORMALSIZE 16 +#define LARGESIZE 32 + +void CG_ParticleBloodCloud( centity_t *cent, vec3_t origin, vec3_t dir ) { + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + length = VectorLength( dir ); + vectoangles( dir, angles ); + AngleVectors( angles, forward, NULL, NULL ); + + if ( cent->currentState.density == 0 ) { // normal ai size + crittersize = NORMALSIZE; + } else { + crittersize = LARGESIZE; + } + + if ( length ) { + dist = length / crittersize; + } + + if ( dist < 1 ) { + dist = 1; + } + + VectorCopy( origin, point ); + + for ( i = 0; i < dist; i++ ) + { + VectorMA( point, crittersize, forward, point ); + + if ( !free_particles ) { + return; + } + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.smokePuffShader; + + p->endtime = cg.time + 350 + ( crandom() * 100 ); + + p->startfade = cg.time; + + if ( cent->currentState.density == 0 ) { // normal ai size + p->width = NORMALSIZE; + p->height = NORMALSIZE; + + p->endheight = NORMALSIZE; + p->endwidth = NORMALSIZE; + } else // large frame + { + p->width = LARGESIZE; + p->height = LARGESIZE; + + p->endheight = LARGESIZE; + p->endwidth = LARGESIZE; + } + + p->type = P_SMOKE; + + VectorCopy( origin, p->org ); + + p->vel[0] = 0; + p->vel[1] = 0; + p->vel[2] = -1; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand() % 179; + + p->color = BLOODRED; + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleBloodCloudZombie( centity_t *cent, vec3_t origin, vec3_t dir ) { + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + length = VectorLength( dir ); + vectoangles( dir, angles ); + AngleVectors( angles, forward, NULL, NULL ); + + if ( cent->currentState.density == 0 ) { // normal ai size + crittersize = NORMALSIZE / 4; + } else { + crittersize = LARGESIZE / 3; + } + + if ( length ) { + dist = length / crittersize; + } + + if ( dist < 1 ) { + dist = 1; + } + + VectorCopy( origin, point ); + + for ( i = 0; i < dist; i++ ) + { + VectorMA( point, crittersize, forward, point ); + + if ( !free_particles ) { + return; + } + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 0.2; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.bloodCloudShader; + + // RF, stay around for long enough to expand and dissipate naturally + if ( length ) { + p->endtime = cg.time + 3500 + ( crandom() * 2000 ); + } else { + p->endtime = cg.time + 750 + ( crandom() * 500 ); + } + + p->startfade = cg.time; + + if ( cent->currentState.density == 0 ) { // normal ai size + p->width = NORMALSIZE; + p->height = NORMALSIZE; + + // RF, expand while falling + p->endheight = NORMALSIZE * 4.0; + p->endwidth = NORMALSIZE * 4.0; + } else // large frame + { + p->width = LARGESIZE; + p->height = LARGESIZE; + + // RF, expand while falling + p->endheight = LARGESIZE * 3.0; + p->endwidth = LARGESIZE * 3.0; + } + + if ( !length ) { + p->width *= 0.2; + p->height *= 0.2; + + p->endheight = NORMALSIZE; + p->endwidth = NORMALSIZE; + } + + p->type = P_SMOKE; + + VectorCopy( origin, p->org ); + + p->vel[0] = crandom() * 6; + p->vel[1] = crandom() * 6; + p->vel[2] = random() * 6; + + // RF, add some gravity/randomness + p->accel[0] = crandom() * 3; + p->accel[1] = crandom() * 3; + p->accel[2] = -PARTICLE_GRAVITY * 0.2; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand() % 179; + + p->color = ZOMBIE; + + } + + +} + +void CG_ParticleSparks( vec3_t org, vec3_t vel, int duration, float x, float y, float speed ) { + cparticle_t *p; + + if ( !free_particles ) { + return; + } + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + + p->endtime = cg.time + duration; + p->startfade = cg.time + duration / 2; + + p->color = EMISIVEFADE; + p->alpha = 0.4; + p->alphavel = 0; + + p->height = 0.5; + p->width = 0.5; + p->endheight = 0.5; + p->endwidth = 0.5; + + p->pshader = cgs.media.tracerShader; + + p->type = P_SMOKE; + + VectorCopy( org, p->org ); + + p->org[0] += ( crandom() * x ); + p->org[1] += ( crandom() * y ); + + p->vel[0] = vel[0]; + p->vel[1] = vel[1]; + p->vel[2] = vel[2]; + + p->accel[0] = p->accel[1] = p->accel[2] = 0; + + p->vel[0] += ( crandom() * 4 ); + p->vel[1] += ( crandom() * 4 ); + p->vel[2] += ( 20 + ( crandom() * 10 ) ) * speed; + + p->accel[0] = crandom() * 4; + p->accel[1] = crandom() * 4; + +} + +void CG_ParticleDust( centity_t *cent, vec3_t origin, vec3_t dir ) { + float length; + float dist; + float crittersize; + vec3_t angles, forward; + vec3_t point; + cparticle_t *p; + int i; + + dist = 0; + + VectorNegate( dir, dir ); + length = VectorLength( dir ); + vectoangles( dir, angles ); + AngleVectors( angles, forward, NULL, NULL ); + + if ( cent->currentState.density == 0 ) { // normal ai size + crittersize = NORMALSIZE; + } else { + crittersize = LARGESIZE; + } + + if ( length ) { + dist = length / crittersize; + } + + if ( dist < 1 ) { + dist = 1; + } + + VectorCopy( origin, point ); + + for ( i = 0; i < dist; i++ ) + { + VectorMA( point, crittersize, forward, point ); + + if ( !free_particles ) { + return; + } + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->time = cg.time; + p->alpha = 5.0; + p->alphavel = 0; + p->roll = 0; + + p->pshader = cgs.media.bloodCloudShader; + + // RF, stay around for long enough to expand and dissipate naturally + if ( length ) { + p->endtime = cg.time + 4500 + ( crandom() * 3500 ); + } else { + p->endtime = cg.time + 750 + ( crandom() * 500 ); + } + + p->startfade = cg.time; + + if ( cent->currentState.density == 0 ) { // normal ai size + p->width = NORMALSIZE; + p->height = NORMALSIZE; + + // RF, expand while falling + p->endheight = NORMALSIZE * 4.0; + p->endwidth = NORMALSIZE * 4.0; + } else // large frame + { + p->width = LARGESIZE; + p->height = LARGESIZE; + + // RF, expand while falling + p->endheight = LARGESIZE * 3.0; + p->endwidth = LARGESIZE * 3.0; + } + + if ( !length ) { + p->width *= 0.2; + p->height *= 0.2; + + p->endheight = NORMALSIZE; + p->endwidth = NORMALSIZE; + } + + p->type = P_SMOKE; + + VectorCopy( point, p->org ); + + p->vel[0] = crandom() * 6; + p->vel[1] = crandom() * 6; + p->vel[2] = random() * 20; + + // RF, add some gravity/randomness + p->accel[0] = crandom() * 3; + p->accel[1] = crandom() * 3; + p->accel[2] = -PARTICLE_GRAVITY * 0.4; + + VectorClear( p->accel ); + + p->rotate = qfalse; + + p->roll = rand() % 179; + + if ( cent->currentState.density ) { + p->color = GREY75; + } else { + p->color = MUSTARD; + } + + p->alpha = 0.75; + + } + + +} + +void CG_ParticleMisc( qhandle_t pshader, vec3_t origin, int size, int duration, float alpha ) { + cparticle_t *p; + + if ( !pshader ) { + CG_Printf( "CG_ParticleImpactSmokePuff pshader == ZERO!\n" ); + } + + if ( !free_particles ) { + return; + } + + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + p->time = cg.time; + p->alpha = 1.0; + p->alphavel = 0; + p->roll = rand() % 179; + + p->pshader = pshader; + + if ( duration > 0 ) { + p->endtime = cg.time + duration; + } else { + p->endtime = duration; + } + + p->startfade = cg.time; + + p->width = size; + p->height = size; + + p->endheight = size; + p->endwidth = size; + + p->type = P_SPRITE; + + VectorCopy( origin, p->org ); + + p->rotate = qfalse; +} diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c new file mode 100644 index 0000000..d79f191 --- /dev/null +++ b/src/cgame/cg_players.c @@ -0,0 +1,3007 @@ +/* +=========================================================================== + +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: cg_players.c + * + * desc: handle the media and animation for player entities + * +*/ + +#include "cg_local.h" + +#define SWING_RIGHT 1 +#define SWING_LEFT 2 + +#define JUMP_HEIGHT 56 +#define SWINGSPEED 0.3 + +static int dp_realtime; +static float jumpHeight; + +animation_t *lastTorsoAnim; +animation_t *lastLegsAnim; + +extern const char* cg_skillRewards[SK_NUM_SKILLS][NUM_SKILL_LEVELS - 1]; + +/* +================ +CG_EntOnFire +================ +*/ +qboolean CG_EntOnFire( centity_t *cent ) { + if ( cent->currentState.number == cg.snap->ps.clientNum ) { + // TAT 11/15/2002 - the player is always starting out on fire, which is easily seen in cinematics + // so make sure onFireStart is not 0 + return ( cg.snap->ps.onFireStart + && ( cg.snap->ps.onFireStart < cg.time ) + && ( ( cg.snap->ps.onFireStart + 2000 ) > cg.time ) ); + } else { + return ( ( cent->currentState.onFireStart < cg.time ) && + ( cent->currentState.onFireEnd > cg.time ) ); + } +} + +/* +================ +CG_IsCrouchingAnim +================ +*/ +qboolean CG_IsCrouchingAnim( animModelInfo_t* animModelInfo, int animNum ) { + animation_t *anim; + + // FIXME: make compatible with new scripting + animNum &= ~ANIM_TOGGLEBIT; + // + anim = BG_GetAnimationForIndex( animModelInfo, animNum ); + // + if ( anim->movetype & ( ( 1 << ANIM_MT_IDLECR ) | ( 1 << ANIM_MT_WALKCR ) | ( 1 << ANIM_MT_WALKCRBK ) ) ) { + return qtrue; + } + // + return qfalse; +} + +/* +================ +CG_CustomSound +================ +*/ +sfxHandle_t CG_CustomSound( int clientNum, const char *soundName ) { + if ( soundName[0] != '*' ) { + return trap_S_RegisterSound( soundName, qfalse ); + } + + return 0; +} + +/* +============================================================================= + +CLIENT INFO + +============================================================================= +*/ + +/* +=================== +CG_LoadClientInfo + +Load it now, taking the disk hits. +This will usually be deferred to a safe time +=================== +*/ +static void CG_LoadClientInfo( int clientNum ) { + int i; + + // reset any existing players and bodies, because they might be in bad + // frames for this new model + for ( i = 0 ; i < MAX_GENTITIES ; i++ ) { + if ( cg_entities[i].currentState.clientNum == clientNum && cg_entities[i].currentState.eType == ET_PLAYER ) { + CG_ResetPlayerEntity( &cg_entities[i] ); + } + } +} + +void CG_ParseTeamXPs( int n ) { + int i, j; + char* cs = (char*)CG_ConfigString( CS_AXIS_MAPS_XP + n ); + const char* token; + + for ( i = 0; i < MAX_MAPS_PER_CAMPAIGN; i++ ) { + for ( j = 0; j < SK_NUM_SKILLS; j++ ) { + token = COM_ParseExt( &cs, qfalse ); + + if ( !token || !*token ) { + return; + } + + if ( n == 0 ) { + cgs.tdbAxisMapsXP[ j ][ i ] = atoi( token ); + } else { + cgs.tdbAlliedMapsXP[ j ][ i ] = atoi( token ); + } + } + } +} + +void CG_LimboPanel_SendSetupMsg( qboolean forceteam ); + +/* +====================== +CG_NewClientInfo +====================== +*/ +void CG_NewClientInfo( int clientNum ) { + clientInfo_t *ci; + clientInfo_t newInfo; + const char *configstring; + const char *v; + int oldclass; + + ci = &cgs.clientinfo[clientNum]; + + configstring = CG_ConfigString( clientNum + CS_PLAYERS ); + if ( !*configstring ) { + memset( ci, 0, sizeof( *ci ) ); + return; // player just left + } + + // build into a temp buffer so the defer checks can use + // the old value + memset( &newInfo, 0, sizeof( newInfo ) ); + + // Gordon: grabbing some older stuff, if it's a new client, tinfo will update within one second anyway, otherwise you get the health thing flashing red + // NOTE: why are we bothering to do all this setting up of a new clientInfo_t anyway? it was all for deffered clients iirc, which we dont have + newInfo.location[0] = ci->location[0]; + newInfo.location[1] = ci->location[1]; + newInfo.health = ci->health; + newInfo.fireteamData = ci->fireteamData; + newInfo.clientNum = clientNum; + newInfo.selected = ci->selected; + newInfo.totalWeapAcc = ci->totalWeapAcc; + + // isolate the player's name + v = Info_ValueForKey( configstring, "n" ); + Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) ); + Q_strncpyz( newInfo.cleanname, v, sizeof( newInfo.cleanname ) ); + Q_CleanStr( newInfo.cleanname ); + + + // bot skill + v = Info_ValueForKey( configstring, "skill" ); + newInfo.botSkill = atoi( v ); + + // team + v = Info_ValueForKey( configstring, "t" ); + newInfo.team = atoi( v ); + + // class + v = Info_ValueForKey( configstring, "c" ); + newInfo.cls = atoi( v ); + + // rank + v = Info_ValueForKey( configstring, "r" ); + newInfo.rank = atoi( v ); + + v = Info_ValueForKey( configstring, "f" ); + newInfo.fireteam = atoi( v ); + + v = Info_ValueForKey( configstring, "m" ); + if ( *v ) { + int i; + char buf[2]; + buf[1] = '\0'; + for ( i = 0; i < SK_NUM_SKILLS; i++ ) { + buf[0] = *v; + newInfo.medals[i] = atoi( buf ); + v++; + } + } + + v = Info_ValueForKey( configstring, "ch" ); + if ( *v ) { + newInfo.character = cgs.gameCharacters[atoi( v )]; + } + + v = Info_ValueForKey( configstring, "s" ); + if ( *v ) { + int i; + + for ( i = 0; i < SK_NUM_SKILLS; i++ ) { + char skill[2]; + + skill[0] = v[i]; + skill[1] = '\0'; + + newInfo.skill[i] = atoi( skill ); + } + } + + // diguiseName + v = Info_ValueForKey( configstring, "dn" ); + Q_strncpyz( newInfo.disguiseName, v, sizeof( newInfo.disguiseName ) ); + + // disguiseRank + v = Info_ValueForKey( configstring, "dr" ); + newInfo.disguiseRank = atoi( v ); + + // Gordon: weapon and latchedweapon ( FIXME: make these more secure ) + v = Info_ValueForKey( configstring, "w" ); + newInfo.weapon = atoi( v ); + + v = Info_ValueForKey( configstring, "lw" ); + newInfo.latchedweapon = atoi( v ); + + v = Info_ValueForKey( configstring, "sw" ); + newInfo.secondaryweapon = atoi( v ); + + v = Info_ValueForKey( configstring, "ref" ); + newInfo.refStatus = atoi( v ); + + // Gordon: detect rank/skill changes client side + if ( clientNum == cg.clientNum ) { + int i; + + if ( newInfo.team != cgs.clientinfo[ cg.clientNum ].team ) { + if ( cgs.autoFireteamCreateEndTime != cg.time + 20000 ) { + cgs.autoFireteamCreateEndTime = 0; + } + if ( cgs.autoFireteamJoinEndTime != cg.time + 20000 ) { + cgs.autoFireteamJoinEndTime = 0; + } + } + + if ( newInfo.rank > cgs.clientinfo[ cg.clientNum ].rank ) { + + CG_SoundPlaySoundScript( cgs.clientinfo[cg.clientNum].team == TEAM_ALLIES ? rankSoundNames_Allies[ newInfo.rank ] : rankSoundNames_Axis[ newInfo.rank ], NULL, -1, qtrue ); + + CG_AddPMItemBig( PM_RANK, va( "Promoted to rank %s!", cgs.clientinfo[ cg.clientNum ].team == TEAM_AXIS ? rankNames_Axis[newInfo.rank] : rankNames_Allies[newInfo.rank] ), rankicons[ newInfo.rank ][ 0 ].shader ); + } + + for ( i = 0; i < SK_NUM_SKILLS; i++ ) { + if ( newInfo.skill[i] > cgs.clientinfo[ cg.clientNum ].skill[i] ) { + // Gordon: slick hack so that funcs we call use teh new value now + cgs.clientinfo[ cg.clientNum ].skill[ i ] = newInfo.skill[ i ]; + + if ( newInfo.skill[i] == 4 && i == SK_HEAVY_WEAPONS ) { + if ( cgs.clientinfo[ cg.clientNum ].skill[SK_LIGHT_WEAPONS] == 4 ) { + oldclass = cgs.ccSelectedClass; + cgs.ccSelectedClass = newInfo.cls; + CG_LimboPanel_SetSelectedWeaponNumForSlot( 1, 2 ); + CG_LimboPanel_SendSetupMsg( qfalse ); + cgs.ccSelectedClass = oldclass; + } else { + oldclass = cgs.ccSelectedClass; + cgs.ccSelectedClass = newInfo.cls; + CG_LimboPanel_SetSelectedWeaponNumForSlot( 1, 1 ); + CG_LimboPanel_SendSetupMsg( qfalse ); + cgs.ccSelectedClass = oldclass; + } + } + + if ( newInfo.skill[i] == 4 && i == SK_LIGHT_WEAPONS ) { + if ( cgs.clientinfo[ cg.clientNum ].skill[SK_HEAVY_WEAPONS] == 4 ) { + if ( cgs.ccSelectedWeapon2 == 2 ) { + oldclass = cgs.ccSelectedClass; + cgs.ccSelectedClass = newInfo.cls; + CG_LimboPanel_SetSelectedWeaponNumForSlot( 1, 3 ); + CG_LimboPanel_SendSetupMsg( qfalse ); + cgs.ccSelectedClass = oldclass; + } + } else { + oldclass = cgs.ccSelectedClass; + cgs.ccSelectedClass = newInfo.cls; + CG_LimboPanel_SetSelectedWeaponNumForSlot( 1, 1 ); + CG_LimboPanel_SendSetupMsg( qfalse ); + cgs.ccSelectedClass = oldclass; + } + } + + CG_AddPMItemBig( PM_SKILL, va( "Increased %s skill to level %i!", skillNames[i], newInfo.skill[i] ), cgs.media.skillPics[ i ] ); + + CG_PriorityCenterPrint( va( "You have been rewarded with %s", cg_skillRewards[ i ][ newInfo.skill[i] - 1 ] ), SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.20 ), SMALLCHAR_WIDTH, 99999 ); + } + } + + if ( newInfo.team != cgs.clientinfo[ cg.clientNum ].team ) { + // clear these + memset( cg.artilleryRequestPos, 0, sizeof( cg.artilleryRequestPos ) ); + memset( cg.artilleryRequestTime, 0, sizeof( cg.artilleryRequestTime ) ); + } + + trap_Cvar_Set( "authLevel", va( "%i", newInfo.refStatus ) ); + + if ( newInfo.refStatus != ci->refStatus ) { + if ( newInfo.refStatus <= RL_NONE ) { + const char *info = CG_ConfigString( CS_SERVERINFO ); + + trap_Cvar_Set( "cg_ui_voteFlags", Info_ValueForKey( info, "voteFlags" ) ); + CG_Printf( "[cgnotify]^3*** You have been stripped of your referee status! ***\n" ); + + } else { + trap_Cvar_Set( "cg_ui_voteFlags", "0" ); + CG_Printf( "[cgnotify]^2*** You have been authorized \"%s\" status ***\n", ( ( newInfo.refStatus == RL_RCON ) ? "rcon" : "referee" ) ); + CG_Printf( "Type: ^3ref^7 (by itself) for a list of referee commands.\n" ); + } + } + } + + + // rain - passing the clientNum since that's all we need, and we + // can't calculate it properly from the clientinfo + CG_LoadClientInfo( clientNum ); + + // replace whatever was there with the new one + newInfo.infoValid = qtrue; + *ci = newInfo; + + // make sure we have a character set + if ( !ci->character ) { + ci->character = BG_GetCharacter( ci->team, ci->cls ); + } + + // Gordon: need to resort the fireteam list, incase ranks etc have changed + CG_SortClientFireteam(); +} + +/* +============================================================================= + +PLAYER ANIMATION + +============================================================================= +*/ + +bg_playerclass_t* CG_PlayerClassForClientinfo( clientInfo_t *ci, centity_t* cent ) { + int team, cls; + + if ( cent && cent->currentState.eType == ET_CORPSE ) { + return BG_GetPlayerClassInfo( cent->currentState.modelindex, cent->currentState.modelindex2 ); + } + + if ( cent && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) { + team = ci->team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + + // rain - fixed incorrect class determination (was & 6, + // should be & 7) + cls = ( cent->currentState.powerups >> PW_OPS_CLASS_1 ) & 7; + + return BG_GetPlayerClassInfo( team, cls ); + } + + return BG_GetPlayerClassInfo( ci->team, ci->cls ); +} + +/* +=============== +CG_SetLerpFrameAnimation + +may include ANIM_TOGGLEBIT +=============== +*/ +static void CG_SetLerpFrameAnimation( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + bg_character_t* character = CG_CharacterForClientinfo( ci, cent ); + + if ( !character ) { + return; + } + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= character->animModelInfo->numAnimations ) { + CG_Error( "CG_SetLerpFrameAnimation: Bad animation number: %i", newAnimation ); + } + + anim = character->animModelInfo->animations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; + + if ( cg_debugAnim.integer == 1 ) { + CG_Printf( "Anim: %i, %s\n", newAnimation, character->animModelInfo->animations[newAnimation]->name ); + } +} + +/* +=============== +CG_RunLerpFrame + +Sets cg.snap, cg.oldFrame, and cg.backlerp +cg.time should be between oldFrameTime and frameTime after exit +=============== +*/ +void CG_RunLerpFrame( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) { + int f; + animation_t *anim; + + // debugging tool to get no animations + if ( cg_animSpeed.integer == 0 ) { + lf->oldFrame = lf->frame = lf->backlerp = 0; + return; + } + + // see if the animation sequence is switching + if ( ci && ( newAnimation != lf->animationNumber || !lf->animation ) ) { + CG_SetLerpFrameAnimation( cent, ci, lf, newAnimation ); + } + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( cg.time >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + lf->oldFrameModel = lf->frameModel; + + // get the next frame based on the animation + anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } + if ( cg.time < lf->animationTime ) { + lf->frameTime = lf->animationTime; // initial lerp + } else { + lf->frameTime = lf->oldFrameTime + anim->frameLerp; + } + f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + f *= speedScale; // adjust for haste, etc + if ( f >= anim->numFrames ) { + f -= anim->numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = anim->numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = cg.time; + } + } + lf->frame = anim->firstFrame + f; + lf->frameModel = anim->mdxFile; + + if ( cg.time > lf->frameTime ) { + lf->frameTime = cg.time; + if ( cg_debugAnim.integer ) { + CG_Printf( "Clamp lf->frameTime\n" ); + } + } + } + + if ( lf->frameTime > cg.time + 200 ) { + lf->frameTime = cg.time; + } + + if ( lf->oldFrameTime > cg.time ) { + lf->oldFrameTime = cg.time; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + + +/* +=============== +CG_ClearLerpFrame +=============== +*/ +static void CG_ClearLerpFrame( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) { + lf->frameTime = lf->oldFrameTime = cg.time; + CG_SetLerpFrameAnimation( cent, ci, lf, animationNumber ); + if ( lf->animation ) { + lf->oldFrame = lf->frame = lf->animation->firstFrame; + lf->oldFrameModel = lf->frameModel = lf->animation->mdxFile; + } +} + +/* +=============== +CG_SetLerpFrameAnimationRate + +may include ANIM_TOGGLEBIT +=============== +*/ +void CG_SetLerpFrameAnimationRate( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim, *oldanim; + int transitionMin = -1, oldAnimTime, oldAnimNum; + qboolean firstAnim = qfalse; + + bg_character_t *character = CG_CharacterForClientinfo( ci, cent ); + + if ( !character ) { + return; + } + + oldAnimTime = lf->animationTime; + oldanim = lf->animation; + oldAnimNum = lf->animationNumber; + + if ( !lf->animation ) { + firstAnim = qtrue; + } + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= character->animModelInfo->numAnimations ) { + CG_Error( "CG_SetLerpFrameAnimationRate: Bad animation number: %i", newAnimation ); + } + + anim = character->animModelInfo->animations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; + + if ( !( anim->flags & ANIMFL_FIRINGANIM ) || ( lf != ¢->pe.torso ) ) { + if ( ( lf == ¢->pe.legs ) && ( CG_IsCrouchingAnim( character->animModelInfo, newAnimation ) != CG_IsCrouchingAnim( character->animModelInfo, oldAnimNum ) ) ) { + if ( anim->moveSpeed || ( anim->movetype & ( ( 1 << ANIM_MT_TURNLEFT ) | ( 1 << ANIM_MT_TURNRIGHT ) ) ) ) { // if unknown movetype, go there faster + transitionMin = lf->frameTime + 200; // slowly raise/drop + } else { + transitionMin = lf->frameTime + 350; // slowly raise/drop + } + } else if ( anim->moveSpeed ) { + transitionMin = lf->frameTime + 120; // always do some lerping (?) + } else { // not moving, so take your time + transitionMin = lf->frameTime + 170; // always do some lerping (?) + + } + if ( oldanim && oldanim->animBlend ) { //transitionMin < lf->frameTime + oldanim->animBlend) { + transitionMin = lf->frameTime + oldanim->animBlend; + lf->animationTime = transitionMin; + } else { + // slow down transitions according to speed + if ( anim->moveSpeed && lf->animSpeedScale < 1.0 ) { + lf->animationTime += anim->initialLerp; + } + + if ( lf->animationTime < transitionMin ) { + lf->animationTime = transitionMin; + } + } + } + + // if first anim, go immediately + if ( firstAnim ) { + lf->frameTime = cg.time - 1; + lf->animationTime = cg.time - 1; + lf->frame = anim->firstFrame; + lf->frameModel = anim->mdxFile; + } + + if ( cg_debugAnim.integer == 1 ) { // DHM - Nerve :: extra debug info + CG_Printf( "Anim: %i, %s\n", newAnimation, character->animModelInfo->animations[newAnimation]->name ); + } +} + +/* +=============== +CG_RunLerpFrameRate + +Sets cg.snap, cg.oldFrame, and cg.backlerp +cg.time should be between oldFrameTime and frameTime after exit +=============== +*/ +void CG_RunLerpFrameRate( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, centity_t *cent, int recursion ) { + int f; + animation_t *anim, *oldAnim; + animation_t *otherAnim = NULL; + qboolean isLadderAnim; + +#define ANIM_SCALEMAX_LOW 1.1 +#define ANIM_SCALEMAX_HIGH 1.6 + +#define ANIM_SPEEDMAX_LOW 100 +#define ANIM_SPEEDMAX_HIGH 20 + + // debugging tool to get no animations + if ( cg_animSpeed.integer == 0 ) { + lf->oldFrame = lf->frame = lf->backlerp = 0; + return; + } + + isLadderAnim = lf->animation && ( lf->animation->flags & ANIMFL_LADDERANIM ); + + oldAnim = lf->animation; + + // see if the animation sequence is switching + if ( newAnimation != lf->animationNumber || !lf->animation ) { + CG_SetLerpFrameAnimationRate( cent, ci, lf, newAnimation ); + } + + // Ridah, make sure the animation speed is updated when possible + anim = lf->animation; + + // check for forcing last frame + if ( cent->currentState.eFlags & EF_FORCE_END_FRAME + // xkan, 12/27/2002 - In SP, corpse also stays at the last frame (of the death animation) + // so that the death animation can end up in different positions + // and the body will stay in that position + || ( /*CG_IsSinglePlayer() &&*/ cent->currentState.eType == ET_CORPSE ) ) { + lf->oldFrame = lf->frame = anim->firstFrame + anim->numFrames - 1; + lf->oldFrameModel = lf->frameModel = anim->mdxFile; + lf->backlerp = 0; + return; + } + + if ( anim->moveSpeed && lf->oldFrameSnapshotTime ) { + float moveSpeed; + + // calculate the speed at which we moved over the last frame + if ( cg.latestSnapshotTime != lf->oldFrameSnapshotTime && cg.nextSnap ) { + if ( cent->currentState.number == cg.snap->ps.clientNum ) { + if ( isLadderAnim ) { // only use Z axis for speed + lf->oldFramePos[0] = cent->lerpOrigin[0]; + lf->oldFramePos[1] = cent->lerpOrigin[1]; + } else { // only use x/y axis + lf->oldFramePos[2] = cent->lerpOrigin[2]; + } + moveSpeed = Distance( cent->lerpOrigin, lf->oldFramePos ) / ( (float)( cg.time - lf->oldFrameTime ) / 1000.0 ); + } else { + if ( isLadderAnim ) { // only use Z axis for speed + lf->oldFramePos[0] = cent->currentState.pos.trBase[0]; + lf->oldFramePos[1] = cent->currentState.pos.trBase[1]; + } + moveSpeed = Distance( cent->lerpOrigin, lf->oldFramePos ) / ( (float)( cg.time - lf->oldFrameTime ) / 1000.0 ); + } + // + // convert it to a factor of this animation's movespeed + lf->animSpeedScale = moveSpeed / (float)anim->moveSpeed; + lf->oldFrameSnapshotTime = cg.latestSnapshotTime; + } + } else { + // move at normal speed + lf->animSpeedScale = 1.0; + lf->oldFrameSnapshotTime = cg.latestSnapshotTime; + } + // adjust with manual setting (pain anims) + lf->animSpeedScale *= cent->pe.animSpeed; + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( cg.time >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + lf->oldFrameModel = lf->frameModel; + VectorCopy( cent->lerpOrigin, lf->oldFramePos ); + + // restrict the speed range + if ( lf->animSpeedScale < 0.25 ) { // if it's too slow, then a really slow spped, combined with a sudden take-off, can leave them playing a really slow frame while they a moving really fast + if ( lf->animSpeedScale < 0.01 && isLadderAnim ) { + lf->animSpeedScale = 0.0; + } else { + lf->animSpeedScale = 0.25; + } + } else if ( lf->animSpeedScale > ANIM_SCALEMAX_LOW ) { + + if ( !( anim->flags & ANIMFL_LADDERANIM ) ) { + // allow slower anims to speed up more than faster anims + if ( anim->moveSpeed > ANIM_SPEEDMAX_LOW ) { + lf->animSpeedScale = ANIM_SCALEMAX_LOW; + } else if ( anim->moveSpeed < ANIM_SPEEDMAX_HIGH ) { + if ( lf->animSpeedScale > ANIM_SCALEMAX_HIGH ) { + lf->animSpeedScale = ANIM_SCALEMAX_HIGH; + } + } else { + lf->animSpeedScale = ANIM_SCALEMAX_HIGH - ( ANIM_SCALEMAX_HIGH - ANIM_SCALEMAX_LOW ) * (float)( anim->moveSpeed - ANIM_SPEEDMAX_HIGH ) / (float)( ANIM_SPEEDMAX_LOW - ANIM_SPEEDMAX_HIGH ); + } + } else if ( lf->animSpeedScale > 4.0 ) { + lf->animSpeedScale = 4.0; + } + + } + + if ( lf == ¢->pe.legs ) { + otherAnim = cent->pe.torso.animation; + } else if ( lf == ¢->pe.torso ) { + otherAnim = cent->pe.legs.animation; + } + + // get the next frame based on the animation + if ( !lf->animSpeedScale ) { + // stopped on the ladder, so stay on the same frame + f = lf->frame - anim->firstFrame; + lf->frameTime += anim->frameLerp; // don't wait too long before starting to move again + } else if ( lf->oldAnimationNumber != lf->animationNumber && + ( !anim->moveSpeed || lf->oldFrame < anim->firstFrame || lf->oldFrame >= anim->firstFrame + anim->numFrames ) ) { // Ridah, added this so walking frames don't always get reset to 0, which can happen in the middle of a walking anim, which looks wierd + lf->frameTime = lf->animationTime; // initial lerp + if ( oldAnim && anim->moveSpeed ) { // keep locomotions going continuously + f = ( lf->frame - oldAnim->firstFrame ) + 1; + while ( f < 0 ) { + f += anim->numFrames; + } + } else { + f = 0; + } + } else if ( ( lf == ¢->pe.legs ) && otherAnim && !( anim->flags & ANIMFL_FIRINGANIM ) && ( ( lf->animationNumber & ~ANIM_TOGGLEBIT ) == ( cent->pe.torso.animationNumber & ~ANIM_TOGGLEBIT ) ) && ( !anim->moveSpeed ) ) { + // legs should synch with torso + f = cent->pe.torso.frame - otherAnim->firstFrame; + if ( f >= anim->numFrames || f < 0 ) { + f = 0; // wait at the start for the legs to catch up (assuming they are still in an old anim) + } + lf->frameTime = cent->pe.torso.frameTime; + } else if ( ( lf == ¢->pe.torso ) && otherAnim && !( anim->flags & ANIMFL_FIRINGANIM ) && ( ( lf->animationNumber & ~ANIM_TOGGLEBIT ) == ( cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT ) ) && ( otherAnim->moveSpeed ) ) { + // torso needs to sync with legs + f = cent->pe.legs.frame - otherAnim->firstFrame; + if ( f >= anim->numFrames || f < 0 ) { + f = 0; // wait at the start for the legs to catch up (assuming they are still in an old anim) + } + lf->frameTime = cent->pe.legs.frameTime; + } else { + lf->frameTime = lf->oldFrameTime + (int)( (float)anim->frameLerp * ( 1.0 / lf->animSpeedScale ) ); + if ( lf->frameTime < cg.time ) { + lf->frameTime = cg.time; + } + + // check for skipping frames (eg. death anims play in slo-mo if low framerate) + if ( anim->flags & ANIMFL_REVERSED ) { + if ( cg.time > lf->frameTime && !anim->moveSpeed ) { + f = ( anim->numFrames - 1 ) - ( ( lf->frame - anim->firstFrame ) - ( 1 + ( cg.time - lf->frameTime ) / anim->frameLerp ) ); + } else { + f = ( anim->numFrames - 1 ) - ( ( lf->frame - anim->firstFrame ) - 1 ); + } + } else { + if ( cg.time > lf->frameTime && !anim->moveSpeed ) { + f = ( lf->frame - anim->firstFrame ) + 1 + ( cg.time - lf->frameTime ) / anim->frameLerp; + } else { + f = ( lf->frame - anim->firstFrame ) + 1; + } + } + + if ( f < 0 ) { + f = 0; + } + } + //f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + if ( f >= anim->numFrames ) { + f -= anim->numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = anim->numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = cg.time; + } + } + if ( anim->flags & ANIMFL_REVERSED ) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - f; + lf->frameModel = anim->mdxFile; + } else { + lf->frame = anim->firstFrame + f; + lf->frameModel = anim->mdxFile; + } + + if ( cg.time > lf->frameTime ) { + + // Ridah, run the frame again until we move ahead of the current time, fixes walking speeds for zombie + if ( /*!anim->moveSpeed ||*/ recursion > 4 ) { + lf->frameTime = cg.time; + } else { + CG_RunLerpFrameRate( ci, lf, newAnimation, cent, recursion + 1 ); + } + + if ( cg_debugAnim.integer > 3 ) { + CG_Printf( "Clamp lf->frameTime\n" ); + } + } + lf->oldAnimationNumber = lf->animationNumber; + } + + // Gordon: BIG hack, occaisionaly (VERY occaisionally), the frametime gets totally wacked + if ( lf->frameTime > cg.time + 5000 ) { + lf->frameTime = cg.time; + } + + if ( lf->oldFrameTime > cg.time ) { + lf->oldFrameTime = cg.time; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + +/* +=============== +CG_ClearLerpFrameRate +=============== +*/ +void CG_ClearLerpFrameRate( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) { + lf->frameTime = lf->oldFrameTime = cg.time; + CG_SetLerpFrameAnimationRate( cent, ci, lf, animationNumber ); + if ( lf->animation ) { + lf->oldFrame = lf->frame = lf->animation->firstFrame; + lf->oldFrameModel = lf->frameModel = lf->animation->mdxFile; + } +} + +/* +=============== +CG_PlayerAnimation +=============== +*/ +static void CG_PlayerAnimation( centity_t *cent, refEntity_t *body ) { + clientInfo_t *ci; + int clientNum; + int animIndex, tempIndex; + bg_character_t *character; + + clientNum = cent->currentState.clientNum; + + ci = &cgs.clientinfo[ clientNum ]; + character = CG_CharacterForClientinfo( ci, cent ); + + if ( !character ) { + return; + } + + if ( cg_noPlayerAnims.integer ) { + body->frame = body->oldframe = body->torsoFrame = body->oldTorsoFrame = 0; + body->frameModel = body->oldframeModel = body->torsoFrameModel = body->oldTorsoFrameModel = character->animModelInfo->animations[0]->mdxFile; + return; + } + + // default to whatever the legs are currently doing + animIndex = cent->currentState.legsAnim; + + // do the shuffle turn frames locally + if ( !( cent->currentState.eFlags & EF_DEAD ) && cent->pe.legs.yawing ) { + //CG_Printf("turn: %i\n", cg.time ); + tempIndex = BG_GetAnimScriptAnimation( clientNum, character->animModelInfo, cent->currentState.aiState, ( cent->pe.legs.yawing == SWING_RIGHT ? ANIM_MT_TURNRIGHT : ANIM_MT_TURNLEFT ) ); + if ( tempIndex > -1 ) { + animIndex = tempIndex; + } + } + // run the animation + CG_RunLerpFrameRate( ci, ¢->pe.legs, animIndex, cent, 0 ); + + body->oldframe = cent->pe.legs.oldFrame; + body->frame = cent->pe.legs.frame; + body->backlerp = cent->pe.legs.backlerp; + body->frameModel = cent->pe.legs.frameModel; + body->oldframeModel = cent->pe.legs.oldFrameModel; + + CG_RunLerpFrameRate( ci, ¢->pe.torso, cent->currentState.torsoAnim, cent, 0 ); + + body->oldTorsoFrame = cent->pe.torso.oldFrame; + body->torsoFrame = cent->pe.torso.frame; + body->torsoBacklerp = cent->pe.torso.backlerp; + body->torsoFrameModel = cent->pe.torso.frameModel; + body->oldTorsoFrameModel = cent->pe.torso.oldFrameModel; +} + +/* +============================================================================= + +PLAYER ANGLES + +============================================================================= +*/ + +/* +================== +CG_SwingAngles +================== +*/ +static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance, + float speed, float *angle, qboolean *swinging ) { + float swing; + float move; + float scale; + + if ( !*swinging ) { + // see if a swing should be started + swing = AngleSubtract( *angle, destination ); + if ( swing > swingTolerance || swing < -swingTolerance ) { + *swinging = qtrue; + } + } + + if ( !*swinging ) { + return; + } + + // modify the speed depending on the delta + // so it doesn't seem so linear + swing = AngleSubtract( destination, *angle ); + scale = fabs( swing ); + scale *= 0.05; + if ( scale < 0.5 ) { + scale = 0.5; + } + + // swing towards the destination angle + if ( swing >= 0 ) { + move = cg.frametime * scale * speed; + if ( move >= swing ) { + move = swing; + *swinging = qfalse; + } else { + *swinging = SWING_LEFT; // left + } + *angle = AngleMod( *angle + move ); + } else if ( swing < 0 ) { + move = cg.frametime * scale * -speed; + if ( move <= swing ) { + move = swing; + *swinging = qfalse; + } else { + *swinging = SWING_RIGHT; // right + } + *angle = AngleMod( *angle + move ); + } + + // clamp to no more than tolerance + swing = AngleSubtract( destination, *angle ); + if ( swing > clampTolerance ) { + *angle = AngleMod( destination - ( clampTolerance - 1 ) ); + } else if ( swing < -clampTolerance ) { + *angle = AngleMod( destination + ( clampTolerance - 1 ) ); + } +} + +/* +================= +CG_AddPainTwitch +================= +*/ +static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) { + int t; + float f; + int duration; + float direction; + + if ( !cent->pe.animSpeed ) { + // we need to inititialize this stuff + cent->pe.painAnimLegs = -1; + cent->pe.painAnimTorso = -1; + cent->pe.animSpeed = 1.0; + } + + if ( cent->currentState.eFlags & EF_DEAD ) { + cent->pe.painAnimLegs = -1; + cent->pe.painAnimTorso = -1; + cent->pe.animSpeed = 1.0; + return; + } + + if ( cent->pe.painDuration ) { + duration = cent->pe.painDuration; + } else { + duration = PAIN_TWITCH_TIME; + } + direction = (float)duration * 0.085; + if ( direction > 30 ) { + direction = 30; + } + if ( direction < 10 ) { + direction = 10; + } + direction *= (float)( cent->pe.painDirection * 2 ) - 1; + + t = cg.time - cent->pe.painTime; + if ( t >= duration ) { + return; + } + + f = 1.0 - (float)t / duration; + if ( cent->pe.painDirection ) { + torsoAngles[ROLL] += 20 * f; + } else { + torsoAngles[ROLL] -= 20 * f; + } +} + +/* +=============== +CG_PlayerAngles + +Handles seperate torso motion + + legs pivot based on direction of movement + + head always looks exactly at cent->lerpAngles + + if motion < 20 degrees, show in head only + if < 45 degrees, also show in torso +=============== +*/ +static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { + vec3_t legsAngles, torsoAngles, headAngles; + float dest; + vec3_t velocity; + float speed; + float clampTolerance; + int legsSet, torsoSet; + clientInfo_t *ci; + bg_character_t *character; + + ci = &cgs.clientinfo[ cent->currentState.clientNum ]; + + character = CG_CharacterForClientinfo( ci, cent ); + + if ( !character ) { + return; + } + + legsSet = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; + torsoSet = cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT; + + VectorCopy( cent->lerpAngles, headAngles ); + headAngles[YAW] = AngleMod( headAngles[YAW] ); + VectorClear( legsAngles ); + VectorClear( torsoAngles ); + + // --------- yaw ------------- + + // allow yaw to drift a bit, unless these conditions don't allow them + if ( !( BG_GetConditionBitFlag( cent->currentState.clientNum, ANIM_COND_MOVETYPE, ANIM_MT_IDLE ) || + BG_GetConditionBitFlag( cent->currentState.clientNum, ANIM_COND_MOVETYPE, ANIM_MT_IDLECR ) )/* + || (BG_GetConditionValue( cent->currentState.clientNum, ANIM_COND_MOVETYPE, qfalse ) & ((1<pe.torso.yawing = qtrue; // always center + cent->pe.torso.pitching = qtrue; // always center + cent->pe.legs.yawing = qtrue; // always center + + // if firing, make sure torso and head are always aligned + } else if ( BG_GetConditionValue( cent->currentState.clientNum, ANIM_COND_FIRING, qtrue ) ) { + cent->pe.torso.yawing = qtrue; // always center + cent->pe.torso.pitching = qtrue; // always center + } + + // adjust legs for movement dir + if ( cent->currentState.eFlags & EF_DEAD || cent->currentState.eFlags & EF_MOUNTEDTANK ) { + // don't let dead bodies twitch + legsAngles[YAW] = headAngles[YAW]; + torsoAngles[YAW] = headAngles[YAW]; + } else { + legsAngles[YAW] = headAngles[YAW] + cent->currentState.angles2[YAW]; + + if ( !( cent->currentState.eFlags & EF_FIRING ) ) { + torsoAngles[YAW] = headAngles[YAW] + 0.35 * cent->currentState.angles2[YAW]; + clampTolerance = 90; + } else { // must be firing + torsoAngles[YAW] = headAngles[YAW]; // always face firing direction + //if (fabs(cent->currentState.angles2[YAW]) > 30) + // legsAngles[YAW] = headAngles[YAW]; + clampTolerance = 60; + } + + // torso + CG_SwingAngles( torsoAngles[YAW], 25, clampTolerance, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing ); + + // if the legs are yawing (facing heading direction), allow them to rotate a bit, so we don't keep calling + // the legs_turn animation while an AI is firing, and therefore his angles will be randomizing according to their accuracy + + clampTolerance = 150; + + if ( BG_GetConditionBitFlag( ci->clientNum, ANIM_COND_MOVETYPE, ANIM_MT_IDLE ) ) { + cent->pe.legs.yawing = qfalse; // set it if they really need to swing + CG_SwingAngles( legsAngles[YAW], 20, clampTolerance, 0.5 * cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); + } else if ( strstr( BG_GetAnimString( character->animModelInfo, legsSet ), "strafe" ) ) { + // FIXME: what is this strstr hack?? + //if ( BG_GetConditionValue( ci->clientNum, ANIM_COND_MOVETYPE, qfalse ) & ((1<pe.legs.yawing = qfalse; // set it if they really need to swing + legsAngles[YAW] = headAngles[YAW]; + CG_SwingAngles( legsAngles[YAW], 0, clampTolerance, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); + } else if ( cent->pe.legs.yawing ) { + CG_SwingAngles( legsAngles[YAW], 0, clampTolerance, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); + } else { + CG_SwingAngles( legsAngles[YAW], 40, clampTolerance, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing ); + } + + torsoAngles[YAW] = cent->pe.torso.yawAngle; + legsAngles[YAW] = cent->pe.legs.yawAngle; + } + + // --------- pitch ------------- + + // only show a fraction of the pitch angle in the torso + if ( headAngles[PITCH] > 180 ) { + dest = ( -360 + headAngles[PITCH] ) * 0.75; + } else { + dest = headAngles[PITCH] * 0.75; + } + //CG_SwingAngles( dest, 15, 30, 0.1, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching ); + //torsoAngles[PITCH] = cent->pe.torso.pitchAngle; + + if ( cent->currentState.eFlags & EF_PRONE ) { + torsoAngles[PITCH] = legsAngles[PITCH] - 3; + } else { + CG_SwingAngles( dest, 15, 30, 0.1, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching ); + torsoAngles[PITCH] = cent->pe.torso.pitchAngle; + } + + + // --------- roll ------------- + + + // lean towards the direction of travel + VectorCopy( cent->currentState.pos.trDelta, velocity ); + speed = VectorNormalize( velocity ); + if ( speed ) { + vec3_t axis[3]; + float side; + + speed *= 0.05; + + AnglesToAxis( legsAngles, axis ); + side = speed * DotProduct( velocity, axis[1] ); + legsAngles[ROLL] -= side; + + side = speed * DotProduct( velocity, axis[0] ); + legsAngles[PITCH] += side; + } + + // pain twitch + CG_AddPainTwitch( cent, torsoAngles ); + + // pull the angles back out of the hierarchial chain + AnglesSubtract( headAngles, torsoAngles, headAngles ); + AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); + AnglesToAxis( legsAngles, legs ); + AnglesToAxis( torsoAngles, torso ); + AnglesToAxis( headAngles, head ); +} + +/* +============== +CG_BreathPuffs +============== +*/ +static void CG_BreathPuffs( centity_t *cent, refEntity_t *head ) { + clientInfo_t *ci; + vec3_t up, forward; + int contents; + vec3_t mang, morg, maxis[3]; + + ci = &cgs.clientinfo[ cent->currentState.number ]; + + if ( !cg_enableBreath.integer ) { + return; + } + + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { + return; + } + + if ( !( cent->currentState.eFlags & EF_DEAD ) ) { + return; + } + + // allow cg_enableBreath to force everyone to have breath + if ( !( cent->currentState.eFlags & EF_BREATH ) ) { + return; + } + + contents = CG_PointContents( head->origin, 0 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + return; + } + if ( ci->breathPuffTime > cg.time ) { + return; + } + + CG_GetOriginForTag( cent, head, "tag_mouth", 0, morg, maxis ); + AxisToAngles( maxis, mang ); + AngleVectors( mang, forward, NULL, up ); + + //push the origin out a tad so it's not right in the guys face (tad==4) + VectorMA( morg, 4, forward, morg ); + + forward[0] = up[0] * 8 + forward[0] * 5; + forward[1] = up[1] * 8 + forward[1] * 5; + forward[2] = up[2] * 8 + forward[2] * 5; + + CG_SmokePuff( morg, forward, 4, 1, 1, 1, 0.5f, 2000, cg.time, cg.time + 400, 0, cgs.media.shotgunSmokePuffShader ); + + ci->breathPuffTime = cg.time + 3000 + random() * 1000; +} + +/* +=============== +CG_TrailItem +=============== +*/ +/*static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) { + refEntity_t ent; + vec3_t angles; + qboolean ducking; + + // DHM - Nerve :: Don't draw icon above your own head + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) + return; + + memset( &ent, 0, sizeof( ent ) ); + + VectorCopy( cent->lerpAngles, angles ); + angles[PITCH] = 0; + angles[ROLL] = 0; + AnglesToAxis( angles, ent.axis ); + + // DHM - Nerve :: adjusted values + VectorCopy( cent->lerpOrigin, ent.origin ); + + // Account for ducking + if ( cent->currentState.clientNum == cg.snap->ps.clientNum ) + ducking = (cg.snap->ps.pm_flags & PMF_DUCKED); + else + ducking = (qboolean)cent->currentState.animMovetype; + + if ( ducking ) + ent.origin[2] += 38; + else + ent.origin[2] += 56; + + ent.hModel = hModel; + trap_R_AddRefEntityToScene( &ent ); +}*/ + +/* +=============== +CG_PlayerFloatSprite + +Float a sprite over the player's head +DHM - Nerve :: added height parameter +=============== +*/ +static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader, int height ) { + int rf; + refEntity_t ent; + + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { + rf = RF_THIRD_PERSON; // only show in mirrors + } else { + rf = 0; + } + + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( cent->lerpOrigin, ent.origin ); + ent.origin[2] += height; // DHM - Nerve :: was '48' + + // Account for ducking + if ( cent->currentState.clientNum == cg.snap->ps.clientNum ) { + if ( cg.snap->ps.pm_flags & PMF_DUCKED ) { + ent.origin[2] -= 18; + } + } else { + if ( (qboolean)cent->currentState.animMovetype ) { + ent.origin[2] -= 18; + } + } + + ent.reType = RT_SPRITE; + ent.customShader = shader; + ent.radius = 6.66; + ent.renderfx = rf; + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 255; + trap_R_AddRefEntityToScene( &ent ); +} + + + +/* +=============== +CG_PlayerSprites + +Float sprites over the player's head +=============== +*/ +static void CG_PlayerSprites( centity_t *cent ) { + int team; + + if ( cent->currentState.powerups & ( 1 << PW_REDFLAG ) || + cent->currentState.powerups & ( 1 << PW_BLUEFLAG ) ) { + CG_PlayerFloatSprite( cent, cgs.media.objectiveShader, 56 ); + return; + } + + if ( cent->currentState.eFlags & EF_CONNECTION ) { + CG_PlayerFloatSprite( cent, cgs.media.disconnectIcon, 48 ); + return; + } + + if ( cent->currentState.powerups & ( 1 << PW_INVULNERABLE ) ) { + CG_PlayerFloatSprite( cent, cgs.media.spawnInvincibleShader, 56 ); + return; + } + + team = cgs.clientinfo[ cent->currentState.clientNum ].team; + + // DHM - Nerve :: If this client is a medic, draw a 'revive' icon over + // dead players that are not in limbo yet. + if ( ( cent->currentState.eFlags & EF_DEAD ) + && cent->currentState.number == cent->currentState.clientNum + && cg.snap->ps.stats[ STAT_PLAYER_CLASS ] == PC_MEDIC + && cg.snap->ps.stats[ STAT_HEALTH ] > 0 + && cg.snap->ps.persistant[PERS_TEAM] == team ) { + + CG_PlayerFloatSprite( cent, cgs.media.medicReviveShader, 8 ); + return; + } + + // DHM - Nerve :: show voice chat signal so players know who's talking + if ( cent->voiceChatSpriteTime > cg.time && cg.snap->ps.persistant[PERS_TEAM] == team ) { + CG_PlayerFloatSprite( cent, cent->voiceChatSprite, 56 ); + return; + } + + // DHM - Nerve :: only show talk icon to team-mates + if ( cent->currentState.eFlags & EF_TALK && cg.snap->ps.persistant[PERS_TEAM] == team ) { + CG_PlayerFloatSprite( cent, cgs.media.balloonShader, 48 ); + return; + } + + { + fireteamData_t* ft; + if ( ( ft = CG_IsOnFireteam( cent->currentState.number ) ) ) { + if ( ft == CG_IsOnFireteam( cg.clientNum ) && cgs.clientinfo[ cent->currentState.number ].selected ) { + CG_PlayerFloatSprite( cent, cgs.media.fireteamicons[ft->ident], 56 ); + } + } + } +} + +/* +=============== +CG_PlayerShadow + +Returns the Z component of the surface being shadowed + + should it return a full plane instead of a Z? +=============== +*/ +#define SHADOW_DISTANCE 64 +#define ZOFS 6.0 +#define SHADOW_MIN_DIST 250.0 +#define SHADOW_MAX_DIST 512.0 + +typedef struct { + char *tagname; + float size; + float maxdist; + float maxalpha; + qhandle_t shader; +} shadowPart_t; + +static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) { + vec3_t end; + trace_t trace; + float dist, distFade; + int tagIndex, subIndex; + vec3_t origin, angles, axis[ 3 ]; + vec4_t projection = { 0, 0, -1, 64 }; + shadowPart_t shadowParts[] = { + {"tag_footleft", 10, 4, 1.0, 0}, + {"tag_footright", 10, 4, 1.0, 0}, + {"tag_torso", 18, 96, 0.8, 0}, + {NULL, 0} + }; + + shadowParts[0].shader = cgs.media.shadowFootShader; //DAJ pulled out of initliization + shadowParts[1].shader = cgs.media.shadowFootShader; + shadowParts[2].shader = cgs.media.shadowTorsoShader; + + *shadowPlane = 0; + + if ( cg_shadows.integer == 0 ) { + return qfalse; + } + + // send a trace down from the player to the ground + VectorCopy( cent->lerpOrigin, end ); + end[2] -= SHADOW_DISTANCE; + + trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, NULL, NULL, 0, MASK_PLAYERSOLID ); + + // no shadow if too high + //% if ( trace.fraction == 1.0 || trace.fraction == 0.0f ) { + //% return qfalse; + //% } + + *shadowPlane = trace.endpos[2] + 1; + + if ( cg_shadows.integer != 1 ) { // no mark for stencil or projection shadows + return qtrue; + } + + // no shadows when dead + if ( cent->currentState.eFlags & EF_DEAD ) { + return qfalse; + } + + // fade the shadow out with height + //% alpha = 1.0 - trace.fraction; + + // add the mark as a temporary, so it goes directly to the renderer + // without taking a spot in the cg_marks array + dist = VectorDistance( cent->lerpOrigin, cg.refdef_current->vieworg ); //% cg.snap->ps.origin ); + distFade = 1.0f; + if ( !( cent->currentState.eFlags & EF_ZOOMING ) && ( dist > SHADOW_MIN_DIST ) ) { + if ( dist > SHADOW_MAX_DIST ) { + if ( dist > SHADOW_MAX_DIST * 2 ) { + return qfalse; + } else { // fade out + distFade = 1.0f - ( ( dist - SHADOW_MAX_DIST ) / SHADOW_MAX_DIST ); + } + + if ( distFade > 1.0f ) { + distFade = 1.0f; + } else if ( distFade < 0.0f ) { + distFade = 0.0f; + } + } + + // set origin + VectorCopy( cent->lerpOrigin, origin ); + + // project it onto the shadow plane + if ( origin[2] < *shadowPlane ) { + origin[2] = *shadowPlane; + } + + // ydnar: add a bit of height so foot shadows don't clip into sloped geometry as much + origin[ 2 ] += 18.0f; + + //% alpha *= distFade; + + // ydnar: decal remix + //% CG_ImpactMark( cgs.media.shadowTorsoShader, trace.endpos, trace.plane.normal, + //% 0, alpha,alpha,alpha,1, qfalse, 16, qtrue, -1 ); + CG_ImpactMark( cgs.media.shadowTorsoShader, origin, projection, 18.0f, + cent->lerpAngles[ YAW ], distFade, distFade, distFade, distFade, -1 ); + return qtrue; + } + + if ( dist < SHADOW_MAX_DIST ) { // show more detail + // now add shadows for the various body parts + for ( tagIndex = 0; shadowParts[tagIndex].tagname; tagIndex++ ) { + // grab each tag with this name + for ( subIndex = 0; ( subIndex = CG_GetOriginForTag( cent, ¢->pe.bodyRefEnt, shadowParts[tagIndex].tagname, subIndex, origin, axis ) ) >= 0; subIndex++ ) + { + // project it onto the shadow plane + if ( origin[2] < *shadowPlane ) { + origin[2] = *shadowPlane; + } + + // ydnar: add a bit of height so foot shadows don't clip into sloped geometry as much + origin[ 2 ] += 5.0f; + + #if 0 + alpha = 1.0 - ( ( origin[2] - ( *shadowPlane + ZOFS ) ) / shadowParts[tagIndex].maxdist ); + if ( alpha < 0 ) { + continue; + } + if ( alpha > shadowParts[tagIndex].maxalpha ) { + alpha = shadowParts[tagIndex].maxalpha; + } + alpha *= ( 1.0 - distFade ); + origin[2] = *shadowPlane; + #endif + + AxisToAngles( axis, angles ); + + // ydnar: decal remix + //% CG_ImpactMark( shadowParts[tagIndex].shader, origin, trace.plane.normal, + //% angles[YAW]/*cent->pe.legs.yawAngle*/, alpha,alpha,alpha,1, qfalse, shadowParts[tagIndex].size, qtrue, -1 ); + + //% CG_ImpactMark( shadowParts[ tagIndex ].shader, origin, up, + //% cent->lerpAngles[ YAW ], 1.0f, 1.0f, 1.0f, 1.0f, qfalse, shadowParts[ tagIndex ].size, qtrue, -1 ); + CG_ImpactMark( shadowParts[ tagIndex ].shader, origin, projection, shadowParts[ tagIndex ].size, + angles[ YAW ], distFade, distFade, distFade, distFade, -1 ); + } + } + } + + return qtrue; +} + +/* +=============== +CG_PlayerSplash + +Draw a mark at the water surface +=============== +*/ +static void CG_PlayerSplash( centity_t *cent ) { + vec3_t start, end; + trace_t trace; + int contents; + polyVert_t verts[4]; + + if ( !cg_shadows.integer ) { + return; + } + + VectorCopy( cent->lerpOrigin, end ); + end[2] -= 24; + + // if the feet aren't in liquid, don't make a mark + // this won't handle moving water brushes, but they wouldn't draw right anyway... + contents = CG_PointContents( end, 0 ); + if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) { + return; + } + + VectorCopy( cent->lerpOrigin, start ); + start[2] += 32; + + // if the head isn't out of liquid, don't make a mark + contents = CG_PointContents( start, 0 ); + if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + return; + } + + // trace down to find the surface + trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ); + + if ( trace.fraction == 1.0 ) { + return; + } + + // create a mark polygon + VectorCopy( trace.endpos, verts[0].xyz ); + verts[0].xyz[0] -= 32; + verts[0].xyz[1] -= 32; + verts[0].st[0] = 0; + verts[0].st[1] = 0; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[1].xyz ); + verts[1].xyz[0] -= 32; + verts[1].xyz[1] += 32; + verts[1].st[0] = 0; + verts[1].st[1] = 1; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[2].xyz ); + verts[2].xyz[0] += 32; + verts[2].xyz[1] += 32; + verts[2].st[0] = 1; + verts[2].st[1] = 1; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorCopy( trace.endpos, verts[3].xyz ); + verts[3].xyz[0] += 32; + verts[3].xyz[1] -= 32; + verts[3].st[0] = 1; + verts[3].st[1] = 0; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts ); +} + +//========================================================================== + +/* +=============== +CG_AddRefEntityWithPowerups + +Adds a piece with modifications or duplications for powerups +Also called by CG_Missile for quad rockets, but nobody can tell... +=============== +*/ +void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, int team, entityState_t *es, const vec3_t fireRiseDir ) { + centity_t *cent; + refEntity_t backupRefEnt; //, parentEnt; + qboolean onFire = qfalse; + float alpha = 0.0; + float fireStart, fireEnd; + + cent = &cg_entities[es->number]; + + ent->entityNum = es->number; + +/* if (cent->pe.forceLOD) { + ent->reFlags |= REFLAG_FORCE_LOD; + }*/ + + backupRefEnt = *ent; + + if ( CG_EntOnFire( &cg_entities[es->number] ) ) { + ent->reFlags |= REFLAG_FORCE_LOD; + } + + trap_R_AddRefEntityToScene( ent ); + + if ( !onFire && CG_EntOnFire( &cg_entities[es->number] ) ) { + onFire = qtrue; + // set the alpha + if ( ent->entityNum == cg.snap->ps.clientNum ) { + fireStart = cg.snap->ps.onFireStart; + fireEnd = cg.snap->ps.onFireStart + 1500; + } else { + fireStart = es->onFireStart; + fireEnd = es->onFireEnd; + } + + alpha = ( cg.time - fireStart ) / 1500.0; + if ( alpha > 1.0 ) { + alpha = ( fireEnd - cg.time ) / 1500.0; + if ( alpha > 1.0 ) { + alpha = 1.0; + } + } + } + + if ( onFire ) { + if ( alpha < 0.0 ) { + alpha = 0.0; + } + ent->shaderRGBA[3] = ( unsigned char )( 255.0 * alpha ); + VectorCopy( fireRiseDir, ent->fireRiseDir ); + if ( VectorCompare( ent->fireRiseDir, vec3_origin ) ) { + VectorSet( ent->fireRiseDir, 0, 0, 1 ); + } + ent->customShader = cgs.media.onFireShader; + trap_R_AddRefEntityToScene( ent ); + + ent->customShader = cgs.media.onFireShader2; + trap_R_AddRefEntityToScene( ent ); + + if ( ent->hModel == cent->pe.bodyRefEnt.hModel ) { + trap_S_AddLoopingSound( ent->origin, vec3_origin, cgs.media.flameCrackSound, (int)( 255.0 * alpha ), 0 ); + } + } + + *ent = backupRefEnt; +} + +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; +} + + +/* +=============== +CG_AnimPlayerConditions + + predict, or calculate condition for this entity, if it is not the local client +=============== +*/ +void CG_AnimPlayerConditions( bg_character_t *character, centity_t *cent ) { + entityState_t *es; + int legsAnim; + + if ( !character ) { + return; + } + if ( cg.snap && cg.snap->ps.clientNum == cent->currentState.number && !cg.renderingThirdPerson ) { + return; + } + + es = ¢->currentState; + + // WEAPON + if ( es->eFlags & EF_ZOOMING ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_WEAPON, WP_BINOCULARS, qtrue ); + } else { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_WEAPON, es->weapon, qtrue ); + } + + // MOUNTED + if ( ( es->eFlags & EF_MG42_ACTIVE ) || ( es->eFlags & EF_MOUNTEDTANK ) ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_MOUNTED, MOUNTED_MG42, qtrue ); + } else if ( es->eFlags & EF_AAGUN_ACTIVE ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_MOUNTED, MOUNTED_AAGUN, qtrue ); + } else { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_MOUNTED, MOUNTED_UNUSED, qtrue ); + } + + // UNDERHAND + BG_UpdateConditionValue( es->clientNum, ANIM_COND_UNDERHAND, cent->lerpAngles[0] > 0, qtrue ); + + if ( es->eFlags & EF_CROUCHING ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_CROUCHING, qtrue, qtrue ); + } else { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_CROUCHING, qfalse, qtrue ); + } + + if ( es->eFlags & EF_FIRING ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_FIRING, qtrue, qtrue ); + } else { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_FIRING, qfalse, qtrue ); + } + + // reverse engineer the legs anim -> movetype (if possible) + legsAnim = es->legsAnim & ~ANIM_TOGGLEBIT; + if ( character->animModelInfo->animations[legsAnim]->movetype ) { + BG_UpdateConditionValue( es->clientNum, ANIM_COND_MOVETYPE, character->animModelInfo->animations[legsAnim]->movetype, qfalse ); + } + +} + + +/* +=============== +CG_Player +=============== +*/ +void CG_Player( centity_t *cent ) { + clientInfo_t *ci; + refEntity_t body; + refEntity_t head; + refEntity_t acc; + vec3_t playerOrigin; + vec3_t lightorigin; + int clientNum,i; + int renderfx; + qboolean shadow; + float shadowPlane; +// float gumsflappin = 0; // talking amplitude + qboolean usingBinocs = qfalse; + centity_t *cgsnap; + bg_character_t *character; + float hilightIntensity = 0.f; + + cgsnap = &cg_entities[cg.snap->ps.clientNum]; + + shadow = qfalse; // gjd added to make sure it was initialized + shadowPlane = 0.0; // ditto + + // if set to invisible, skip + if ( cent->currentState.eFlags & EF_NODRAW ) { + return; + } + + // the client number is stored in clientNum. It can't be derived + // from the entity number, because a single client may have + // multiple corpses on the level using the same clientinfo + clientNum = cent->currentState.clientNum; + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + CG_Error( "Bad clientNum on player entity" ); + } + ci = &cgs.clientinfo[ clientNum ]; + + // it is possible to see corpses from disconnected players that may + // not have valid clientinfo + if ( !ci->infoValid ) { + return; + } + + character = CG_CharacterForClientinfo( ci, cent ); + + if ( cent->currentState.eFlags & EF_MOUNTEDTANK ) { + VectorCopy( cg_entities[ cg_entities[ cent->currentState.clientNum ].tagParent ].mountedMG42Player.origin, playerOrigin ); + } else if ( cent->currentState.eFlags & EF_MG42_ACTIVE || cent->currentState.eFlags & EF_AAGUN_ACTIVE ) { // Arnout: see if we're attached to a gun + centity_t *mg42; + int num; + + // find the mg42 we're attached to + for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { + mg42 = &cg_entities[ cg.snap->entities[ num ].number ]; + if ( mg42->currentState.eType == ET_MG42_BARREL && + mg42->currentState.otherEntityNum == cent->currentState.number ) { + // found it, clamp behind gun + vec3_t forward, right, up; + + //AngleVectors (mg42->s.apos.trBase, forward, right, up); + AngleVectors( cent->lerpAngles, forward, right, up ); + VectorMA( mg42->currentState.pos.trBase, -36, forward, playerOrigin ); + playerOrigin[2] = cent->lerpOrigin[2]; + break; + } + } + + if ( num == cg.snap->numEntities ) { + VectorCopy( cent->lerpOrigin, playerOrigin ); + } + } else { + VectorCopy( cent->lerpOrigin, playerOrigin ); + } + + memset( &body, 0, sizeof( body ) ); + memset( &head, 0, sizeof( head ) ); + memset( &acc, 0, sizeof( acc ) ); + + // get the rotation information + CG_PlayerAngles( cent, body.axis, body.torsoAxis, head.axis ); + + // FIXME: move this into CG_PlayerAngles + if ( cgsnap == cent && ( cg.snap->ps.pm_flags & PMF_LADDER ) ) { + memcpy( body.torsoAxis, body.axis, sizeof( body.torsoAxis ) ); + } + + // copy the torso rotation to the accessories + AxisCopy( body.torsoAxis, acc.axis ); + + // calculate client-side conditions + CG_AnimPlayerConditions( character, cent ); + + // get the animation state (after rotation, to allow feet shuffle) + CG_PlayerAnimation( cent, &body ); + + // forcibly set binoc animation + if ( cent->currentState.eFlags & EF_ZOOMING ) { + usingBinocs = qtrue; + } + + // add the any sprites hovering above the player + // rain - corpses don't get icons (fireteam check ran out of bounds) + if ( cent->currentState.eType != ET_CORPSE ) { + CG_PlayerSprites( cent ); + } + + // add a water splash if partially in and out of water + CG_PlayerSplash( cent ); + + // get the player model information + renderfx = 0; + if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) { + renderfx = RF_THIRD_PERSON; // only draw in mirrors + } + + // draw the player in cameras + if ( cg.cameraMode ) { + renderfx &= ~RF_THIRD_PERSON; + } + + if ( cg_shadows.integer == 3 && shadow ) { + renderfx |= RF_SHADOW_PLANE; + } + + renderfx |= RF_LIGHTING_ORIGIN; // use the same origin for all + + // set renderfx for accessories + acc.renderfx = renderfx; + + VectorCopy( playerOrigin, lightorigin ); + lightorigin[2] += 31; + + { + vec3_t dist; + vec_t distSquared; + + VectorSubtract( lightorigin, cg.refdef_current->vieworg, dist ); + distSquared = VectorLengthSquared( dist ); + if ( distSquared > Square( 384.f ) ) { + renderfx |= RF_MINLIGHT; + + distSquared -= Square( 384.f ); + + if ( distSquared > Square( 768.f ) ) { + hilightIntensity = 1.f; + } else { + hilightIntensity = 1.f * ( distSquared / Square( 768.f ) ); + } + + //CG_Printf( "%f\n", hilightIntensity ); + } + } + + body.hilightIntensity = hilightIntensity; + head.hilightIntensity = hilightIntensity; + acc.hilightIntensity = hilightIntensity; + + // + // add the body + // + if ( cent->currentState.eType == ET_CORPSE && cent->currentState.time2 == 1 ) { + body.hModel = character->undressedCorpseModel; + body.customSkin = character->undressedCorpseSkin; + } else { + body.customSkin = character->skin; + body.hModel = character->mesh; + } + + VectorCopy( playerOrigin, body.origin ); + VectorCopy( lightorigin, body.lightingOrigin ); + body.shadowPlane = shadowPlane; + body.renderfx = renderfx; + VectorCopy( body.origin, body.oldorigin ); // don't positionally lerp at all + + cent->pe.bodyRefEnt = body; + + // if the model failed, allow the default nullmodel to be displayed + // Gordon: whoever wrote that comment sucks + if ( !body.hModel ) { + return; + } + + // (SA) only need to set this once... + VectorCopy( lightorigin, acc.lightingOrigin ); + + CG_AddRefEntityWithPowerups( &body, cent->currentState.powerups, ci->team, ¢->currentState, cent->fireRiseDir ); + + // ydnar debug + #if 0 + { + int y; + vec3_t oldOrigin; + + VectorCopy( body.origin, oldOrigin ); + body.origin[ 0 ] -= 20; + //body.origin[ 0 ] -= 20 * 36; + for ( y = 0; y < 40; y++ ) + { + body.origin[ 0 ] += 1; + //body.origin[ 0 ] += 36; + //body.origin[ 2 ] = BG_GetGroundHeightAtPoint( body.origin ) + (oldOrigin[2] - BG_GetGroundHeightAtPoint( oldOrigin )); + body.frame += ( y & 1 ) ? 1 : -1; + body.oldframe += ( y & 1 ) ? -1 : 1; + CG_AddRefEntityWithPowerups( &body, cent->currentState.powerups, ci->team, ¢->currentState, cent->fireRiseDir ); + } + VectorCopy( oldOrigin, body.origin ); + } + #endif + +// DEBUG + /*{ + int x, zd, zu; + vec3_t bmins, bmaxs; + + x = (cent->currentState.solid & 255); + zd = ((cent->currentState.solid>>8) & 255); + zu = ((cent->currentState.solid>>16) & 255) - 32; + + bmins[0] = bmins[1] = -x; + bmaxs[0] = bmaxs[1] = x; + bmins[2] = -zd; + bmaxs[2] = zu; + + VectorAdd( bmins, cent->lerpOrigin, bmins ); + VectorAdd( bmaxs, cent->lerpOrigin, bmaxs ); + + CG_RailTrail( NULL, bmins, bmaxs, 1 ); + }*/ + + /*{ + orientation_t tag; + int idx; + vec3_t start; + vec3_t ends[3]; + vec3_t axis[3]; + trap_R_LerpTag( &tag, &body, "tag_head", 0 ); + + VectorCopy( body.origin, start ); + + for( idx = 0; idx < 3; idx++ ) { + VectorMA( start, tag.origin[idx], body.axis[idx], start ); + } + + MatrixMultiply( tag.axis, body.axis, axis ); + + for( idx = 0; idx < 3; idx++ ) { + VectorMA( start, 32, axis[idx], ends[idx] ); + CG_RailTrail2( NULL, start, ends[idx] ); + } + } + { + vec3_t mins, maxs; + VectorCopy( cg.predictedPlayerState.mins, mins ); + VectorCopy( cg.predictedPlayerState.maxs, maxs ); + + if( cg.predictedPlayerState.eFlags & EF_PRONE ) { + maxs[2] = maxs[2] - (cg.predictedPlayerState.standViewHeight - PRONE_VIEWHEIGHT + 8); + } else if( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) { + maxs[2] = cg.predictedPlayerState.crouchMaxZ; + } + + VectorAdd( cent->lerpOrigin, mins, mins ); + VectorAdd( cent->lerpOrigin, maxs, maxs ); + CG_RailTrail( NULL, mins, maxs, 1 ); + + if( cg.predictedPlayerState.eFlags & EF_PRONE ) { + vec3_t org, forward; + + // The legs + VectorCopy( playerlegsProneMins, mins ); + VectorCopy( playerlegsProneMaxs, maxs ); + + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + forward[2] = 0; + VectorNormalizeFast( forward ); + + org[0] = cent->lerpOrigin[0] + forward[0] * -32; + org[1] = cent->lerpOrigin[1] + forward[1] * -32; + org[2] = cent->lerpOrigin[2] + cg.pmext.proneLegsOffset; + + VectorAdd( org, mins, mins ); + VectorAdd( org, maxs, maxs ); + CG_RailTrail( NULL, mins, maxs, 1 ); + + // And the head + VectorSet( mins, -6, -6, -22 ); + VectorSet( maxs, 6, 6, -10 ); + + org[0] = cent->lerpOrigin[0] + forward[0] * 12; + org[1] = cent->lerpOrigin[1] + forward[1] * 12; + org[2] = cent->lerpOrigin[2]; + + VectorAdd( org, mins, mins ); + VectorAdd( org, maxs, maxs ); + CG_RailTrail( NULL, mins, maxs, 1 ); + } + }*/ +// DEBUG + + // + // add the head + // + + if ( !( head.hModel = character->hudhead ) ) { + return; + } + head.customSkin = character->hudheadskin; + + VectorCopy( lightorigin, head.lightingOrigin ); + + CG_PositionRotatedEntityOnTag( &head, &body, "tag_head" ); + + head.shadowPlane = shadowPlane; + head.renderfx = renderfx; + + if ( cent->currentState.eFlags & EF_FIRING ) { + cent->pe.lastFiredWeaponTime = 0; + cent->pe.weaponFireTime += cg.frametime; + } else { + if ( cent->pe.weaponFireTime > 500 && cent->pe.weaponFireTime ) { + cent->pe.lastFiredWeaponTime = cg.time; + } + + cent->pe.weaponFireTime = 0; + } + + if ( cent->currentState.eType != ET_CORPSE && !( cent->currentState.eFlags & EF_DEAD ) ) { + hudHeadAnimNumber_t anim; + + if ( cent->pe.weaponFireTime > 500 ) { + anim = HD_ATTACK; + } else if ( cg.time - cent->pe.lastFiredWeaponTime < 500 ) { + anim = HD_ATTACK_END; + } else { + anim = HD_IDLE1; + } + + CG_HudHeadAnimation( character, ¢->pe.head, &head.oldframe, &head.frame, &head.backlerp, anim ); + } else { + head.frame = 0; + head.oldframe = 0; + head.backlerp = 0.f; + } + + // set blinking flag + CG_AddRefEntityWithPowerups( &head, cent->currentState.powerups, ci->team, ¢->currentState, cent->fireRiseDir ); + + cent->pe.headRefEnt = head; + + // add the + + shadow = CG_PlayerShadow( cent, &shadowPlane ); + + // set the shadowplane for accessories + acc.shadowPlane = shadowPlane; + + CG_BreathPuffs( cent, &head ); + + // + // add the gun / barrel / flash + // + if ( !( cent->currentState.eFlags & EF_DEAD ) /*&& !usingBinocs*/ ) { // NERVE - SMF + CG_AddPlayerWeapon( &body, NULL, cent ); + } + + // + // add binoculars (if it's not the player) + // + if ( usingBinocs ) { // NERVE - SMF + acc.hModel = cgs.media.thirdPersonBinocModel; + CG_PositionEntityOnTag( &acc, &body, "tag_weapon", 0, NULL ); + CG_AddRefEntityWithPowerups( &acc, cent->currentState.powerups, ci->team, ¢->currentState, cent->fireRiseDir ); + } + + // + // add accessories + // + for ( i = ACC_BELT_LEFT; i < ACC_MAX; i++ ) { + if ( !( character->accModels[i] ) ) { + continue; + } + acc.hModel = character->accModels[i]; + acc.customSkin = character->accSkins[i]; + + // Gordon: looted corpses dont have any accsserories, evil looters :E + if ( !( cent->currentState.eType == ET_CORPSE && cent->currentState.time2 == 1 ) ) { + switch ( i ) + { + case ACC_BELT_LEFT: + CG_PositionEntityOnTag( &acc, &body, "tag_bright", 0, NULL ); + break; + case ACC_BELT_RIGHT: + CG_PositionEntityOnTag( &acc, &body, "tag_bleft", 0, NULL ); + break; + + case ACC_BELT: + CG_PositionEntityOnTag( &acc, &body, "tag_ubelt", 0, NULL ); + break; + case ACC_BACK: + CG_PositionEntityOnTag( &acc, &body, "tag_back", 0, NULL ); + break; + + case ACC_HAT: //hat + case ACC_RANK: + if ( cent->currentState.eFlags & EF_HEADSHOT ) { + continue; + } + case ACC_MOUTH2: // hat2 + case ACC_MOUTH3: // hat3 + + if ( i == ACC_RANK ) { + if ( ci->rank == 0 ) { + continue; + } + acc.customShader = rankicons[ ci->rank ][ 1 ].shader; + } + + CG_PositionEntityOnTag( &acc, &head, "tag_mouth", 0, NULL ); + break; + + // weapon and weapon2 + // these are used by characters who have permanent weapons attached to their character in the skin + case ACC_WEAPON: // weap + CG_PositionEntityOnTag( &acc, &body, "tag_weapon", 0, NULL ); + break; + case ACC_WEAPON2: // weap2 + CG_PositionEntityOnTag( &acc, &body, "tag_weapon2", 0, NULL ); + break; + + + default: + continue; + } + + CG_AddRefEntityWithPowerups( &acc, cent->currentState.powerups, ci->team, ¢->currentState, cent->fireRiseDir ); + } + } +} + +//===================================================================== + +extern void CG_ClearWeapLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ); + +/* +=============== +CG_ResetPlayerEntity + +A player just came into view or teleported, so reset all animation info +=============== +*/ +void CG_ResetPlayerEntity( centity_t *cent ) { + // Gordon: these are unused +// cent->errorTime = -99999; // guarantee no error decay added +// cent->extrapolated = qfalse; + + if ( !( cent->currentState.eFlags & EF_DEAD ) ) { + CG_ClearLerpFrameRate( cent, &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.legs, cent->currentState.legsAnim ); + CG_ClearLerpFrame( cent, &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.torso, cent->currentState.torsoAnim ); + + memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) ); + cent->pe.legs.yawAngle = cent->rawAngles[YAW]; + cent->pe.legs.yawing = qfalse; + cent->pe.legs.pitchAngle = 0; + cent->pe.legs.pitching = qfalse; + + memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) ); + cent->pe.torso.yawAngle = cent->rawAngles[YAW]; + cent->pe.torso.yawing = qfalse; + cent->pe.torso.pitchAngle = cent->rawAngles[PITCH]; + cent->pe.torso.pitching = qfalse; + } + + BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles, qtrue, cent->currentState.effect2Time ); + + VectorCopy( cent->lerpOrigin, cent->rawOrigin ); + VectorCopy( cent->lerpAngles, cent->rawAngles ); + + if ( cg_debugPosition.integer ) { + CG_Printf( "%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle ); + } + + cent->pe.painAnimLegs = -1; + cent->pe.painAnimTorso = -1; + cent->pe.animSpeed = 1.0; + +} + +void CG_GetBleedOrigin( vec3_t head_origin, vec3_t body_origin, int fleshEntityNum ) { + clientInfo_t *ci; + refEntity_t body; + refEntity_t head; + centity_t *cent, backupCent; + bg_character_t *character; + + ci = &cgs.clientinfo[ fleshEntityNum ]; + + if ( !ci->infoValid ) { + return; + } + + character = CG_CharacterForClientinfo( ci, NULL ); + + cent = &cg_entities [ fleshEntityNum ]; + backupCent = *cent; + + memset( &body, 0, sizeof( body ) ); + memset( &head, 0, sizeof( head ) ); + + CG_PlayerAngles( cent, body.axis, body.torsoAxis, head.axis ); + CG_PlayerAnimation( cent, &body ); + + body.hModel = character->mesh; + if ( !body.hModel ) { + return; + } + + head.hModel = character->hudhead; + if ( !head.hModel ) { + return; + } + + VectorCopy( cent->lerpOrigin, body.origin ); + VectorCopy( body.origin, body.oldorigin ); + + // Ridah, restore the cent so we don't interfere with animation timings + *cent = backupCent; + + CG_PositionRotatedEntityOnTag( &head, &body, "tag_head" ); + + VectorCopy( head.origin, head_origin ); + VectorCopy( body.origin, body_origin ); +} + +/* +=============== +CG_GetTag +=============== +*/ +qboolean CG_GetTag( int clientNum, char *tagname, orientation_t *or ) { + clientInfo_t *ci; + centity_t *cent; + refEntity_t *refent; + vec3_t tempAxis[3]; + vec3_t org; + int i; + + ci = &cgs.clientinfo[ clientNum ]; + + if ( cg.snap && clientNum == cg.snap->ps.clientNum && cg.renderingThirdPerson ) { + cent = &cg.predictedPlayerEntity; + } else { + cent = &cg_entities[ci->clientNum]; + if ( !cent->currentValid ) { + return qfalse; // not currently in PVS + } + } + + refent = ¢->pe.bodyRefEnt; + + if ( trap_R_LerpTag( or, refent, tagname, 0 ) < 0 ) { + return qfalse; + } + + VectorCopy( refent->origin, org ); + + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( org, or->origin[i], refent->axis[i], org ); + } + + VectorCopy( org, or->origin ); + + // rotate with entity + MatrixMultiply( refent->axis, or->axis, tempAxis ); + memcpy( or->axis, tempAxis, sizeof( vec3_t ) * 3 ); + + return qtrue; +} + +/* +=============== +CG_GetWeaponTag +=============== +*/ +qboolean CG_GetWeaponTag( int clientNum, char *tagname, orientation_t *or ) { + clientInfo_t *ci; + centity_t *cent; + refEntity_t *refent; + vec3_t tempAxis[3]; + vec3_t org; + int i; + + ci = &cgs.clientinfo[ clientNum ]; + + if ( cg.snap && clientNum == cg.snap->ps.clientNum && cg.renderingThirdPerson ) { + cent = &cg.predictedPlayerEntity; + } else { + cent = &cg_entities[ci->clientNum]; + if ( !cent->currentValid ) { + return qfalse; // not currently in PVS + } + } + + if ( cent->pe.gunRefEntFrame < cg.clientFrame - 1 ) { + return qfalse; + } + + refent = ¢->pe.gunRefEnt; + + if ( trap_R_LerpTag( or, refent, tagname, 0 ) < 0 ) { + return qfalse; + } + + VectorCopy( refent->origin, org ); + + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( org, or->origin[i], refent->axis[i], org ); + } + + VectorCopy( org, or->origin ); + + // rotate with entity + MatrixMultiply( refent->axis, or->axis, tempAxis ); + memcpy( or->axis, tempAxis, sizeof( vec3_t ) * 3 ); + + return qtrue; +} + +// ============= +// Menu Versions +// ============= + +/* +================== +CG_SwingAngles_Limbo +================== +*/ +static void CG_SwingAngles_Limbo( float destination, float swingTolerance, float clampTolerance, + float speed, float *angle, qboolean *swinging ) { + float swing; + float move; + float scale; + + if ( !*swinging ) { + // see if a swing should be started + swing = AngleSubtract( *angle, destination ); + if ( swing > swingTolerance || swing < -swingTolerance ) { + *swinging = qtrue; + } + } + + if ( !*swinging ) { + return; + } + + // modify the speed depending on the delta + // so it doesn't seem so linear + swing = AngleSubtract( destination, *angle ); + scale = fabs( swing ); + if ( scale < swingTolerance * 0.5 ) { + scale = 0.5; + } else if ( scale < swingTolerance ) { + scale = 1.0; + } else { + scale = 2.0; + } + + // swing towards the destination angle + if ( swing >= 0 ) { + move = cg.frametime * scale * speed; + if ( move >= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } else if ( swing < 0 ) { + move = cg.frametime * scale * -speed; + if ( move <= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } + + // clamp to no more than tolerance + swing = AngleSubtract( destination, *angle ); + if ( swing > clampTolerance ) { + *angle = AngleMod( destination - ( clampTolerance - 1 ) ); + } else if ( swing < -clampTolerance ) { + *angle = AngleMod( destination + ( clampTolerance - 1 ) ); + } +} + +/* +=============== +CG_PlayerAngles_Limbo +=============== +*/ +void CG_PlayerAngles_Limbo( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { + vec3_t legsAngles, torsoAngles, headAngles; + float dest; + + VectorCopy( pi->viewAngles, headAngles ); + headAngles[YAW] = AngleMod( headAngles[YAW] ); + VectorClear( legsAngles ); + VectorClear( torsoAngles ); + + torsoAngles[YAW] = 180; + legsAngles[YAW] = 180; + headAngles[YAW] = 180; + + // --------- pitch ------------- + + // only show a fraction of the pitch angle in the torso + if ( headAngles[PITCH] > 180 ) { + dest = ( -360 + headAngles[PITCH] ) * 0.75; + } else { + dest = headAngles[PITCH] * 0.75; + } + CG_SwingAngles_Limbo( dest, 15, 30, 0.1, &pi->torso.pitchAngle, &pi->torso.pitching ); + torsoAngles[PITCH] = pi->torso.pitchAngle; + + // pull the angles back out of the hierarchial chain + AnglesSubtract( headAngles, torsoAngles, headAngles ); + AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); + + AnglesSubtract( legsAngles, pi->moveAngles, legsAngles ); // NERVE - SMF + + AnglesToAxis( legsAngles, legs ); + AnglesToAxis( torsoAngles, torso ); + AnglesToAxis( headAngles, head ); +} + +animation_t *CG_GetLimboAnimation( playerInfo_t *pi, const char *name ) { + int i; + bg_character_t *character = BG_GetCharacter( pi->teamNum, pi->classNum ); + + if ( !character ) { + return NULL; + } + + for ( i = 0; i < character->animModelInfo->numAnimations; i++ ) { + if ( !Q_stricmp( character->animModelInfo->animations[i]->name, name ) ) { + return character->animModelInfo->animations[i]; + } + } + + return character->animModelInfo->animations[0]; // safe fallback so we never end up without an animation (which will crash the game) +} + +int CG_GetSelectedWeapon( void ) { + return 0; +} + +void CG_DrawPlayer_Limbo( float x, float y, float w, float h, playerInfo_t *pi, int time, clientInfo_t *ci, qboolean animatedHead ) { + refdef_t refdef; + refEntity_t body; + refEntity_t head; + refEntity_t gun; + refEntity_t barrel; + refEntity_t acc; + vec3_t origin; + int renderfx; + vec3_t mins = {-16, -16, -24}; + vec3_t maxs = {16, 16, 32}; + float len; +// float xx; + vec4_t hcolor = { 1, 0, 0, 0.5 }; + bg_character_t *character = BG_GetCharacter( pi->teamNum, pi->classNum ); + int i; + + dp_realtime = time; + + CG_AdjustFrom640( &x, &y, &w, &h ); + y -= jumpHeight; + + memset( &refdef, 0, sizeof( refdef ) ); + memset( &body, 0, sizeof( body ) ); + memset( &head, 0, sizeof( head ) ); + memset( &acc, 0, sizeof( acc ) ); + + refdef.rdflags = RDF_NOWORLDMODEL; + + AxisClear( refdef.viewaxis ); + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + +/* refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); + xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); + refdef.fov_y = atan2( refdef.height, xx ); + refdef.fov_y *= ( 360 / M_PI );*/ + + refdef.fov_x = 35; + refdef.fov_y = 35; + + // calculate distance so the player nearly fills the box + + // START Mad Doc - TDF + // for "talking heads", we calc the origin differently + // FIXME - this is stupid - should all be character driven - NO CODE HACKS FOR SPECIFIC THINGS THAT CAN BE DONE CLEANLY + if ( animatedHead == qfalse ) { + // END Mad Doc - TDF + len = 0.9 * ( maxs[2] - mins[2] ); // NERVE - SMF - changed from 0.7 + origin[0] = pi->y - 70 + ( len / tan( DEG2RAD( refdef.fov_x ) * 0.5 ) ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + origin[2] = pi->z - 23 + ( -0.5 * ( mins[2] + maxs[2] ) ); + } else + { + // for "talking head" animations, we want to center just below the face + // we precalculated this elsewhere + VectorCopy( pi->headOrigin, origin ); + } + // END Mad Doc - TDF + + refdef.time = dp_realtime; + + trap_R_SetColor( hcolor ); + trap_R_ClearScene(); + trap_R_SetColor( NULL ); + + // get the rotation information + CG_PlayerAngles_Limbo( pi, body.axis, body.torsoAxis, head.axis ); + + renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; + + acc.renderfx = renderfx; + + + // + // add the body + // + + body.hModel = character->mesh; + body.customSkin = character->skin; + + body.renderfx = renderfx; + + VectorCopy( origin, body.origin ); + VectorCopy( origin, body.lightingOrigin ); + VectorCopy( body.origin, body.oldorigin ); + + if ( cg.time >= pi->torso.frameTime ) { + pi->torso.oldFrameTime = pi->torso.frameTime; + pi->torso.oldFrame = pi->torso.frame; + pi->torso.oldFrameModel = pi->torso.frameModel = pi->torso.animation->mdxFile; + + while ( cg.time >= pi->torso.frameTime ) { + pi->torso.frameTime += ( pi->torso.animation->duration / (float)( pi->torso.animation->numFrames ) ); + pi->torso.frame++; + + if ( pi->torso.frame >= pi->torso.animation->firstFrame + pi->torso.animation->numFrames ) { + pi->torso.frame = pi->torso.animation->firstFrame; + } + } + } + + if ( pi->torso.frameTime == pi->torso.oldFrameTime ) { + pi->torso.backlerp = 0; + } else { + pi->torso.backlerp = 1.0 - (float)( cg.time - pi->torso.oldFrameTime ) / ( pi->torso.frameTime - pi->torso.oldFrameTime ); + } + + if ( cg.time >= pi->legs.frameTime ) { + pi->legs.oldFrameTime = pi->legs.frameTime; + pi->legs.oldFrame = pi->legs.frame; + pi->legs.oldFrameModel = pi->legs.frameModel = pi->legs.animation->mdxFile; + + while ( cg.time >= pi->legs.frameTime ) { + pi->legs.frameTime += ( pi->legs.animation->duration / (float)( pi->legs.animation->numFrames ) ); + pi->legs.frame++; + + if ( pi->legs.frame >= pi->legs.animation->firstFrame + pi->legs.animation->numFrames ) { + pi->legs.frame = pi->legs.animation->firstFrame; + } + } + } + + if ( pi->legs.frameTime == pi->legs.oldFrameTime ) { + pi->legs.backlerp = 0; + } else { + pi->legs.backlerp = 1.0 - (float)( cg.time - pi->legs.oldFrameTime ) / ( pi->legs.frameTime - pi->legs.oldFrameTime ); + } + + body.oldTorsoFrame = pi->torso.oldFrame; + body.torsoFrame = pi->torso.frame; + body.torsoBacklerp = pi->torso.backlerp; + body.torsoFrameModel = pi->torso.frameModel; + body.oldTorsoFrameModel = pi->torso.oldFrameModel; + + body.oldframe = pi->legs.oldFrame; + body.frame = pi->legs.frame; + body.backlerp = pi->legs.backlerp; + body.frameModel = pi->legs.frameModel; + body.oldframeModel = pi->legs.oldFrameModel; + + trap_R_AddRefEntityToScene( &body ); + + // + // add the head + // + + head.hModel = character->hudhead; + if ( !head.hModel ) { + return; + } + head.customSkin = character->headSkin; + + VectorCopy( origin, head.lightingOrigin ); + + CG_PositionRotatedEntityOnTag( &head, &body, "tag_head" ); + + head.renderfx = renderfx; + + head.frame = 0; + head.oldframe = 0; + head.backlerp = 0.f; + + trap_R_AddRefEntityToScene( &head ); + + AxisCopy( body.torsoAxis, acc.axis ); + VectorCopy( origin, acc.lightingOrigin ); + + + for ( i = ACC_BELT_LEFT; i < ACC_MAX; i++ ) { + if ( !( character->accModels[i] ) ) { + continue; + } + acc.hModel = character->accModels[i]; + acc.customSkin = character->accSkins[i]; + + switch ( i ) { + case ACC_BELT_LEFT: + CG_PositionEntityOnTag( &acc, &body, "tag_bright", 0, NULL ); + break; + case ACC_BELT_RIGHT: + CG_PositionEntityOnTag( &acc, &body, "tag_bleft", 0, NULL ); + break; + + case ACC_BELT: + CG_PositionEntityOnTag( &acc, &body, "tag_ubelt", 0, NULL ); + break; + case ACC_BACK: + CG_PositionEntityOnTag( &acc, &body, "tag_back", 0, NULL ); + break; + + case ACC_HAT: // hat + case ACC_MOUTH2: // hat2 + case ACC_MOUTH3: // hat3 + CG_PositionEntityOnTag( &acc, &head, "tag_mouth", 0, NULL ); + break; + + // weapon and weapon2 + // these are used by characters who have permanent weapons attached to their character in the skin + case ACC_WEAPON: // weap + CG_PositionEntityOnTag( &acc, &body, "tag_weapon", 0, NULL ); + break; + case ACC_WEAPON2: // weap2 + CG_PositionEntityOnTag( &acc, &body, "tag_weapon2", 0, NULL ); + break; + + default: + continue; + } + + trap_R_AddRefEntityToScene( &acc ); + } + + // + // add the gun + // + { + int weap = CG_GetSelectedWeapon(); + + memset( &gun, 0, sizeof( gun ) ); + memset( &barrel, 0, sizeof( barrel ) ); + + gun.hModel = cg_weapons[weap].weaponModel[W_TP_MODEL].model; + + VectorCopy( origin, gun.lightingOrigin ); + CG_PositionEntityOnTag( &gun, &body, "tag_weapon", 0, NULL ); + gun.renderfx = renderfx; + trap_R_AddRefEntityToScene( &gun ); + } + + // Save out the old render info so we don't kill the LOD system here + trap_R_SaveViewParms(); + + // + // add an accent light + // + origin[0] -= 100; // + = behind, - = in front + origin[1] += 100; // + = left, - = right + origin[2] += 100; // + = above, - = below + //% trap_R_AddLightToScene( origin, 1000, 1.0, 1.0, 1.0, 0 ); + trap_R_AddLightToScene( origin, 1000, 1.0, 1.0, 1.0, 1.0, 0, 0 ); + + origin[0] -= 100; + origin[1] -= 100; + origin[2] -= 100; + //% trap_R_AddLightToScene( origin, 1000, 1.0, 1.0, 1.0, 0 ); + trap_R_AddLightToScene( origin, 1000, 1.0, 1.0, 1.0, 1.0, 0, 0 ); + + trap_R_RenderScene( &refdef ); + + // Reset the view parameters + trap_R_RestoreViewParms(); +} + +weaponType_t weaponTypes[] = { + { WP_MP40, "MP 40" }, + { WP_THOMPSON, "THOMPSON" }, + { WP_STEN, "STEN", }, + { WP_PANZERFAUST, "PANZERFAUST", }, + { WP_FLAMETHROWER, "FLAMETHROWER", }, + { WP_KAR98, "K43", }, + { WP_CARBINE, "M1 GARAND", }, + { WP_FG42, "FG42", }, + { WP_GARAND, "M1 GARAND", }, + { WP_MOBILE_MG42, "MOBILE MG42", }, + { WP_K43, "K43", }, + { WP_MORTAR, "MORTAR", }, + { WP_COLT, "COLT", }, + { WP_LUGER, "LUGER", }, + { WP_AKIMBO_COLT, "AKIMBO COLTS", }, + { WP_AKIMBO_LUGER, "AKIMBO LUGERS",}, + { WP_SILENCED_COLT, "COLT", }, + { WP_SILENCER, "LUGER", }, + { WP_AKIMBO_SILENCEDCOLT, "AKIMBO COLTS", }, + { WP_AKIMBO_SILENCEDLUGER, "AKIMBO LUGERS",}, + { WP_NONE, NULL, }, + { -1, NULL, }, +}; + +weaponType_t* WM_FindWeaponTypeForWeapon( weapon_t weapon ) { + weaponType_t* w = weaponTypes; + + if ( !weapon ) { + return NULL; + } + + while ( w->weapindex != -1 ) { + if ( w->weapindex == weapon ) { + return w; + } + w++; + } + return NULL; +} + +void WM_RegisterWeaponTypeShaders() { + weaponType_t* w = weaponTypes; + + while ( w->weapindex ) { +// w->shaderHandle = trap_R_RegisterShaderNoMip( w->shader ); + w++; + } +} + +void CG_MenuSetAnimation( playerInfo_t *pi, const char* legsAnim, const char* torsoAnim, qboolean force, qboolean clearpending ) { + lastLegsAnim = pi->legs.animation = CG_GetLimboAnimation( pi, legsAnim ); + lastTorsoAnim = pi->torso.animation = CG_GetLimboAnimation( pi, torsoAnim ); + + if ( force ) { + pi->legs.oldFrame = pi->legs.frame = pi->legs.animation->firstFrame; + pi->torso.oldFrame = pi->torso.frame = pi->torso.animation->firstFrame; + + pi->legs.frameTime = cg.time; + pi->torso.frameTime = cg.time; + + pi->legs.oldFrameModel = pi->legs.frameModel = pi->legs.animation->mdxFile; + pi->torso.oldFrameModel = pi->torso.frameModel = pi->torso.animation->mdxFile; + + pi->numPendingAnimations = 0; + } else { + pi->legs.oldFrame = pi->legs.frame; + pi->legs.oldFrameModel = pi->legs.frameModel; + pi->legs.frame = pi->legs.animation->firstFrame; + pi->torso.oldFrame = pi->torso.frame; + pi->torso.oldFrameModel = pi->torso.frameModel; + pi->torso.frame = pi->torso.animation->firstFrame; + + pi->legs.frameTime += 200; // Give them some time to lerp between animations + pi->torso.frameTime += 200; + } + + if ( clearpending ) { + pi->numPendingAnimations = 0; + } +} + +void CG_MenuPendingAnimation( playerInfo_t *pi, const char* legsAnim, const char* torsoAnim, int delay ) { + if ( pi->numPendingAnimations >= 4 ) { + return; + } + + if ( !pi->numPendingAnimations ) { + pi->pendingAnimations[pi->numPendingAnimations].pendingAnimationTime = cg.time + delay; + } else { + pi->pendingAnimations[pi->numPendingAnimations].pendingAnimationTime = pi->pendingAnimations[pi->numPendingAnimations - 1].pendingAnimationTime + delay; + } + pi->pendingAnimations[pi->numPendingAnimations].pendingLegsAnim = legsAnim; + pi->pendingAnimations[pi->numPendingAnimations].pendingTorsoAnim = torsoAnim; + + lastLegsAnim = CG_GetLimboAnimation( pi, legsAnim ); + lastTorsoAnim = CG_GetLimboAnimation( pi, torsoAnim ); + pi->numPendingAnimations++; +} + +void CG_MenuCheckPendingAnimation( playerInfo_t *pi ) { + int i; + + if ( pi->numPendingAnimations <= 0 ) { + return; + } + + if ( pi->pendingAnimations[0].pendingAnimationTime && pi->pendingAnimations[0].pendingAnimationTime < cg.time ) { + CG_MenuSetAnimation( pi, pi->pendingAnimations[0].pendingLegsAnim, pi->pendingAnimations[0].pendingTorsoAnim, qfalse, qfalse ); + + for ( i = 0; i < 3; i++ ) { + memcpy( &pi->pendingAnimations[i], &pi->pendingAnimations[i + 1], sizeof( pendingAnimation_t ) ); + } + + pi->numPendingAnimations--; + } +} + + +void CG_SetHudHeadLerpFrameAnimation( bg_character_t* ch, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= MAX_HD_ANIMATIONS ) { + CG_Error( "Bad animation number (CG_SetHudHeadLerpFrameAnimation): %i", newAnimation ); + } + + anim = &ch->hudheadanimations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; +} + +void CG_ClearHudHeadLerpFrame( bg_character_t* ch, lerpFrame_t *lf, int animationNumber ) { + lf->frameTime = lf->oldFrameTime = cg.time; + CG_SetHudHeadLerpFrameAnimation( ch, lf, animationNumber ); + lf->oldFrame = lf->frame = lf->animation->firstFrame; + lf->oldFrameModel = lf->frameModel = lf->animation->mdxFile; +} + +void CG_RunHudHeadLerpFrame( bg_character_t* ch, lerpFrame_t *lf, int newAnimation, float speedScale ) { + int f; + animation_t *anim; + + + + // see if the animation sequence is switching + if ( !lf->animation ) { + CG_ClearHudHeadLerpFrame( ch, lf, newAnimation ); + } else if ( newAnimation != lf->animationNumber ) { + CG_SetHudHeadLerpFrameAnimation( ch, lf, newAnimation ); + } + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( cg.time >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + lf->oldFrameModel = lf->frameModel; + + // get the next frame based on the animation + anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } + if ( cg.time < lf->animationTime ) { + lf->frameTime = lf->animationTime; // initial lerp + } else { + lf->frameTime = lf->oldFrameTime + anim->frameLerp; + } + f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + f *= speedScale; // adjust for haste, etc + if ( f >= anim->numFrames ) { + f -= anim->numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = anim->numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = cg.time; + } + } + lf->frame = anim->firstFrame + f; + lf->frameModel = anim->mdxFile; + if ( cg.time > lf->frameTime ) { + lf->frameTime = cg.time; + } + } + + if ( lf->frameTime > cg.time + 200 ) { + lf->frameTime = cg.time; + } + + if ( lf->oldFrameTime > cg.time ) { + lf->oldFrameTime = cg.time; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + +void CG_HudHeadAnimation( bg_character_t* ch, lerpFrame_t* lf, int *oldframe, int *frame, float *backlerp, hudHeadAnimNumber_t animation ) { +// centity_t *cent = &cg.predictedPlayerEntity; + + CG_RunHudHeadLerpFrame( ch, lf, (int)animation, 1.f ); + + *oldframe = lf->oldFrame; + *frame = lf->frame; + *backlerp = lf->backlerp; +} diff --git a/src/cgame/cg_playerstate.c b/src/cgame/cg_playerstate.c new file mode 100644 index 0000000..a8ee113 --- /dev/null +++ b/src/cgame/cg_playerstate.c @@ -0,0 +1,577 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +// cg_playerstate.c -- this file acts on changes in a new playerState_t +// With normal play, this will be done after local prediction, but when +// following another player or playing back a demo, it will be checked +// when the snapshot transitions like all the other entities + +#include "cg_local.h" + +/* +============== +CG_CheckAmmo + +If the ammo has gone low enough to generate the warning, play a sound +============== +*/ +void CG_CheckAmmo( void ) { + int i; + int total; + int weapons[MAX_WEAPONS / ( sizeof( int ) * 8 )]; + + // see about how many seconds of ammo we have remaining + memcpy( weapons, cg.snap->ps.weapons, sizeof( weapons ) ); + + if ( !weapons[0] && !weapons[1] ) { // (SA) we start out with no weapons, so don't make a click on startup + return; + } + + total = 0; + + for ( i = 0 ; i < WP_NUM_WEAPONS ; i++ ) + { + if ( !( weapons[0] & ( 1 << i ) ) ) { + continue; + } + switch ( i ) + { + case WP_PANZERFAUST: + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + case WP_LUGER: + case WP_COLT: + case WP_AKIMBO_COLT: + case WP_AKIMBO_SILENCEDCOLT: + case WP_AKIMBO_LUGER: + case WP_AKIMBO_SILENCEDLUGER: + case WP_SILENCER: + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + case WP_GARAND: + case WP_FG42: + case WP_FG42SCOPE: + case WP_KAR98: + case WP_GPG40: + case WP_CARBINE: + case WP_M7: + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + case WP_K43: + case WP_MORTAR_SET: + default: + total += cg.snap->ps.ammo[BG_FindAmmoForWeapon( i )] * 1000; + break; +// default: +// total += cg.snap->ps.ammo[BG_FindAmmoForWeapon(i)] * 200; +// break; + } + + if ( total >= 5000 ) { + cg.lowAmmoWarning = 0; + return; + } + } + + if ( !cg.lowAmmoWarning ) { + // play a sound on this transition + trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); + } + + if ( total == 0 ) { + cg.lowAmmoWarning = 2; + } else { + cg.lowAmmoWarning = 1; + } +} + +/* +============== +CG_DamageFeedback +============== +*/ +void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) { + float left, front, up; + float kick; + int health; + float scale; + vec3_t dir; + vec3_t angles; + float dist; + float yaw, pitch; + int slot; + viewDamage_t *vd; + + // show the attacking player's head and name in corner + cg.attackerTime = cg.time; + + // the lower on health you are, the greater the view kick will be + health = cg.snap->ps.stats[STAT_HEALTH]; + if ( health < 40 ) { + scale = 1; + } else { + scale = 40.0 / health; + } + kick = damage * scale; + + if ( kick < 5 ) { + kick = 5; + } + if ( kick > 10 ) { + kick = 10; + } + + // find a free slot + for ( slot = 0; slot < MAX_VIEWDAMAGE; slot++ ) { + if ( cg.viewDamage[slot].damageTime + cg.viewDamage[slot].damageDuration < cg.time ) { + break; + } + } + + if ( slot == MAX_VIEWDAMAGE ) { + return; // no free slots, never override or splats will suddenly disappear + + } + vd = &cg.viewDamage[slot]; + + // if yaw and pitch are both 255, make the damage always centered (falling, etc) + if ( yawByte == 255 && pitchByte == 255 ) { + vd->damageX = 0; + vd->damageY = 0; + cg.v_dmg_roll = 0; + cg.v_dmg_pitch = -kick; + } else { + // positional + pitch = pitchByte / 255.0 * 360; + yaw = yawByte / 255.0 * 360; + + angles[PITCH] = pitch; + angles[YAW] = yaw; + angles[ROLL] = 0; + + AngleVectors( angles, dir, NULL, NULL ); + VectorSubtract( vec3_origin, dir, dir ); + + front = DotProduct( dir, cg.refdef.viewaxis[0] ); + left = DotProduct( dir, cg.refdef.viewaxis[1] ); + up = DotProduct( dir, cg.refdef.viewaxis[2] ); + + dir[0] = front; + dir[1] = left; + dir[2] = 0; + dist = VectorLength( dir ); + if ( dist < 0.1 ) { + dist = 0.1; + } + + cg.v_dmg_roll = kick * left; + + cg.v_dmg_pitch = -kick * front; + + if ( front <= 0.1 ) { + front = 0.1; + } + vd->damageX = crandom() * 0.3 + - left / front; + vd->damageY = crandom() * 0.3 + up / dist; + } + + // clamp the position + if ( vd->damageX > 1.0 ) { + vd->damageX = 1.0; + } + if ( vd->damageX < -1.0 ) { + vd->damageX = -1.0; + } + + if ( vd->damageY > 1.0 ) { + vd->damageY = 1.0; + } + if ( vd->damageY < -1.0 ) { + vd->damageY = -1.0; + } + + // don't let the screen flashes vary as much + if ( kick > 10 ) { + kick = 10; + } + vd->damageValue = kick; + cg.v_dmg_time = cg.time + DAMAGE_TIME; + vd->damageTime = cg.snap->serverTime; + vd->damageDuration = kick * 50 * ( 1 + 2 * ( !vd->damageX && !vd->damageY ) ); + cg.damageTime = cg.snap->serverTime; + cg.damageIndex = slot; +} + + + + +/* +================ +CG_Respawn + +A respawn happened this snapshot +================ +*/ +void CG_Respawn( qboolean revived ) { + cg.serverRespawning = qfalse; // Arnout: just in case + + // no error decay on player movement + cg.thisFrameTeleport = qtrue; + + // need to reset client-side weapon animations + cg.predictedPlayerState.weapAnim = ( ( cg.predictedPlayerState.weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | PM_IdleAnimForWeapon( cg.snap->ps.weapon ); // reset weapon animations + cg.predictedPlayerState.weaponstate = WEAPON_READY; // hmm, set this? what to? + + // display weapons available + cg.weaponSelectTime = cg.time; + + cg.cursorHintIcon = 0; + cg.cursorHintTime = 0; + + cg.cameraMode = qfalse; //----(SA) get out of camera for sure + + // select the weapon the server says we are using + cg.weaponSelect = cg.snap->ps.weapon; + // DHM - Nerve :: Clear even more things on respawn + cg.zoomedBinoc = qfalse; + cg.zoomedScope = qfalse; + cg.zoomTime = 0; + cg.zoomval = 0; + + trap_SendConsoleCommand( "-zoom\n" ); + cg.binocZoomTime = 0; + + + // clear pmext + memset( &cg.pmext, 0, sizeof( cg.pmext ) ); + + cg.pmext.bAutoReload = ( cg_autoReload.integer > 0 ); + + cg.pmext.sprintTime = SPRINTTIME; + + if ( !revived ) { + cgs.limboLoadoutSelected = qfalse; + } + + if ( cg.predictedPlayerState.stats[STAT_PLAYER_CLASS] == PC_COVERTOPS ) { + cg.pmext.silencedSideArm = 1; + } + + cg.proneMovingTime = 0; + + // reset fog to world fog (if present) + trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_MAP,20,0,0,0,0 ); + // dhm - end +} + +extern char *eventnames[]; + +/* +============== +CG_CheckPlayerstateEvents +============== +*/ +void CG_CheckPlayerstateEvents_wolf( playerState_t *ps, playerState_t *ops ) { + int i; + int event; + centity_t *cent; +/* + if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) { + cent = &cg_entities[ ps->clientNum ]; + cent->currentState.event = ps->externalEvent; + cent->currentState.eventParm = ps->externalEventParm; + CG_EntityEvent( cent, cent->lerpOrigin ); + } +*/ + cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ]; + // go through the predictable events buffer + for ( i = ps->eventSequence - MAX_EVENTS ; i < ps->eventSequence ; i++ ) { + if ( ps->events[i & ( MAX_EVENTS - 1 )] != ops->events[i & ( MAX_EVENTS - 1 )] + || i >= ops->eventSequence ) { + event = ps->events[ i & ( MAX_EVENTS - 1 ) ]; + + cent->currentState.event = event; + cent->currentState.eventParm = ps->eventParms[ i & ( MAX_EVENTS - 1 ) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + } + } +} + +void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) { + int i; + int event; + centity_t *cent; + + if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) { + cent = &cg_entities[ ps->clientNum ]; + cent->currentState.event = ps->externalEvent; + cent->currentState.eventParm = ps->externalEventParm; + CG_EntityEvent( cent, cent->lerpOrigin ); + } + + cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ]; + // go through the predictable events buffer + for ( i = ps->eventSequence - MAX_EVENTS ; i < ps->eventSequence ; i++ ) { + // if we have a new predictable event + if ( i >= ops->eventSequence + // or the server told us to play another event instead of a predicted event we already issued + // or something the server told us changed our prediction causing a different event + || ( i > ops->eventSequence - MAX_EVENTS && ps->events[i & ( MAX_EVENTS - 1 )] != ops->events[i & ( MAX_EVENTS - 1 )] ) ) { + + event = ps->events[ i & ( MAX_EVENTS - 1 ) ]; + cent->currentState.event = event; + cent->currentState.eventParm = ps->eventParms[ i & ( MAX_EVENTS - 1 ) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + + cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event; + + cg.eventSequence++; + } + } +} + +/* +================== +CG_CheckChangedPredictableEvents +================== +*/ +void CG_CheckChangedPredictableEvents( playerState_t *ps ) { + int i; + int event; + centity_t *cent; + + cent = &cg.predictedPlayerEntity; + for ( i = ps->eventSequence - MAX_EVENTS ; i < ps->eventSequence ; i++ ) { + // + if ( i >= cg.eventSequence ) { + continue; + } + // if this event is not further back in than the maximum predictable events we remember + if ( i > cg.eventSequence - MAX_PREDICTED_EVENTS ) { + // if the new playerstate event is different from a previously predicted one + if ( ps->events[i & ( MAX_EVENTS - 1 )] != cg.predictableEvents[i & ( MAX_PREDICTED_EVENTS - 1 ) ] ) { + + event = ps->events[ i & ( MAX_EVENTS - 1 ) ]; + cent->currentState.event = event; + cent->currentState.eventParm = ps->eventParms[ i & ( MAX_EVENTS - 1 ) ]; + CG_EntityEvent( cent, cent->lerpOrigin ); + + cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event; + + if ( cg_showmiss.integer ) { + CG_Printf( "WARNING: changed predicted event\n" ); + } + } + } + } +} + +/* +================== +CG_CheckLocalSounds +================== +*/ +void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) { + // health changes of more than -1 should make pain sounds + if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) { + if ( ps->stats[STAT_HEALTH] > 0 ) { + CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH], qfalse ); + + cg.painTime = cg.time; + } + } + + // timelimit warnings + if ( cgs.timelimit > 0 && cgs.gamestate == GS_PLAYING ) { + int msec; + + msec = cg.time - cgs.levelStartTime; + + if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && ( msec > ( cgs.timelimit - 5 ) * 60 * 1000 ) && + ( msec < ( cgs.timelimit - 5 ) * 60 * 1000 + 1000 ) ) { + cg.timelimitWarnings |= 1; + if ( ps->persistant[PERS_TEAM] == TEAM_AXIS ) { + if ( cgs.media.fiveMinuteSound_g == -1 ) { + CG_SoundPlaySoundScript( cg.fiveMinuteSound_g, NULL, -1, qtrue ); + } else if ( cgs.media.fiveMinuteSound_g ) { + trap_S_StartLocalSound( cgs.media.fiveMinuteSound_g, CHAN_ANNOUNCER ); + } + } else if ( ps->persistant[PERS_TEAM] == TEAM_ALLIES ) { + if ( cgs.media.fiveMinuteSound_a == -1 ) { + CG_SoundPlaySoundScript( cg.fiveMinuteSound_a, NULL, -1, qtrue ); + } else if ( cgs.media.fiveMinuteSound_a ) { + trap_S_StartLocalSound( cgs.media.fiveMinuteSound_a, CHAN_ANNOUNCER ); + } + } + } + if ( cgs.timelimit > 2 && !( cg.timelimitWarnings & 2 ) && ( msec > ( cgs.timelimit - 2 ) * 60 * 1000 ) && + ( msec < ( cgs.timelimit - 2 ) * 60 * 1000 + 1000 ) ) { + cg.timelimitWarnings |= 2; + if ( ps->persistant[PERS_TEAM] == TEAM_AXIS ) { + if ( cgs.media.twoMinuteSound_g == -1 ) { + CG_SoundPlaySoundScript( cg.twoMinuteSound_g, NULL, -1, qtrue ); + } else if ( cgs.media.twoMinuteSound_g ) { + trap_S_StartLocalSound( cgs.media.twoMinuteSound_g, CHAN_ANNOUNCER ); + } + } else if ( ps->persistant[PERS_TEAM] == TEAM_ALLIES ) { + if ( cgs.media.twoMinuteSound_a == -1 ) { + CG_SoundPlaySoundScript( cg.twoMinuteSound_a, NULL, -1, qtrue ); + } else if ( cgs.media.twoMinuteSound_a ) { + trap_S_StartLocalSound( cgs.media.twoMinuteSound_a, CHAN_ANNOUNCER ); + } + } + } + if ( !( cg.timelimitWarnings & 4 ) && ( msec > ( cgs.timelimit ) * 60 * 1000 - 30000 ) && + ( msec < ( cgs.timelimit ) * 60 * 1000 - 29000 ) ) { + cg.timelimitWarnings |= 4; + if ( ps->persistant[PERS_TEAM] == TEAM_AXIS ) { + if ( cgs.media.thirtySecondSound_g == -1 ) { + CG_SoundPlaySoundScript( cg.thirtySecondSound_g, NULL, -1, qtrue ); + } else if ( cgs.media.thirtySecondSound_g ) { + trap_S_StartLocalSound( cgs.media.thirtySecondSound_g, CHAN_ANNOUNCER ); + } + } else if ( ps->persistant[PERS_TEAM] == TEAM_ALLIES ) { + if ( cgs.media.thirtySecondSound_a == -1 ) { + CG_SoundPlaySoundScript( cg.thirtySecondSound_a, NULL, -1, qtrue ); + } else if ( cgs.media.thirtySecondSound_a ) { + trap_S_StartLocalSound( cgs.media.thirtySecondSound_a, CHAN_ANNOUNCER ); + } + } + } + } +} + +/* +=============== +CG_TransitionPlayerState + +=============== +*/ +void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) { + // OSP - MV client handling + if ( cg.mvTotalClients > 0 ) { + if ( ps->clientNum != ops->clientNum ) { + cg.thisFrameTeleport = qtrue; + + // clear voicechat + cg.predictedPlayerEntity.voiceChatSpriteTime = 0; // CHECKME: should we do this here? + cg_entities[ps->clientNum].voiceChatSpriteTime = 0; + + *ops = *ps; + } + CG_CheckLocalSounds( ps, ops ); + return; + } + + // check for changing follow mode + if ( ps->clientNum != ops->clientNum ) { + cg.thisFrameTeleport = qtrue; + + // clear voicechat + cg.predictedPlayerEntity.voiceChatSpriteTime = 0; + cg_entities[ps->clientNum].voiceChatSpriteTime = 0; + + // make sure we don't get any unwanted transition effects + *ops = *ps; + + // DHM - Nerve :: After Limbo, make sure and do a CG_Respawn + if ( ps->clientNum == cg.clientNum ) { + ops->persistant[PERS_SPAWN_COUNT]--; + } + } + + if ( ps->eFlags & EF_FIRING ) { + cg.lastFiredWeaponTime = 0; + cg.weaponFireTime += cg.frametime; + } else { + if ( cg.weaponFireTime > 500 && cg.weaponFireTime ) { + cg.lastFiredWeaponTime = cg.time; + } + + cg.weaponFireTime = 0; + } + + // damage events (player is getting wounded) + if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) { + CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount ); + } + + // respawning + if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) { + CG_Respawn( ps->persistant[PERS_REVIVE_COUNT] != ops->persistant[PERS_REVIVE_COUNT] ? qtrue : qfalse ); + } + + if ( cg.mapRestart ) { + CG_Respawn( qfalse ); + cg.mapRestart = qfalse; + } + + if ( cg.snap->ps.pm_type != PM_INTERMISSION + && ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) { + CG_CheckLocalSounds( ps, ops ); + } + + // check for going low on ammo + CG_CheckAmmo(); + + if ( ps->eFlags & EF_PRONE_MOVING ) { + if ( ps->weapon == WP_BINOCULARS ) { + if ( ps->eFlags & EF_ZOOMING ) { + trap_SendConsoleCommand( "-zoom\n" ); + } + } + + if ( !( ops->eFlags & EF_PRONE_MOVING ) ) { + // ydnar: this screws up auto-switching when dynamite planted or grenade thrown/out of ammo + //% CG_FinishWeaponChange( cg.weaponSelect, ps->nextWeapon ); + + cg.proneMovingTime = cg.time; + } + } else if ( ops->eFlags & EF_PRONE_MOVING ) { + cg.proneMovingTime = -cg.time; + } + + if ( !( ps->eFlags & EF_PRONE ) && ops->eFlags & EF_PRONE ) { + if ( cg.weaponSelect == WP_MOBILE_MG42_SET ) { + CG_FinishWeaponChange( cg.weaponSelect, ps->nextWeapon ); + } + } + + // run events + CG_CheckPlayerstateEvents( ps, ops ); + + // smooth the ducking viewheight change + if ( ps->viewheight != ops->viewheight ) { + cg.duckChange = ps->viewheight - ops->viewheight; + cg.duckTime = cg.time; + } +} + diff --git a/src/cgame/cg_polybus.c b/src/cgame/cg_polybus.c new file mode 100644 index 0000000..9f33c48 --- /dev/null +++ b/src/cgame/cg_polybus.c @@ -0,0 +1,93 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +#include "cg_local.h" + +#define MAX_PB_BUFFERS 128 + +polyBuffer_t cg_polyBuffers[MAX_PB_BUFFERS]; +qboolean cg_polyBuffersInuse[MAX_PB_BUFFERS]; + +polyBuffer_t* CG_PB_FindFreePolyBuffer( qhandle_t shader, int numVerts, int numIndicies ) { + int i; + + // Gordon: first find one with the same shader if possible + for ( i = 0; i < MAX_PB_BUFFERS; i++ ) { + if ( cg_polyBuffers[i].shader != shader ) { + continue; + } + + if ( !cg_polyBuffersInuse[i] ) { + continue; + } + + if ( cg_polyBuffers[i].numIndicies + numIndicies >= MAX_PB_INDICIES ) { + continue; + } + + if ( cg_polyBuffers[i].numVerts + numVerts >= MAX_PB_VERTS ) { + continue; + } + + cg_polyBuffersInuse[i] = qtrue; + cg_polyBuffers[i].shader = shader; + + return &cg_polyBuffers[i]; + } + + // Gordon: or just find a free one + for ( i = 0; i < MAX_PB_BUFFERS; i++ ) { + if ( !cg_polyBuffersInuse[i] ) { + cg_polyBuffersInuse[i] = qtrue; + cg_polyBuffers[i].shader = shader; + cg_polyBuffers[i].numIndicies = 0; + cg_polyBuffers[i].numVerts = 0; + + return &cg_polyBuffers[i]; + } + } + + return NULL; +} + +void CG_PB_ClearPolyBuffers( void ) { + // Gordon: changed numIndicies and numVerts to be reset in CG_PB_FindFreePolyBuffer, not here (should save the cache misses we were prolly getting) + memset( cg_polyBuffersInuse, 0, sizeof( cg_polyBuffersInuse ) ); +} + +void CG_PB_RenderPolyBuffers( void ) { + int i; + + for ( i = 0; i < MAX_PB_BUFFERS; i++ ) { + if ( cg_polyBuffersInuse[i] ) { + trap_R_AddPolyBufferToScene( &cg_polyBuffers[i] ); + } + } +} diff --git a/src/cgame/cg_popupmessages.c b/src/cgame/cg_popupmessages.c new file mode 100644 index 0000000..9c26c82 --- /dev/null +++ b/src/cgame/cg_popupmessages.c @@ -0,0 +1,619 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +#define NUM_PM_STACK_ITEMS 32 +#define MAX_VISIBLE_ITEMS 5 + +#define NUM_PM_STACK_ITEMS_BIG 8 // Gordon: we shouldn't need many of these + +typedef struct pmStackItem_s pmListItem_t; +typedef struct pmStackItemBig_s pmListItemBig_t; + +struct pmStackItem_s { + popupMessageType_t type; + qboolean inuse; + int time; + char message[128]; + qhandle_t shader; + + pmListItem_t* next; +}; + +struct pmStackItemBig_s { + popupMessageBigType_t type; + qboolean inuse; + int time; + char message[128]; + qhandle_t shader; + + pmListItemBig_t* next; +}; + +pmListItem_t cg_pmStack[NUM_PM_STACK_ITEMS]; +pmListItem_t* cg_pmOldList; +pmListItem_t* cg_pmWaitingList; +pmListItemBig_t* cg_pmWaitingListBig; + +pmListItemBig_t cg_pmStackBig[NUM_PM_STACK_ITEMS_BIG]; + + +const char* cg_skillRewards[SK_NUM_SKILLS][NUM_SKILL_LEVELS - 1] = { + { "Binoculars", "Improved Physical Fitness", "Improved Health", "Trap Awareness" }, // battle sense + { "Improved use of Explosive Ammunition", "Improved Dexterity", "Improved Construction and Destruction", "a Flak Jacket" }, // explosives & construction + { "Medic Ammo", "Improved Resources", "Full Revive", "Adrenalin Self" }, // first aid + { "Improved Resources", "Improved Signals", "Improved Air and Ground Support", "Enemy Recognition" }, // signals + { "Improved use of Light Weapon Ammunition", "Faster Reload", "Improved Light Weapon Handling", "Dual-Wield Pistols" }, // light weapons + { "Improved Projectile Resources", "Heavy Weapon Proficiency", "Improved Dexterity", "Improved Weapon Handling" }, // heavy weapons + { "Improved use of Scoped Weapon Ammunition", "Improved use of Sabotage and Misdirection", "Breath Control", "Assassin" } // scoped weapons & military intelligence +}; + +void CG_PMItemBigSound( pmListItemBig_t* item ); + + + + +void CG_InitPMGraphics( void ) { + cgs.media.pmImages[PM_DYNAMITE] = trap_R_RegisterShaderNoMip( "gfx/limbo/cm_dynamite" ); + cgs.media.pmImages[PM_CONSTRUCTION] = trap_R_RegisterShaderNoMip( "sprites/voiceChat" ); + cgs.media.pmImages[PM_MINES] = trap_R_RegisterShaderNoMip( "sprites/voiceChat" ); + cgs.media.pmImages[PM_DEATH] = trap_R_RegisterShaderNoMip( "gfx/hud/pm_death" ); + cgs.media.pmImages[PM_MESSAGE] = trap_R_RegisterShaderNoMip( "sprites/voiceChat" ); + cgs.media.pmImages[PM_OBJECTIVE] = trap_R_RegisterShaderNoMip( "sprites/objective" ); + cgs.media.pmImages[PM_DESTRUCTION] = trap_R_RegisterShaderNoMip( "sprites/voiceChat" ); + cgs.media.pmImages[PM_TEAM] = trap_R_RegisterShaderNoMip( "sprites/voiceChat" ); + + cgs.media.pmImageAlliesConstruct = trap_R_RegisterShaderNoMip( "gfx/hud/pm_constallied" ); + cgs.media.pmImageAxisConstruct = trap_R_RegisterShaderNoMip( "gfx/hud/pm_constaxis" ); + cgs.media.pmImageAlliesMine = trap_R_RegisterShaderNoMip( "gfx/hud/pm_mineallied" ); + cgs.media.pmImageAxisMine = trap_R_RegisterShaderNoMip( "gfx/hud/pm_mineaxis" ); + cgs.media.hintKey = trap_R_RegisterShaderNoMip( "gfx/hud/keyboardkey_old" ); +} + +void CG_InitPM( void ) { + memset( &cg_pmStack, 0, sizeof( cg_pmStack ) ); + memset( &cg_pmStackBig, 0, sizeof( cg_pmStackBig ) ); + + cg_pmOldList = NULL; + cg_pmWaitingList = NULL; + cg_pmWaitingListBig = NULL; +} + +#define PM_FADETIME 2500 +#define PM_WAITTIME 2000 + +#define PM_FADETIME_BIG 1000 +#define PM_WAITTIME_BIG 3500 + +int CG_TimeForPopup( popupMessageType_t type ) { + switch ( type ) { + default: + return 1000; + } +} + +int CG_TimeForBigPopup( popupMessageBigType_t type ) { + switch ( type ) { + default: + return 2500; + } +} + +void CG_AddToListFront( pmListItem_t** list, pmListItem_t* item ) { + item->next = *list; + *list = item; +} + +void CG_UpdatePMLists( void ) { + pmListItem_t* listItem; + pmListItem_t* lastItem; + pmListItemBig_t* listItem2; + + if ( ( listItem = cg_pmWaitingList ) ) { + int t = ( CG_TimeForPopup( listItem->type ) + listItem->time ); + if ( cg.time > t ) { + if ( listItem->next ) { + // there's another item waiting to come on, so move to old list + cg_pmWaitingList = listItem->next; + cg_pmWaitingList->time = cg.time; // set time we popped up at + + CG_AddToListFront( &cg_pmOldList, listItem ); + } else { + if ( cg.time > t + PM_WAITTIME + PM_FADETIME ) { + // we're gone completely + cg_pmWaitingList = NULL; + listItem->inuse = qfalse; + listItem->next = NULL; + } else { + // just sit where we are, no pressure to do anything... + } + } + } + } + + listItem = cg_pmOldList; + lastItem = NULL; + while ( listItem ) { + int t = ( CG_TimeForPopup( listItem->type ) + listItem->time + PM_WAITTIME + PM_FADETIME ); + if ( cg.time > t ) { + // nuke this, and everything below it (though there shouldn't BE anything below us anyway) + pmListItem_t* next; + + if ( !lastItem ) { + // we're the top of the old list, so set to NULL + cg_pmOldList = NULL; + } else { + lastItem->next = NULL; + } + + do { + next = listItem->next; + + listItem->next = NULL; + listItem->inuse = qfalse; + + } while ( ( listItem = next ) ); + + + break; + } + + lastItem = listItem; + listItem = listItem->next; + } + + + if ( ( listItem2 = cg_pmWaitingListBig ) ) { + int t = CG_TimeForBigPopup( listItem2->type ) + listItem2->time; + if ( cg.time > t ) { + if ( listItem2->next ) { + // there's another item waiting to come on, so kill us and shove the next one to the front + cg_pmWaitingListBig = listItem2->next; + cg_pmWaitingListBig->time = cg.time; // set time we popped up at + + CG_PMItemBigSound( cg_pmWaitingListBig ); + + listItem2->inuse = qfalse; + listItem2->next = NULL; + } else { + if ( cg.time > t + PM_WAITTIME + PM_FADETIME ) { + // we're gone completely + cg_pmWaitingListBig = NULL; + listItem2->inuse = qfalse; + listItem2->next = NULL; + } else { + // just sit where we are, no pressure to do anything... + } + } + } + } +} + +pmListItemBig_t* CG_FindFreePMItem2( void ) { + int i = 0; + for ( ; i < NUM_PM_STACK_ITEMS_BIG; i++ ) { + if ( !cg_pmStackBig[i].inuse ) { + return &cg_pmStackBig[i]; + } + } + + return NULL; +} + +pmListItem_t* CG_FindFreePMItem( void ) { + pmListItem_t* listItem; + pmListItem_t* lastItem; + + int i = 0; + for ( ; i < NUM_PM_STACK_ITEMS; i++ ) { + if ( !cg_pmStack[i].inuse ) { + return &cg_pmStack[i]; + } + } + + // no totally free items, so just grab the last item in the oldlist + if ( ( lastItem = listItem = cg_pmOldList ) ) { + while ( listItem->next ) { + lastItem = listItem; + listItem = listItem->next; + } + + if ( lastItem == cg_pmOldList ) { + cg_pmOldList = NULL; + } else { + lastItem->next = NULL; + } + + listItem->inuse = qfalse; + + return listItem; + } else { + // there is no old list... PANIC! + return NULL; + } +} + +void CG_AddPMItem( popupMessageType_t type, const char* message, qhandle_t shader ) { + pmListItem_t* listItem; + char* end; + + if ( !message || !*message ) { + return; + } + if ( type < 0 || type >= PM_NUM_TYPES ) { + CG_Printf( "Invalid popup type: %d\n", type ); + return; + } + + listItem = CG_FindFreePMItem(); + + if ( !listItem ) { + return; + } + + if ( shader ) { + listItem->shader = shader; + } else { + listItem->shader = cgs.media.pmImages[type]; + } + + listItem->inuse = qtrue; + listItem->type = type; + Q_strncpyz( listItem->message, message, sizeof( cg_pmStack[0].message ) ); + + // rain - moved this: print and THEN chop off the newline, as the + // console deals with newlines perfectly. We do chop off the newline + // at the end, if any, though. + if ( listItem->message[strlen( listItem->message ) - 1] == '\n' ) { + listItem->message[strlen( listItem->message ) - 1] = 0; + } + + trap_Print( va( "%s\n", listItem->message ) ); + + // rain - added parens + while ( ( end = strchr( listItem->message, '\n' ) ) ) { + *end = '\0'; + } + + // rain - don't eat popups for empty lines + if ( *listItem->message == '\0' ) { + return; + } + + if ( !cg_pmWaitingList ) { + cg_pmWaitingList = listItem; + listItem->time = cg.time; + } else { + pmListItem_t* loop = cg_pmWaitingList; + while ( loop->next ) { + loop = loop->next; + } + + loop->next = listItem; + } +} + +void CG_PMItemBigSound( pmListItemBig_t* item ) { + if ( !cg.snap ) { + return; + } + + switch ( item->type ) { + case PM_RANK: + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.sndRankUp ); + break; + case PM_SKILL: + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.sndSkillUp ); + break; + default: + break; + } +} + +void CG_AddPMItemBig( popupMessageBigType_t type, const char* message, qhandle_t shader ) { + pmListItemBig_t* listItem = CG_FindFreePMItem2(); + if ( !listItem ) { + return; + } + + if ( shader ) { + listItem->shader = shader; + } else { + listItem->shader = cgs.media.pmImages[type]; + } + + listItem->inuse = qtrue; + listItem->type = type; + listItem->next = NULL; + Q_strncpyz( listItem->message, message, sizeof( cg_pmStackBig[0].message ) ); + + if ( !cg_pmWaitingListBig ) { + cg_pmWaitingListBig = listItem; + listItem->time = cg.time; + + CG_PMItemBigSound( listItem ); + } else { + pmListItemBig_t* loop = cg_pmWaitingListBig; + while ( loop->next ) { + loop = loop->next; + } + + loop->next = listItem; + } +} + +#define PM_ICON_SIZE_NORMAL 20 +#define PM_ICON_SIZE_SMALL 12 +void CG_DrawPMItems( void ) { + vec4_t colour = { 0.f, 0.f, 0.f, 1.f }; + vec4_t colourText = { 1.f, 1.f, 1.f, 1.f }; + float t; + int i, size; + pmListItem_t* listItem = cg_pmOldList; + float y = 360; + + if ( cg_drawSmallPopupIcons.integer ) { + size = PM_ICON_SIZE_SMALL; + + y += 4; + } else { + size = PM_ICON_SIZE_NORMAL; + } + + if ( cg.snap->ps.persistant[PERS_RESPAWNS_LEFT] >= 0 ) { + y -= 20; + } + + if ( !cg_pmWaitingList ) { + return; + } + + t = cg_pmWaitingList->time + CG_TimeForPopup( cg_pmWaitingList->type ) + PM_WAITTIME; + if ( cg.time > t ) { + colourText[3] = colour[3] = 1 - ( ( cg.time - t ) / (float)PM_FADETIME ); + } + + trap_R_SetColor( colourText ); + CG_DrawPic( 4, y, size, size, cg_pmWaitingList->shader ); + trap_R_SetColor( NULL ); + CG_Text_Paint_Ext( 4 + size + 2, y + 12, 0.2f, 0.2f, colourText, cg_pmWaitingList->message, 0, 0, 0, &cgs.media.limboFont2 ); + + for ( i = 0; i < 4 && listItem; i++, listItem = listItem->next ) { + y -= size + 2; + + t = listItem->time + CG_TimeForPopup( listItem->type ) + PM_WAITTIME; + if ( cg.time > t ) { + colourText[3] = colour[3] = 1 - ( ( cg.time - t ) / (float)PM_FADETIME ); + } else { + colourText[3] = colour[3] = 1.f; + } + + trap_R_SetColor( colourText ); + CG_DrawPic( 4, y, size, size, listItem->shader ); + trap_R_SetColor( NULL ); + CG_Text_Paint_Ext( 4 + size + 2, y + 12, 0.2f, 0.2f, colourText, listItem->message, 0, 0, 0, &cgs.media.limboFont2 ); + } +} + +void CG_DrawPMItemsBig( void ) { + vec4_t colour = { 0.f, 0.f, 0.f, 1.f }; + vec4_t colourText = { 1.f, 1.f, 1.f, 1.f }; + float t; + float y = 270; + float w; + + if ( !cg_pmWaitingListBig ) { + return; + } + + t = cg_pmWaitingListBig->time + CG_TimeForBigPopup( cg_pmWaitingListBig->type ) + PM_WAITTIME_BIG; + if ( cg.time > t ) { + colourText[3] = colour[3] = 1 - ( ( cg.time - t ) / (float)PM_FADETIME_BIG ); + } + + trap_R_SetColor( colourText ); + CG_DrawPic( 640 - 56, y, 48, 48, cg_pmWaitingListBig->shader ); + trap_R_SetColor( NULL ); + + + w = CG_Text_Width_Ext( cg_pmWaitingListBig->message, 0.22f, 0, &cgs.media.limboFont2 ); + CG_Text_Paint_Ext( 640 - 4 - w, y + 56, 0.22f, 0.24f, colourText, cg_pmWaitingListBig->message, 0, 0, 0, &cgs.media.limboFont2 ); +} + +const char* CG_GetPMItemText( centity_t* cent ) { + switch ( cent->currentState.effect1Time ) { + case PM_DYNAMITE: + switch ( cent->currentState.effect2Time ) { + case 0: + return va( "Planted at %s.", CG_ConfigString( CS_OID_TRIGGERS + cent->currentState.effect3Time ) ); + case 1: + return va( "Defused at %s.", CG_ConfigString( CS_OID_TRIGGERS + cent->currentState.effect3Time ) ); + } + break; + case PM_CONSTRUCTION: + switch ( cent->currentState.effect2Time ) { + case - 1: + return CG_ConfigString( CS_STRINGS + cent->currentState.effect3Time ); + case 0: + return va( "%s has been constructed.", CG_ConfigString( CS_OID_TRIGGERS + cent->currentState.effect3Time ) ); + } + break; + case PM_DESTRUCTION: + switch ( cent->currentState.effect2Time ) { + case 0: + return va( "%s has been damaged.", CG_ConfigString( CS_OID_TRIGGERS + cent->currentState.effect3Time ) ); + case 1: + return va( "%s has been destroyed.", CG_ConfigString( CS_OID_TRIGGERS + cent->currentState.effect3Time ) ); + } + break; + case PM_MINES: + if ( cgs.clientinfo[cg.clientNum].team == cent->currentState.effect2Time ) { + return NULL; + } + return va( "Spotted by %s^7 at %s", cgs.clientinfo[cent->currentState.effect3Time].name, BG_GetLocationString( cent->currentState.origin ) ); + case PM_OBJECTIVE: + switch ( cent->currentState.density ) { + case 0: + return va( "%s have stolen %s!", cent->currentState.effect2Time == TEAM_ALLIES ? "Allies" : "Axis", CG_ConfigString( CS_STRINGS + cent->currentState.effect3Time ) ); + case 1: + return va( "%s have returned %s!", cent->currentState.effect2Time == TEAM_ALLIES ? "Allies" : "Axis", CG_ConfigString( CS_STRINGS + cent->currentState.effect3Time ) ); + } + break; + case PM_TEAM: + switch ( cent->currentState.density ) { + case 0: // joined + { + const char* teamstr = NULL; + switch ( cent->currentState.effect2Time ) { + case TEAM_AXIS: + teamstr = "Axis team"; + break; + case TEAM_ALLIES: + teamstr = "Allied team"; + break; + default: + teamstr = "Spectators"; + break; + } + + return va( "%s^7 has joined the %s^7!", cgs.clientinfo[cent->currentState.effect3Time].name, teamstr ); + } + case 1: + return va( "%s^7 disconnected", cgs.clientinfo[cent->currentState.effect3Time].name ); + } + } + + return NULL; +} + +void CG_PlayPMItemSound( centity_t *cent ) { + switch ( cent->currentState.effect1Time ) { + case PM_DYNAMITE: + switch ( cent->currentState.effect2Time ) { + case 0: + if ( cent->currentState.teamNum == TEAM_AXIS ) { + CG_SoundPlaySoundScript( "axis_hq_dynamite_planted", NULL, -1, qtrue ); + } else { + CG_SoundPlaySoundScript( "allies_hq_dynamite_planted", NULL, -1, qtrue ); + } + break; + case 1: + if ( cent->currentState.teamNum == TEAM_AXIS ) { + CG_SoundPlaySoundScript( "axis_hq_dynamite_defused", NULL, -1, qtrue ); + } else { + CG_SoundPlaySoundScript( "allies_hq_dynamite_defused", NULL, -1, qtrue ); + } + break; + } + break; + case PM_MINES: + if ( cgs.clientinfo[cg.clientNum].team != cent->currentState.effect2Time ) { + // inverted teams + if ( cent->currentState.effect2Time == TEAM_AXIS ) { + CG_SoundPlaySoundScript( "allies_hq_mines_spotted", NULL, -1, qtrue ); + } else { + CG_SoundPlaySoundScript( "axis_hq_mines_spotted", NULL, -1, qtrue ); + } + } + break; + case PM_OBJECTIVE: + switch ( cent->currentState.density ) { + case 0: + if ( cent->currentState.effect2Time == TEAM_AXIS ) { + CG_SoundPlaySoundScript( "axis_hq_objective_taken", NULL, -1, qtrue ); + } else { + CG_SoundPlaySoundScript( "allies_hq_objective_taken", NULL, -1, qtrue ); + } + break; + case 1: + if ( cent->currentState.effect2Time == TEAM_AXIS ) { + CG_SoundPlaySoundScript( "axis_hq_objective_secure", NULL, -1, qtrue ); + } else { + CG_SoundPlaySoundScript( "allies_hq_objective_secure", NULL, -1, qtrue ); + } + break; + } + break; + default: + break; + } +} + +qhandle_t CG_GetPMItemIcon( centity_t* cent ) { + switch ( cent->currentState.effect1Time ) { + case PM_CONSTRUCTION: + if ( cent->currentState.density == TEAM_AXIS ) { + return cgs.media.pmImageAxisConstruct; + } + return cgs.media.pmImageAlliesConstruct; + case PM_MINES: + if ( cent->currentState.effect2Time == TEAM_AXIS ) { + return cgs.media.pmImageAlliesMine; + } + return cgs.media.pmImageAxisMine; + default: + return cgs.media.pmImages[cent->currentState.effect1Time]; + } + + return 0; +} + + + +void CG_DrawKeyHint( rectDef_t* rect, const char* binding ) { +/* int k1, k2; + char buffer[256]; + char k[2] = { 0, 0 }; + float w; + + trap_Key_KeysForBinding( binding, &k1, &k2 ); + + if( k1 != -1 ) { + trap_Key_KeynumToStringBuf( k1, buffer, 256 ); + if( strlen( buffer ) != 1 ) { + if( k2 != -1 ) { + trap_Key_KeynumToStringBuf( k2, buffer, 256 ); + if( strlen( buffer ) == 1 ) { + *k = toupper( *buffer ); + } + } + } else { + *k = toupper( *buffer ); + } + } + + if( !*k ) { + return; + } + + CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.hintKey ); + + w = CG_Text_Width_Ext( k, 0.2f, 0, &cgs.media.limboFont1 ); + CG_Text_Paint_Ext( rect->x + ((rect->w - w) * 0.5f), rect->y + 14, 0.2f, 0.2f, colorWhite, k, 0, 0, 0, &cgs.media.limboFont1 );*/ +} diff --git a/src/cgame/cg_predict.c b/src/cgame/cg_predict.c new file mode 100644 index 0000000..3262828 --- /dev/null +++ b/src/cgame/cg_predict.c @@ -0,0 +1,1140 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +// cg_predict.c -- this file generates cg.predictedPlayerState by either +// interpolating between snapshots from the server or locally predicting +// ahead the client's movement. +// It also handles local physics interaction, like fragments bouncing off walls + +#include "cg_local.h" + +/*static*/ pmove_t cg_pmove; + +static int cg_numSolidEntities; +static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT]; +static int cg_numSolidFTEntities; +static centity_t *cg_solidFTEntities[MAX_ENTITIES_IN_SNAPSHOT]; +static int cg_numTriggerEntities; +static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT]; + +/* +==================== +CG_BuildSolidList + +When a new cg.snap has been set, this function builds a sublist +of the entities that are actually solid, to make for more +efficient collision detection +==================== +*/ +void CG_BuildSolidList( void ) { + int i; + centity_t *cent; + snapshot_t *snap; + entityState_t *ent; + + cg_numSolidEntities = 0; + cg_numSolidFTEntities = 0; + cg_numTriggerEntities = 0; + + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + snap = cg.nextSnap; + } else { + snap = cg.snap; + } + + for ( i = 0 ; i < snap->numEntities ; i++ ) { + cent = &cg_entities[ snap->entities[ i ].number ]; + ent = ¢->currentState; + + // rain - don't clip against temporarily non-solid SOLID_BMODELS + // (e.g. constructibles); use current state so prediction isn't fubar + if ( cent->currentState.solid == SOLID_BMODEL && + ( cent->currentState.eFlags & EF_NONSOLID_BMODEL ) ) { + continue; + } + + if ( ent->eType == ET_ITEM || + ent->eType == ET_PUSH_TRIGGER || + ent->eType == ET_TELEPORT_TRIGGER || + ent->eType == ET_CONCUSSIVE_TRIGGER || + ent->eType == ET_OID_TRIGGER +#ifdef VISIBLE_TRIGGERS + || ent->eType == ET_TRIGGER_MULTIPLE + || ent->eType == ET_TRIGGER_FLAGONLY + || ent->eType == ET_TRIGGER_FLAGONLY_MULTIPLE +#endif // VISIBLE_TRIGGERS + ) { + + cg_triggerEntities[cg_numTriggerEntities] = cent; + cg_numTriggerEntities++; + continue; + } + + if ( ent->eType == ET_CONSTRUCTIBLE ) { + cg_triggerEntities[cg_numTriggerEntities] = cent; + cg_numTriggerEntities++; + } + + if ( cent->nextState.solid ) { +/* if(cg_fastSolids.integer) { // Gordon: "optimization" (disabling until i fix it) + vec3_t vec; + float len; + + cg_solidFTEntities[cg_numSolidFTEntities] = cent; + cg_numSolidFTEntities++; + + // FIXME: use range to bbox, not to origin + if ( cent->nextState.solid == SOLID_BMODEL ) { + VectorAdd( cgs.inlineModelMidpoints[ cent->currentState.modelindex ], cent->lerpOrigin, vec ); + VectorSubtract( vec, cg.predictedPlayerEntity.lerpOrigin, vec ); + } else { + VectorSubtract( cent->lerpOrigin, cg.predictedPlayerEntity.lerpOrigin, vec ); + } + if((len = DotProduct( vec, vec )) < (512 * 512)) { + cg_solidEntities[cg_numSolidEntities] = cent; + cg_numSolidEntities++; + continue; + } + } else*/{ + cg_solidEntities[cg_numSolidEntities] = cent; + cg_numSolidEntities++; + + cg_solidFTEntities[cg_numSolidFTEntities] = cent; + cg_numSolidFTEntities++; + continue; + } + } + } +} + +/* +==================== +CG_ClipMoveToEntities + +==================== +*/ +static void CG_ClipMoveToEntities( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask, int capsule, trace_t *tr ) { + int i, x, zd, zu; + trace_t trace; + entityState_t *ent; + clipHandle_t cmodel; + vec3_t bmins, bmaxs; + vec3_t origin, angles; + centity_t *cent; + + for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { + cent = cg_solidEntities[ i ]; + ent = ¢->currentState; + + if ( ent->number == skipNumber ) { + continue; + } + + if ( ent->solid == SOLID_BMODEL ) { + // special value for bmodel + cmodel = trap_CM_InlineModel( ent->modelindex ); +// VectorCopy( cent->lerpAngles, angles ); +// VectorCopy( cent->lerpOrigin, origin ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.physicsTime, angles, qtrue, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin, qfalse, cent->currentState.effect2Time ); + } else { + // encoded bbox + x = ( ent->solid & 255 ); + zd = ( ( ent->solid >> 8 ) & 255 ); + zu = ( ( ent->solid >> 16 ) & 255 ) - 32; + + bmins[0] = bmins[1] = -x; + bmaxs[0] = bmaxs[1] = x; + bmins[2] = -zd; + bmaxs[2] = zu; + + //cmodel = trap_CM_TempCapsuleModel( bmins, bmaxs ); + cmodel = trap_CM_TempBoxModel( bmins, bmaxs ); + + VectorCopy( vec3_origin, angles ); + VectorCopy( cent->lerpOrigin, origin ); + } + // MrE: use bbox of capsule + if ( capsule ) { + trap_CM_TransformedCapsuleTrace( &trace, start, end, + mins, maxs, cmodel, mask, origin, angles ); + } else { + trap_CM_TransformedBoxTrace( &trace, start, end, + mins, maxs, cmodel, mask, origin, angles ); + } + + if ( trace.allsolid || trace.fraction < tr->fraction ) { + trace.entityNum = ent->number; + *tr = trace; + } else if ( trace.startsolid ) { + tr->startsolid = qtrue; + } + if ( tr->allsolid ) { + return; + } + } +} + +static void CG_ClipMoveToEntities_FT( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask, int capsule, trace_t *tr ) { + int i, x, zd, zu; + trace_t trace; + entityState_t *ent; + clipHandle_t cmodel; + vec3_t bmins, bmaxs; + vec3_t origin, angles; + centity_t *cent; + + for ( i = 0 ; i < cg_numSolidFTEntities ; i++ ) { + cent = cg_solidFTEntities[ i ]; + ent = ¢->currentState; + + if ( ent->number == skipNumber ) { + continue; + } + + if ( ent->solid == SOLID_BMODEL ) { + // special value for bmodel + cmodel = trap_CM_InlineModel( ent->modelindex ); +// VectorCopy( cent->lerpAngles, angles ); +// VectorCopy( cent->lerpOrigin, origin ); + BG_EvaluateTrajectory( ¢->currentState.apos, cg.physicsTime, angles, qtrue, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin, qfalse, cent->currentState.effect2Time ); + } else { + // encoded bbox + x = ( ent->solid & 255 ); + zd = ( ( ent->solid >> 8 ) & 255 ); + zu = ( ( ent->solid >> 16 ) & 255 ) - 32; + + bmins[0] = bmins[1] = -x; + bmaxs[0] = bmaxs[1] = x; + bmins[2] = -zd; + bmaxs[2] = zu; + + cmodel = trap_CM_TempCapsuleModel( bmins, bmaxs ); + + VectorCopy( vec3_origin, angles ); + VectorCopy( cent->lerpOrigin, origin ); + } + // MrE: use bbox of capsule + if ( capsule ) { + trap_CM_TransformedCapsuleTrace( &trace, start, end, + mins, maxs, cmodel, mask, origin, angles ); + } else { + trap_CM_TransformedBoxTrace( &trace, start, end, + mins, maxs, cmodel, mask, origin, angles ); + } + + if ( trace.allsolid || trace.fraction < tr->fraction ) { + trace.entityNum = ent->number; + *tr = trace; + } else if ( trace.startsolid ) { + tr->startsolid = qtrue; + } + if ( tr->allsolid ) { + return; + } + } +} + +/* +================ +CG_Trace +================ +*/ +void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask ) { + trace_t t; + + trap_CM_BoxTrace( &t, start, end, mins, maxs, 0, mask ); + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + // check all other solid models + CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, qfalse, &t ); + + *result = t; +} + +void CG_Trace_World( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, + int skipNumber, int mask ) { + trace_t t; + + trap_CM_BoxTrace( &t, start, end, mins, maxs, 0, mask ); + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + + *result = t; +} + +void CG_FTTrace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { + trace_t t; + + + trap_CM_BoxTrace( &t, start, end, mins, maxs, 0, mask ); + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + // check all other solid models + + CG_ClipMoveToEntities_FT( start, mins, maxs, end, skipNumber, mask, qfalse, &t ); + + *result = t; +} + + +/* +================ +CG_TraceCapsule +================ +*/ +void CG_TraceCapsule( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { + trace_t t; + + trap_CM_CapsuleTrace( &t, start, end, mins, maxs, 0, mask ); + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + // check all other solid models + CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, qtrue, &t ); + + *result = t; +} + +void CG_TraceCapsule_World( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { + trace_t t; + trap_CM_CapsuleTrace( &t, start, end, mins, maxs, 0, mask ); + + t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + *result = t; +} +/* +================ +CG_PointContents +================ +*/ +int CG_PointContents( const vec3_t point, int passEntityNum ) { + int i; + entityState_t *ent; + centity_t *cent; + clipHandle_t cmodel; + int contents; + + contents = trap_CM_PointContents( point, 0 ); + + for ( i = 0 ; i < cg_numSolidEntities ; i++ ) { + cent = cg_solidEntities[ i ]; + + ent = ¢->currentState; + + if ( ent->number == passEntityNum ) { + continue; + } + + if ( ent->solid != SOLID_BMODEL ) { // special value for bmodel + continue; + } + + cmodel = trap_CM_InlineModel( ent->modelindex ); + if ( !cmodel ) { + continue; + } + + contents |= trap_CM_TransformedPointContents( point, cmodel, cent->lerpOrigin, cent->lerpAngles ); + // Gordon: again, need to use the projected water position to allow for moving entity based water. +// contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles ); + } + + return contents; +} + + +/* +======================== +CG_InterpolatePlayerState + +Generates cg.predictedPlayerState by interpolating between +cg.snap->player_state and cg.nextFrame->player_state +======================== +*/ +static void CG_InterpolatePlayerState( qboolean grabAngles ) { + float f; + int i; + playerState_t *out; + snapshot_t *prev, *next; + + out = &cg.predictedPlayerState; + prev = cg.snap; + next = cg.nextSnap; + + *out = cg.snap->ps; + + if ( cg.showGameView ) { + return; + } + + // if we are still allowing local input, short circuit the view angles + if ( grabAngles ) { + usercmd_t cmd; + int cmdNum; + + cmdNum = trap_GetCurrentCmdNumber(); + trap_GetUserCmd( cmdNum, &cmd ); + + // rain - added tracemask + PM_UpdateViewAngles( out, &cg.pmext, &cmd, CG_Trace, MASK_PLAYERSOLID ); + } + + // if the next frame is a teleport, we can't lerp to it + if ( cg.nextFrameTeleport ) { + return; + } + + if ( !next || next->serverTime <= prev->serverTime ) { + return; + } + + f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime ); + + i = next->ps.bobCycle; + if ( i < prev->ps.bobCycle ) { + i += 256; // handle wraparound + } + out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle ); + + for ( i = 0 ; i < 3 ; i++ ) { + out->origin[i] = prev->ps.origin[i] + f * ( next->ps.origin[i] - prev->ps.origin[i] ); + if ( !grabAngles ) { + out->viewangles[i] = LerpAngle( + prev->ps.viewangles[i], next->ps.viewangles[i], f ); + } + out->velocity[i] = prev->ps.velocity[i] + + f * ( next->ps.velocity[i] - prev->ps.velocity[i] ); + } + +} + +/* +=================== +CG_TouchItem +=================== +*/ +static void CG_TouchItem( centity_t *cent ) { + gitem_t *item; + + return; + + if ( !cg_predictItems.integer ) { + return; + } + + if ( !cg_autoactivate.integer ) { + return; + } + + if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) { + return; + } + + // never pick an item up twice in a prediction + if ( cent->miscTime == cg.time ) { + return; + } + + if ( !BG_CanItemBeGrabbed( ¢->currentState, &cg.predictedPlayerState, cgs.clientinfo[cg.snap->ps.clientNum].skill, cgs.clientinfo[cg.snap->ps.clientNum].team ) ) { + return; // can't hold it + } + + item = &bg_itemlist[ cent->currentState.modelindex ]; + + // force activate only for weapons you don't have + if ( item->giType == IT_WEAPON ) { + if ( item->giTag != WP_AMMO ) { + if ( !COM_BitCheck( cg.predictedPlayerState.weapons, item->giTag ) ) { + return; // force activate only + } + } + } + + // OSP - Do it here rather than forcing gamestate into BG_CanItemBeGrabbed + if ( cgs.gamestate != GS_PLAYING && + item->giType != IT_WEAPON && + item->giType != IT_AMMO && + item->giType != IT_HEALTH ) { + return; + } + + // OSP - special case for panzers, as server may not allow us to pick them up + // let the server tell us for sure that we got it + if ( item->giType == IT_WEAPON && item->giTag == WP_PANZERFAUST ) { + return; + } + + + // (SA) no prediction of books/clipboards + if ( item->giType == IT_HOLDABLE ) { + if ( item->giTag >= HI_BOOK1 && item->giTag <= HI_BOOK3 ) { + return; + } + } + + // (SA) treasure needs to be activeated, no touch + if ( item->giType == IT_TREASURE ) { + return; + } + + // Special case for flags. + // We don't predict touching our own flag + if ( cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_AXIS && + item->giTag == PW_REDFLAG ) { + return; + } + if ( cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_ALLIES && + item->giTag == PW_BLUEFLAG ) { + return; + } + + + // grab it + BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex, &cg.predictedPlayerState ); + + // remove it from the frame so it won't be drawn + cent->currentState.eFlags |= EF_NODRAW; + + // don't touch it again this prediction + cent->miscTime = cg.time; + + // if its a weapon, give them some predicted ammo so the autoswitch will work + if ( item->giType == IT_WEAPON ) { + COM_BitSet( cg.predictedPlayerState.weapons, item->giTag ); + + if ( !cg.predictedPlayerState.ammo[ BG_FindAmmoForWeapon( item->giTag )] ) { + cg.predictedPlayerState.ammo[ BG_FindAmmoForWeapon( item->giTag )] = 1; + } + } +} + +void CG_AddDirtBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale, float width, float height, float alpha, qhandle_t shader ); + + +/* +========================= +CG_TouchTriggerPrediction + +Predict push triggers and items +========================= +*/ +static void CG_TouchTriggerPrediction( void ) { + int i; +// trace_t trace; + entityState_t *ent; + clipHandle_t cmodel; + centity_t *cent; + qboolean spectator; +// vec3_t mins, maxs; // TTimo: unused + const char *cs; + + // dead clients don't activate triggers + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + return; + } + + spectator = ( ( cg.predictedPlayerState.pm_type == PM_SPECTATOR ) || ( cg.predictedPlayerState.pm_flags & PMF_LIMBO ) ); // JPW NERVE + + if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) { + return; + } + + for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) { + cent = cg_triggerEntities[ i ]; + ent = ¢->currentState; + + if ( ent->eType == ET_ITEM && !spectator && ( cg.predictedPlayerState.groundEntityNum == ENTITYNUM_WORLD ) ) { + CG_TouchItem( cent ); + continue; + } + + if ( ent->solid != SOLID_BMODEL ) { + continue; + } + + // Gordon: er, this lookup was wrong... + cmodel = cgs.inlineDrawModel[ ent->modelindex ]; +// cmodel = trap_CM_InlineModel( ent->modelindex ); + if ( !cmodel ) { + continue; + } + + if ( ent->eType == ET_CONSTRUCTIBLE || + ent->eType == ET_OID_TRIGGER +#ifdef VISIBLE_TRIGGERS + || ent->eType == ET_TRIGGER_MULTIPLE + || ent->eType == ET_TRIGGER_FLAGONLY + || ent->eType == ET_TRIGGER_FLAGONLY_MULTIPLE +#endif // VISIBLE_TRIGGERS + ) { + vec3_t mins, maxs, pmins, pmaxs; + + if ( ent->eType == ET_CONSTRUCTIBLE && ent->aiState ) { + continue; + } + + trap_R_ModelBounds( cmodel, mins, maxs ); + + VectorAdd( cent->lerpOrigin, mins, mins ); + VectorAdd( cent->lerpOrigin, maxs, maxs ); + +#ifdef VISIBLE_TRIGGERS + if ( ent->eType == ET_TRIGGER_MULTIPLE || ent->eType == ET_TRIGGER_FLAGONLY || ent->eType == ET_TRIGGER_FLAGONLY_MULTIPLE ) { + } else +#endif // VISIBLE_TRIGGERS + { + // expand the bbox a bit + VectorSet( mins, mins[0] - 48, mins[1] - 48, mins[2] - 48 ); + VectorSet( maxs, maxs[0] + 48, maxs[1] + 48, maxs[2] + 48 ); + } + + VectorAdd( cg.predictedPlayerState.origin, cg_pmove.mins, pmins ); + VectorAdd( cg.predictedPlayerState.origin, cg_pmove.maxs, pmaxs ); + +#ifdef VISIBLE_TRIGGERS + CG_RailTrail( NULL, mins, maxs, 1 ); +#endif // VISIBLE_TRIGGERS + + if ( !BG_BBoxCollision( pmins, pmaxs, mins, maxs ) ) { + continue; + } + + cs = NULL; + if ( ent->eType == ET_OID_TRIGGER ) { + cs = CG_ConfigString( CS_OID_TRIGGERS + ent->teamNum ); + } else if ( ent->eType == ET_CONSTRUCTIBLE ) { + cs = CG_ConfigString( CS_OID_TRIGGERS + ent->otherEntityNum2 ); + } + + if ( cs ) { + CG_ObjectivePrint( va( "You are near %s\n", cs ), SMALLCHAR_WIDTH ); + } + + continue; + } + } +} + + +#define MAX_PREDICT_ORIGIN_DELTA 0.1f +#define MAX_PREDICT_VELOCITY_DELTA 0.1f +#define MAX_PREDICT_VIEWANGLES_DELTA 1.0f + +qboolean CG_PredictionOk( playerState_t *ps1, playerState_t *ps2 ) { + vec3_t vec; + int i; + + if ( ps2->pm_type != ps1->pm_type || ps2->pm_flags != ps1->pm_flags || ps2->pm_time != ps1->pm_time ) { + return qfalse; + } + + VectorSubtract( ps2->origin, ps1->origin, vec ); + if ( DotProduct( vec, vec ) > Square( MAX_PREDICT_ORIGIN_DELTA ) ) { + return qfalse; + } + + VectorSubtract( ps2->velocity, ps1->velocity, vec ); + if ( DotProduct( vec, vec ) > Square( MAX_PREDICT_VELOCITY_DELTA ) ) { + return qfalse; + } + + if ( ps2->eFlags != ps1->eFlags ) { + return qfalse; + } + + if ( ps2->weaponTime != ps1->weaponTime ) { + return qfalse; + } + + if ( ps2->groundEntityNum != ps1->groundEntityNum ) { + return qfalse; + } + + if ( ps1->groundEntityNum != ENTITYNUM_WORLD || ps1->groundEntityNum != ENTITYNUM_NONE || ps2->groundEntityNum != ENTITYNUM_WORLD || ps2->groundEntityNum != ENTITYNUM_NONE ) { + return qfalse; + } + + if ( ps2->speed != ps1->speed || ps2->delta_angles[0] != ps1->delta_angles[0] || ps2->delta_angles[1] != ps1->delta_angles[1] || ps2->delta_angles[2] != ps1->delta_angles[2] ) { + return qfalse; + } + + if ( ps2->legsTimer != ps1->legsTimer || ps2->legsAnim != ps1->legsAnim || + ps2->torsoTimer != ps1->torsoTimer || ps2->torsoAnim != ps1->torsoAnim ) { + return qfalse; + } + +/* if( ps2->movementDir != ps1->movementDir ) { + return qfalse; + }*/ + + if ( ps2->eventSequence != ps1->eventSequence ) { + return qfalse; + } + + for ( i = 0; i < MAX_EVENTS; i++ ) { + if ( ps2->events[i] != ps1->events[i] || ps2->eventParms[i] != ps1->eventParms[i] ) { + return qfalse; + } + } + + if ( ps2->externalEvent != ps1->externalEvent || ps2->externalEventParm != ps1->externalEventParm || ps2->externalEventTime != ps1->externalEventTime ) { + return qfalse; + } + + if ( ps2->clientNum != ps1->clientNum ) { + return qfalse; + } + + if ( ps2->weapon != ps1->weapon || ps2->weaponstate != ps1->weaponstate ) { + return qfalse; + } + + for ( i = 0; i < 3; i++ ) { + if ( abs( ps2->viewangles[i] - ps1->viewangles[i] ) > MAX_PREDICT_VIEWANGLES_DELTA ) { + return qfalse; + } + } + + if ( ps2->viewheight != ps1->viewheight ) { + return qfalse; + } + + if ( ps2->damageEvent != ps1->damageEvent || ps2->damageYaw != ps1->damageYaw || ps2->damagePitch != ps1->damagePitch || ps2->damageCount != ps1->damageCount ) { + return qfalse; + } + + for ( i = 0; i < MAX_STATS; i++ ) { + if ( ps2->stats[i] != ps1->stats[i] ) { + return qfalse; + } + } + + for ( i = 0; i < MAX_PERSISTANT; i++ ) { + if ( ps2->persistant[i] != ps1->persistant[i] ) { + return qfalse; + } + } + + for ( i = 0; i < MAX_POWERUPS; i++ ) { + if ( ps2->powerups[i] != ps1->powerups[i] ) { + return qfalse; + } + } + + for ( i = 0; i < MAX_WEAPONS; i++ ) { + if ( ps2->ammo[i] != ps1->ammo[i] || ps2->ammoclip[i] != ps1->ammoclip[i] ) { + return qfalse; + } + } + + if ( ps1->viewlocked != ps2->viewlocked || ps1->viewlocked_entNum != ps2->viewlocked_entNum ) { + return qfalse; + } + + if ( ps1->onFireStart != ps2->onFireStart ) { + return qfalse; + } + + return qtrue; +} + +#define RESET_PREDICTION \ + cg.lastPredictedCommand = 0; \ + cg.backupStateTail = cg.backupStateTop; \ + useCommand = current - CMD_BACKUP + 1; + + +/* +================= +CG_PredictPlayerState + +Generates cg.predictedPlayerState for the current cg.time +cg.predictedPlayerState is guaranteed to be valid after exiting. + +For demo playback, this will be an interpolation between two valid +playerState_t. + +For normal gameplay, it will be the result of predicted usercmd_t on +top of the most recent playerState_t received from the server. + +Each new snapshot will usually have one or more new usercmd over the last, +but we simulate all unacknowledged commands each time, not just the new ones. +This means that on an internet connection, quite a few pmoves may be issued +each frame. + +OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t +differs from the predicted one. Would require saving all intermediate +playerState_t during prediction. (this is "dead reckoning" and would definately +be nice to have in there (SA)) + +We detect prediction errors and allow them to be decayed off over several frames +to ease the jerk. +================= +*/ + +// rain - we need to keep pmext around for old frames, because Pmove() +// fills in some values when it does prediction. This in itself is fine, +// but the prediction loop starts in the past and predicts from the +// snapshot time up to the current time, and having things like jumpTime +// appear to be set for prediction runs where they previously weren't +// is a Bad Thing. This is my bugfix for #166. + +pmoveExt_t oldpmext[CMD_BACKUP]; + +void CG_PredictPlayerState( void ) { + int cmdNum, current; + playerState_t oldPlayerState; + qboolean moved; + usercmd_t oldestCmd; + usercmd_t latestCmd; + vec3_t deltaAngles; + pmoveExt_t pmext; +// int useCommand = 0; + + cg.hyperspace = qfalse; // will be set if touching a trigger_teleport + + // if this is the first frame we must guarantee + // predictedPlayerState is valid even if there is some + // other error condition + if ( !cg.validPPS ) { + cg.validPPS = qtrue; + cg.predictedPlayerState = cg.snap->ps; + } + + // demo playback just copies the moves + if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { + CG_InterpolatePlayerState( qfalse ); + return; + } + + // non-predicting local movement will grab the latest angles + if ( cg_nopredict.integer +#ifdef ALLOW_GSYNC + || cg_synchronousClients.integer +#endif // ALLOW_GSYNC + ) { + cg_pmove.ps = &cg.predictedPlayerState; + cg_pmove.pmext = &cg.pmext; + + cg.pmext.airleft = ( cg.waterundertime - cg.time ); + + // Arnout: are we using an mg42? + if ( cg_pmove.ps->eFlags & EF_MG42_ACTIVE || cg_pmove.ps->eFlags & EF_AAGUN_ACTIVE ) { + cg.pmext.harc = cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.origin2[0]; + cg.pmext.varc = cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.origin2[1]; + + VectorCopy( cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.angles2, cg.pmext.centerangles ); + + cg.pmext.centerangles[PITCH] = AngleNormalize180( cg.pmext.centerangles[PITCH] ); + cg.pmext.centerangles[YAW] = AngleNormalize180( cg.pmext.centerangles[YAW] ); + cg.pmext.centerangles[ROLL] = AngleNormalize180( cg.pmext.centerangles[ROLL] ); + } + + CG_InterpolatePlayerState( qtrue ); + return; + } + + if ( cg_pmove.ps && cg_pmove.ps->eFlags & EF_MOUNTEDTANK ) { + centity_t* tank = &cg_entities[cg_entities[cg.snap->ps.clientNum].tagParent]; + + cg.pmext.centerangles[YAW] = tank->lerpAngles[ YAW ]; + cg.pmext.centerangles[PITCH] = tank->lerpAngles[ PITCH ]; + } + + // prepare for pmove + cg_pmove.ps = &cg.predictedPlayerState; + cg_pmove.pmext = &pmext; //&cg.pmext; + cg_pmove.character = CG_CharacterForClientinfo( &cgs.clientinfo[cg.snap->ps.clientNum], &cg_entities[cg.snap->ps.clientNum] ); + cg.pmext.airleft = ( cg.waterundertime - cg.time ); + + // Arnout: are we using an mg42? + if ( cg_pmove.ps->eFlags & EF_MG42_ACTIVE || cg_pmove.ps->eFlags & EF_AAGUN_ACTIVE ) { + cg.pmext.harc = cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.origin2[0]; + cg.pmext.varc = cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.origin2[1]; + + VectorCopy( cg_entities[cg_pmove.ps->viewlocked_entNum].currentState.angles2, cg.pmext.centerangles ); + + cg.pmext.centerangles[PITCH] = AngleNormalize180( cg.pmext.centerangles[PITCH] ); + cg.pmext.centerangles[YAW] = AngleNormalize180( cg.pmext.centerangles[YAW] ); + cg.pmext.centerangles[ROLL] = AngleNormalize180( cg.pmext.centerangles[ROLL] ); + } else if ( cg_pmove.ps->eFlags & EF_MOUNTEDTANK ) { + centity_t* tank = &cg_entities[cg_entities[cg.snap->ps.clientNum].tagParent]; + + cg.pmext.centerangles[PITCH] = tank->lerpAngles[PITCH]; + } + + cg_pmove.skill = cgs.clientinfo[cg.snap->ps.clientNum].skill; + + cg_pmove.trace = CG_TraceCapsule; + //cg_pmove.trace = CG_Trace; + cg_pmove.pointcontents = CG_PointContents; + if ( cg_pmove.ps->pm_type == PM_DEAD ) { + cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; + cg_pmove.ps->eFlags |= EF_DEAD; // DHM-Nerve added:: EF_DEAD is checked for in Pmove functions, but wasn't being set until after Pmove + } else if ( cg_pmove.ps->pm_type == PM_SPECTATOR ) { + // rain - fix the spectator can-move-partway-through-world weirdness + // bug by actually setting tracemask when spectating :x + cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; + cg_pmove.trace = CG_TraceCapsule_World; + } else { + cg_pmove.tracemask = MASK_PLAYERSOLID; + } + + if ( ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) || ( cg.snap->ps.pm_flags & PMF_LIMBO ) ) { // JPW NERVE limbo + cg_pmove.tracemask &= ~CONTENTS_BODY; // spectators can fly through bodies + } + + cg_pmove.noFootsteps = qfalse; + cg_pmove.noWeapClips = qfalse; + + // save the state before the pmove so we can detect transitions + oldPlayerState = cg.predictedPlayerState; + + current = trap_GetCurrentCmdNumber(); + + // rain - fill in the current cmd with the latest prediction from + // cg.pmext (#166) + memcpy( &oldpmext[current & CMD_MASK], &cg.pmext, sizeof( pmoveExt_t ) ); + + // if we don't have the commands right after the snapshot, we + // can't accurately predict a current position, so just freeze at + // the last good position we had + cmdNum = current - CMD_BACKUP + 1; + trap_GetUserCmd( cmdNum, &oldestCmd ); + if ( oldestCmd.serverTime > cg.snap->ps.commandTime + && oldestCmd.serverTime < cg.time ) { // special check for map_restart + if ( cg_showmiss.integer ) { + CG_Printf( "exceeded PACKET_BACKUP on commands\n" ); + } + } + + // get the latest command so we can know which commands are from previous map_restarts + trap_GetUserCmd( current, &latestCmd ); + + // get the most recent information we have, even if + // the server time is beyond our current cg.time, + // because predicted player positions are going to + // be ahead of everything else anyway + // rain - NEIN - this'll cause us to execute events from the next frame + // early, resulting in doubled events and the like. it seems to be + // worse as far as prediction, too, so BLAH at id. (#405) +#if 0 + if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) { + cg.predictedPlayerState = cg.nextSnap->ps; + cg.physicsTime = cg.nextSnap->serverTime; + } else { +#endif + cg.predictedPlayerState = cg.snap->ps; + cg.physicsTime = cg.snap->serverTime; +// } + + if ( pmove_msec.integer < 8 ) { + trap_Cvar_Set( "pmove_msec", "8" ); + } else if ( pmove_msec.integer > 33 ) { + trap_Cvar_Set( "pmove_msec", "33" ); + } + + cg_pmove.pmove_fixed = pmove_fixed.integer; // | cg_pmove_fixed.integer; + cg_pmove.pmove_msec = pmove_msec.integer; + + // run cmds + moved = qfalse; + for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) { + // get the command + trap_GetUserCmd( cmdNum, &cg_pmove.cmd ); + // get the previous command + trap_GetUserCmd( cmdNum - 1, &cg_pmove.oldcmd ); + + if ( cg_pmove.pmove_fixed ) { + // rain - added tracemask + PM_UpdateViewAngles( cg_pmove.ps, cg_pmove.pmext, &cg_pmove.cmd, CG_Trace, cg_pmove.tracemask ); + } + + // don't do anything if the time is before the snapshot player time + if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) { + continue; + } + + // don't do anything if the command was from a previous map_restart + if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) { + continue; + } + + // check for a prediction error from last frame + // on a lan, this will often be the exact value + // from the snapshot, but on a wan we will have + // to predict several commands to get to the point + // we want to compare + if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) { + vec3_t delta; + float len; + + if ( BG_PlayerMounted( cg_pmove.ps->eFlags ) ) { + // no prediction errors here, we're locked in place + VectorClear( cg.predictedError ); + } else if ( cg.thisFrameTeleport ) { + // a teleport will not cause an error decay + VectorClear( cg.predictedError ); + if ( cg_showmiss.integer ) { + CG_Printf( "PredictionTeleport\n" ); + } + cg.thisFrameTeleport = qfalse; + } else if ( !cg.showGameView ) { + vec3_t adjusted; + CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted, deltaAngles ); + // RF, add the deltaAngles (fixes jittery view while riding trains) + // ydnar: only do this if player is prone or using set mortar + if ( ( cg.predictedPlayerState.eFlags & EF_PRONE ) || cg.weaponSelect == WP_MORTAR_SET ) { + cg.predictedPlayerState.delta_angles[YAW] += ANGLE2SHORT( deltaAngles[YAW] ); + } + + if ( cg_showmiss.integer ) { + if ( !VectorCompare( oldPlayerState.origin, adjusted ) ) { + CG_Printf( "prediction error\n" ); + } + } + VectorSubtract( oldPlayerState.origin, adjusted, delta ); + len = VectorLength( delta ); + if ( len > 0.1 ) { + if ( cg_showmiss.integer ) { + CG_Printf( "Prediction miss: %f\n", len ); + } + if ( cg_errorDecay.integer ) { + int t; + float f; + + t = cg.time - cg.predictedErrorTime; + f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; + if ( f < 0 ) { + f = 0; + } + if ( f > 0 && cg_showmiss.integer ) { + CG_Printf( "Double prediction decay: %f\n", f ); + } + VectorScale( cg.predictedError, f, cg.predictedError ); + } else { + VectorClear( cg.predictedError ); + } + VectorAdd( delta, cg.predictedError, cg.predictedError ); + cg.predictedErrorTime = cg.oldTime; + } + } + } + + // don't predict gauntlet firing, which is only supposed to happen + // when it actually inflicts damage + cg_pmove.gauntletHit = qfalse; + + if ( cg_pmove.pmove_fixed ) { + cg_pmove.cmd.serverTime = ( ( cg_pmove.cmd.serverTime + pmove_msec.integer - 1 ) / pmove_msec.integer ) * pmove_msec.integer; + } + + // ydnar: if server respawning, freeze the player + if ( cg.serverRespawning ) { + cg_pmove.ps->pm_type = PM_FREEZE; + } + + cg_pmove.gametype = cgs.gametype; + + // rain - only fill in the charge times if we're on a playing team + if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_AXIS || cg.snap->ps.persistant[PERS_TEAM] == TEAM_ALLIES ) { + cg_pmove.ltChargeTime = cg.ltChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + cg_pmove.soldierChargeTime = cg.soldierChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + cg_pmove.engineerChargeTime = cg.engineerChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + cg_pmove.medicChargeTime = cg.medicChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + cg_pmove.covertopsChargeTime = cg.covertopsChargeTime[cg.snap->ps.persistant[PERS_TEAM] - 1]; + } + +#ifdef SAVEGAME_SUPPORT + if ( CG_IsSinglePlayer() && cg_reloading.integer ) { + cg_pmove.reloading = qtrue; + } +#endif // SAVEGAME_SUPPORT + +// memcpy( &pmext, &cg.pmext, sizeof(pmoveExt_t) ); // grab data, we only want the final result + // rain - copy the pmext as it was just before we + // previously ran this cmd (or, this will be the + // current predicted data if this is the current cmd) (#166) + memcpy( &pmext, &oldpmext[cmdNum & CMD_MASK], sizeof( pmoveExt_t ) ); + + fflush( stdout ); + + Pmove( &cg_pmove ); + + moved = qtrue; + + // add push trigger movement effects + CG_TouchTriggerPrediction(); + } + + if ( cg_showmiss.integer > 1 ) { + CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time ); + } + + if ( !moved ) { + if ( cg_showmiss.integer ) { + CG_Printf( "not moved\n" ); + } + return; + } + + // restore pmext + memcpy( &cg.pmext, &pmext, sizeof( pmoveExt_t ) ); + + if ( !cg.showGameView ) { + // adjust for the movement of the groundentity + CG_AdjustPositionForMover( cg.predictedPlayerState.origin, cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.time, cg.predictedPlayerState.origin, deltaAngles ); + } + + // fire events and other transition triggered things + CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState ); + + + // ydnar: shake player view here, rather than fiddle with view angles + if ( cg.time > cg.cameraShakeTime ) { + cg.cameraShakeScale = 0; + } else + { + float x; + + + // starts at 1, approaches 0 over time + x = ( cg.cameraShakeTime - cg.time ) / cg.cameraShakeLength; + + // move + cg.predictedPlayerState.origin[ 2 ] += + sin( M_PI * 8 * 13 + cg.cameraShakePhase ) * x * 6.0f * cg.cameraShakeScale; + + cg.predictedPlayerState.origin[ 1 ] += + sin( M_PI * 17 * x + cg.cameraShakePhase ) * x * 6.0f * cg.cameraShakeScale; + + cg.predictedPlayerState.origin[ 0 ] += + cos( M_PI * 7 * x + cg.cameraShakePhase ) * x * 6.0f * cg.cameraShakeScale; + } +} diff --git a/src/cgame/cg_public.h b/src/cgame/cg_public.h new file mode 100644 index 0000000..073152d --- /dev/null +++ b/src/cgame/cg_public.h @@ -0,0 +1,333 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + + +#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, // OSP + CGAME_EVENT_FIRETEAMMSG, +} cgameEvent_t; + +/* +================================================================== + +functions imported from the main executable + +================================================================== +*/ + +#define CGAME_IMPORT_API_VERSION 3 + +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_FS_FOPENFILE, + CG_FS_READ, + CG_FS_WRITE, + CG_FS_FCLOSEFILE, + CG_FS_GETFILELIST, + CG_FS_DELETEFILE, + CG_SENDCONSOLECOMMAND, + CG_ADDCOMMAND, + CG_SENDCLIENTCOMMAND, + CG_UPDATESCREEN, + CG_CM_LOADMAP, + CG_CM_NUMINLINEMODELS, + CG_CM_INLINEMODEL, + CG_CM_LOADMODEL, + CG_CM_TEMPBOXMODEL, + CG_CM_POINTCONTENTS, + CG_CM_TRANSFORMEDPOINTCONTENTS, + CG_CM_BOXTRACE, + CG_CM_TRANSFORMEDBOXTRACE, +// MrE: + CG_CM_CAPSULETRACE, + CG_CM_TRANSFORMEDCAPSULETRACE, + CG_CM_TEMPCAPSULEMODEL, +// done. + CG_CM_MARKFRAGMENTS, + CG_R_PROJECTDECAL, // ydnar: projects a decal onto brush models + CG_R_CLEARDECALS, // ydnar: clears world/entity decals + CG_S_STARTSOUND, + CG_S_STARTSOUNDEX, //----(SA) added + CG_S_STARTLOCALSOUND, + CG_S_CLEARLOOPINGSOUNDS, + CG_S_CLEARSOUNDS, + CG_S_ADDLOOPINGSOUND, + CG_S_UPDATEENTITYPOSITION, +// Ridah, talking animations + CG_S_GETVOICEAMPLITUDE, +// done. + CG_S_RESPATIALIZE, + CG_S_REGISTERSOUND, + CG_S_STARTBACKGROUNDTRACK, + CG_S_FADESTREAMINGSOUND, //----(SA) modified + CG_S_FADEALLSOUNDS, //----(SA) added for fading out everything + CG_S_STARTSTREAMINGSOUND, + CG_S_GETSOUNDLENGTH, // xkan - get the length (in milliseconds) of the sound + CG_S_GETCURRENTSOUNDTIME, + CG_R_LOADWORLDMAP, + CG_R_REGISTERMODEL, + CG_R_REGISTERSKIN, + CG_R_REGISTERSHADER, + + CG_R_GETSKINMODEL, // client allowed to view what the .skin loaded so they can set their model appropriately + CG_R_GETMODELSHADER, // client allowed the shader handle for given model/surface (for things like debris inheriting shader from explosive) + + CG_R_REGISTERFONT, + CG_R_CLEARSCENE, + CG_R_ADDREFENTITYTOSCENE, + CG_GET_ENTITY_TOKEN, + CG_R_ADDPOLYTOSCENE, +// Ridah + CG_R_ADDPOLYSTOSCENE, +// done. + CG_R_ADDPOLYBUFFERTOSCENE, + CG_R_ADDLIGHTTOSCENE, + + CG_R_ADDCORONATOSCENE, + CG_R_SETFOG, + CG_R_SETGLOBALFOG, + + CG_R_RENDERSCENE, + CG_R_SAVEVIEWPARMS, + CG_R_RESTOREVIEWPARMS, + CG_R_SETCOLOR, + CG_R_DRAWSTRETCHPIC, + CG_R_DRAWSTRETCHPIC_GRADIENT, //----(SA) added + CG_R_MODELBOUNDS, + CG_R_LERPTAG, + CG_GETGLCONFIG, + CG_GETGAMESTATE, + CG_GETCURRENTSNAPSHOTNUMBER, + CG_GETSNAPSHOT, + CG_GETSERVERCOMMAND, + CG_GETCURRENTCMDNUMBER, + CG_GETUSERCMD, + CG_SETUSERCMDVALUE, + CG_SETCLIENTLERPORIGIN, // DHM - Nerve + CG_R_REGISTERSHADERNOMIP, + CG_MEMORY_REMAINING, + + CG_KEY_ISDOWN, + CG_KEY_GETCATCHER, + CG_KEY_SETCATCHER, + CG_KEY_GETKEY, + CG_KEY_GETOVERSTRIKEMODE, + CG_KEY_SETOVERSTRIKEMODE, + + 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_REMOVECOMMAND, + CG_R_LIGHTFORPOINT, + + CG_CIN_PLAYCINEMATIC, + CG_CIN_STOPCINEMATIC, + CG_CIN_RUNCINEMATIC, + CG_CIN_DRAWCINEMATIC, + CG_CIN_SETEXTENTS, + CG_R_REMAP_SHADER, + CG_S_ADDREALLOOPINGSOUND, + CG_S_STOPSTREAMINGSOUND, //----(SA) added + + CG_LOADCAMERA, + CG_STARTCAMERA, + CG_STOPCAMERA, + CG_GETCAMERAINFO, + + CG_MEMSET = 150, + CG_MEMCPY, + CG_STRNCPY, + CG_SIN, + CG_COS, + CG_ATAN2, + CG_SQRT, + CG_FLOOR, + CG_CEIL, + + CG_TESTPRINTINT, + CG_TESTPRINTFLOAT, + CG_ACOS, + + CG_INGAME_POPUP, //----(SA) added + + // NERVE - SMF + CG_INGAME_CLOSEPOPUP, + + CG_R_DRAWROTATEDPIC, + CG_R_DRAW2DPOLYS, + + CG_KEY_GETBINDINGBUF, + CG_KEY_SETBINDING, + CG_KEY_KEYNUMTOSTRINGBUF, + CG_KEY_BINDINGTOKEYS, + + CG_TRANSLATE_STRING, + // -NERVE - SMF + + CG_R_INPVS, + CG_GETHUNKDATA, + + CG_PUMPEVENTLOOP, + + // zinx + CG_SENDMESSAGE, + CG_MESSAGESTATUS, + // -zinx + + // bani + CG_R_LOADDYNAMICSHADER, + // -bani + + // fretn + CG_R_RENDERTOTEXTURE, + // -fretn + // bani + CG_R_GETTEXTUREID, + // -bani + // bani + CG_R_FINISH, + // -bani +} cgameImport_t; + + +/* +================================================================== + +functions exported to the main executable + +================================================================== +*/ + +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_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 + +} cgameExport_t; + +//---------------------------------------------- diff --git a/src/cgame/cg_scoreboard.c b/src/cgame/cg_scoreboard.c new file mode 100644 index 0000000..76d199d --- /dev/null +++ b/src/cgame/cg_scoreboard.c @@ -0,0 +1,768 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_scoreboard -- draw the scoreboard on top of the game screen +#include "cg_local.h" + + +#define SCOREBOARD_WIDTH ( 31 * BIGCHAR_WIDTH ) + +vec4_t clrUiBack = { 0.f, 0.f, 0.f, .6f }; +vec4_t clrUiBar = { .16f, .2f, .17f, .8f }; + +/* +================= +WM_DrawObjectives +================= +*/ + +#define INFO_PLAYER_WIDTH 134 +#define INFO_SCORE_WIDTH 56 +#define INFO_XP_WIDTH 36 +#define INFO_CLASS_WIDTH 50 +#define INFO_LATENCY_WIDTH 40 +#define INFO_LIVES_WIDTH 20 +#define INFO_TEAM_HEIGHT 24 +#define INFO_BORDER 2 +#define INFO_LINE_HEIGHT 30 +#define INFO_TOTAL_WIDTH ( INFO_PLAYER_WIDTH + INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH ) + +int WM_DrawObjectives( int x, int y, int width, float fade ) { + const char *s, *str; + int tempy, rows; + int msec, mins, seconds, tens; // JPW NERVE + vec4_t tclr = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + const char *s, *buf, *shader = NULL, *flagshader = NULL, *nameshader = NULL; + + // Moved to CG_DrawIntermission +/* static int doScreenshot = 0, doDemostop = 0; + + // OSP - End-of-level autoactions + if(!cg.demoPlayback) { + if(!cg.latchVictorySound) { + if(cg_autoAction.integer & AA_SCREENSHOT) { + doScreenshot = cg.time + 1000; + } + if(cg_autoAction.integer & AA_STATSDUMP) { + CG_dumpStats_f(); + } + if((cg_autoAction.integer & AA_DEMORECORD) && (cgs.gametype == GT_WOLF_STOPWATCH && cgs.currentRound != 1)) { + doDemostop = cg.time + 5000; // stats should show up within 5 seconds + } + } + if(doScreenshot > 0 && doScreenshot < cg.time) { + CG_autoScreenShot_f(); + doScreenshot = 0; + } + if(doDemostop > 0 && doDemostop < cg.time) { + trap_SendConsoleCommand("stoprecord\n"); + doDemostop = 0; + } + } +*/ + rows = 8; + y += SMALLCHAR_HEIGHT * ( rows - 1 ); + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + buf = Info_ValueForKey( s, "winner" ); + + if ( atoi( buf ) == -1 ) { + str = "ITS A TIE!"; + } else if ( atoi( buf ) ) { + str = "ALLIES"; +// shader = "ui/assets/portraits/allies_win"; + flagshader = "ui/assets/portraits/allies_win_flag.tga"; + nameshader = "ui/assets/portraits/text_allies.tga"; + +/* if ( !cg.latchVictorySound ) { + cg.latchVictorySound = qtrue; + trap_S_StartLocalSound( trap_S_RegisterSound( "sound/music/allies_win.wav", qtrue ), CHAN_LOCAL_SOUND ); // FIXME: stream + }*/ + } else { + str = "AXIS"; +// shader = "ui/assets/portraits/axis_win"; + flagshader = "ui/assets/portraits/axis_win_flag.tga"; + nameshader = "ui/assets/portraits/text_axis.tga"; + +/* if ( !cg.latchVictorySound ) { + cg.latchVictorySound = qtrue; + trap_S_StartLocalSound( trap_S_RegisterSound( "sound/music/axis_win.wav", qtrue ), CHAN_LOCAL_SOUND ); // FIXME: stream + }*/ + } + + y += SMALLCHAR_HEIGHT * ( ( rows - 2 ) / 2 ); + + if ( flagshader ) { + CG_DrawPic( 100, 10, 210, 136, trap_R_RegisterShaderNoMip( flagshader ) ); + CG_DrawPic( 325, 10, 210, 136, trap_R_RegisterShaderNoMip( flagshader ) ); + } + + if ( shader ) { + CG_DrawPic( 229, 10, 182, 136, trap_R_RegisterShaderNoMip( shader ) ); + } + if ( nameshader ) { + CG_DrawPic( 140, 50, 127, 64, trap_R_RegisterShaderNoMip( nameshader ) ); + CG_DrawPic( 365, 50, 127, 64, trap_R_RegisterShaderNoMip( "ui/assets/portraits/text_win.tga" ) ); + } + return y; + } +// JPW NERVE -- mission time & reinforce time + else { + tempy = y; + rows = 1; + + CG_FillRect( x - 5, y - 2, width + 5, 21, clrUiBack ); + CG_FillRect( x - 5, y - 2, width + 5, 21, clrUiBar ); + CG_DrawRect_FixedBorder( x - 5, y - 2, width + 5, 21, 1, colorBlack ); + + y += SMALLCHAR_HEIGHT * ( rows - 1 ); + if ( cgs.timelimit > 0.0f ) { + msec = ( cgs.timelimit * 60.f * 1000.f ) - ( cg.time - cgs.levelStartTime ); + + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + } else { + msec = mins = tens = seconds = 0; + } + + if ( cgs.gamestate != GS_PLAYING ) { + s = va( "%s %s", CG_TranslateString( "MISSION TIME:" ), CG_TranslateString( "WARMUP" ) ); + } else if ( msec < 0 && cgs.timelimit > 0.0f ) { + if ( cgs.gamestate == GS_WAITING_FOR_PLAYERS ) { + s = va( "%s %s", CG_TranslateString( "MISSION TIME:" ), CG_TranslateString( "GAME STOPPED" ) ); + } else { + s = va( "%s %s", CG_TranslateString( "MISSION TIME:" ), CG_TranslateString( "SUDDEN DEATH" ) ); + } + } else { + s = va( "%s %2.0f:%i%i", CG_TranslateString( "MISSION TIME:" ), (float)mins, tens, seconds ); // float cast to line up with reinforce time + } + + CG_Text_Paint_Ext( x, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + + if ( cgs.gametype != GT_WOLF_LMS ) { + if ( cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_AXIS || cgs.clientinfo[cg.snap->ps.clientNum].team == TEAM_ALLIES ) { + msec = CG_CalculateReinfTime( qfalse ) * 1000; + } else { // no team (spectator mode) + msec = 0; + } + + if ( msec ) { + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + + s = va( "%s %2.0f:%i%i", CG_TranslateString( "REINFORCE TIME:" ), (float)mins, tens, seconds ); + CG_Text_Paint_Ext( 640 - 20 - CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ), y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } + } + + // NERVE - SMF + if ( cgs.gametype == GT_WOLF_STOPWATCH ) { + int w; + s = va( "%s %i", CG_TranslateString( "STOPWATCH ROUND" ), cgs.currentRound + 1 ); + + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + + CG_Text_Paint_Ext( x + 300 - w * 0.5f, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } else if ( cgs.gametype == GT_WOLF_LMS ) { + int w; + s = va( "%s %i %s %i-%i", CG_TranslateString( "ROUND" ), cgs.currentRound + 1, CG_TranslateString( "SCORE" ), cg.teamWonRounds[1], cg.teamWonRounds[0] ); + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + + CG_Text_Paint_Ext( x + 300 - w * 0.5f, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } else if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + int w; + s = va( "MAP %i of %i", cgs.currentCampaignMap + 1, cgs.campaignData.mapCount ); + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + + CG_Text_Paint_Ext( x + 300 - w * 0.5f, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } + + y += SMALLCHAR_HEIGHT * 2; + } +// jpw + + return y; +} + +static void WM_DrawClientScore( int x, int y, score_t *score, float *color, float fade ) { + int maxchars, offset; + int i, j; + float tempx; + vec4_t hcolor; + clientInfo_t *ci; + char buf[64]; + + if ( y + SMALLCHAR_HEIGHT >= 470 ) { + return; + } + + ci = &cgs.clientinfo[score->client]; + + if ( score->client == cg.snap->ps.clientNum ) { + tempx = x; + + hcolor[3] = fade * 0.3; + VectorSet( hcolor, .5f, .5f, .2f ); // DARK-RED + + CG_FillRect( tempx, y + 1, INFO_PLAYER_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_PLAYER_WIDTH; + + if ( ci->team == TEAM_SPECTATOR ) { + int width; + width = INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH; + + CG_FillRect( tempx, y + 1, width - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += width; + } else { + CG_FillRect( tempx, y + 1, INFO_CLASS_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_CLASS_WIDTH; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + CG_FillRect( tempx, y + 1, INFO_SCORE_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_SCORE_WIDTH; + } else { + CG_FillRect( tempx, y + 1, INFO_XP_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_XP_WIDTH; + } + + CG_FillRect( tempx, y + 1, INFO_LATENCY_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_LATENCY_WIDTH; + + if ( cg_gameType.integer != GT_WOLF_LMS ) { + CG_FillRect( tempx, y + 1, INFO_LIVES_WIDTH - INFO_BORDER, SMALLCHAR_HEIGHT - 1, hcolor ); + tempx += INFO_LIVES_WIDTH; + } + } + } + + tempx = x; + + // DHM - Nerve + VectorSet( hcolor, 1, 1, 1 ); + hcolor[3] = fade; + + maxchars = 16; + offset = 0; + + if ( ci->team != TEAM_SPECTATOR ) { + if ( ci->powerups & ( ( 1 << PW_REDFLAG ) | ( 1 << PW_BLUEFLAG ) ) ) { + CG_DrawPic( tempx - 4, y, 16, 16, cgs.media.objectiveShader ); + offset += 8; + tempx += 12; + maxchars -= 2; + } + + // draw the skull icon if out of lives + if ( score->respawnsLeft == -2 || ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR && ci->team == cgs.clientinfo[cg.clientNum].team && cgs.clientinfo[score->client].health == -1 ) ) { + CG_DrawPic( tempx, y, 18, 18, cgs.media.scoreEliminatedShader ); + offset += 18; + tempx += 18; + maxchars -= 2; + } else if ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR && ci->team == cgs.clientinfo[cg.clientNum].team && cgs.clientinfo[score->client].health == 0 ) { + CG_DrawPic( tempx + 1, y + 1, 16, 16, cgs.media.medicIcon ); + offset += 18; + tempx += 18; + maxchars -= 2; + } + } + + // draw name + CG_DrawStringExt( tempx, y, ci->name, hcolor, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, maxchars ); + maxchars -= CG_DrawStrlen( ci->name ); + + // draw medals + buf[0] = '\0'; + for ( i = 0; i < SK_NUM_SKILLS; i++ ) { + for ( j = 0; j < ci->medals[i]; j++ ) + Q_strcat( buf, sizeof( buf ), va( "^%c%c", COLOR_RED + i, skillNames[i][0] ) ); + } + maxchars--; + CG_DrawStringExt( tempx + ( BG_drawStrlen( ci->name ) * SMALLCHAR_WIDTH + SMALLCHAR_WIDTH ), y, buf, hcolor, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, maxchars ); + + tempx += INFO_PLAYER_WIDTH - offset; + + if ( ci->team == TEAM_SPECTATOR ) { + const char *s; + int w, totalwidth; + + totalwidth = INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH - 8; + + s = CG_TranslateString( "^3SPECTATOR" ); + w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + + CG_DrawSmallString( tempx + totalwidth - w, y, s, fade ); + return; + } + // OSP - allow MV clients see the class of its merged client's on the scoreboard + else if ( cg.snap->ps.persistant[PERS_TEAM] == ci->team || CG_mvMergedClientLocate( score->client ) ) { + CG_DrawSmallString( tempx, y, CG_TranslateString( BG_ShortClassnameForNumber( score->playerClass ) ), fade ); + } + tempx += INFO_CLASS_WIDTH; + + CG_DrawSmallString( tempx, y, va( "%3i", score->score ), fade ); + if ( cg_gameType.integer == GT_WOLF_LMS ) { + tempx += INFO_SCORE_WIDTH; + } else { + tempx += INFO_XP_WIDTH; + } + + CG_DrawSmallString( tempx, y, va( "%4i", score->ping ), fade ); + tempx += INFO_LATENCY_WIDTH; + + if ( cg_gameType.integer != GT_WOLF_LMS ) { + if ( score->respawnsLeft >= 0 ) { + CG_DrawSmallString( tempx, y, va( "%2i", score->respawnsLeft ), fade ); + } else { + CG_DrawSmallString( tempx, y, " -", fade ); + } + tempx += INFO_LIVES_WIDTH; + } +} + +const char* WM_TimeToString( float msec ) { + int mins, seconds, tens; + + seconds = msec / 1000; + mins = seconds / 60; + seconds -= mins * 60; + tens = seconds / 10; + seconds -= tens * 10; + + return va( "%i:%i%i", mins, tens, seconds ); +} + +static void WM_DrawClientScore_Small( int x, int y, score_t *score, float *color, float fade ) { + int maxchars, offset; + float tempx; + vec4_t hcolor; + clientInfo_t *ci; + + if ( y + SMALLCHAR_HEIGHT >= 470 ) { + return; + } + + ci = &cgs.clientinfo[score->client]; + + if ( score->client == cg.snap->ps.clientNum ) { + tempx = x; + + hcolor[3] = fade * 0.3; + VectorSet( hcolor, .5f, .5f, .2f ); // DARK-RED + + CG_FillRect( tempx, y + 1, INFO_PLAYER_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_PLAYER_WIDTH; + + if ( ci->team == TEAM_SPECTATOR ) { + int width; + width = INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH; + + CG_FillRect( tempx, y + 1, width - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += width; + } else { + CG_FillRect( tempx, y + 1, INFO_CLASS_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_CLASS_WIDTH; + + if ( cg_gameType.integer == GT_WOLF_LMS ) { + CG_FillRect( tempx, y + 1, INFO_SCORE_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_SCORE_WIDTH; + } else { + CG_FillRect( tempx, y + 1, INFO_XP_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_XP_WIDTH; + } + + CG_FillRect( tempx, y + 1, INFO_LATENCY_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_LATENCY_WIDTH; + + if ( cg_gameType.integer != GT_WOLF_LMS ) { + CG_FillRect( tempx, y + 1, INFO_LIVES_WIDTH - INFO_BORDER, MINICHAR_HEIGHT - 1, hcolor ); + tempx += INFO_LIVES_WIDTH; + } + } + } + + tempx = x; + + // DHM - Nerve + VectorSet( hcolor, 1, 1, 1 ); + hcolor[3] = fade; + + maxchars = 17; + offset = 0; + + if ( ci->team != TEAM_SPECTATOR ) { + if ( ci->powerups & ( ( 1 << PW_REDFLAG ) | ( 1 << PW_BLUEFLAG ) ) ) { + CG_DrawPic( tempx - 2, y - 4, 20, 20, trap_R_RegisterShader( "models/multiplayer/treasure/treasure" ) ); + offset += 14; + tempx += 14; + maxchars -= 2; + } + + // draw the skull icon if out of lives + if ( score->respawnsLeft == -2 || ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR && ci->team == cgs.clientinfo[cg.clientNum].team && cgs.clientinfo[score->client].health == -1 ) ) { + CG_DrawPic( tempx, y, 12, 12, cgs.media.scoreEliminatedShader ); + offset += 14; + tempx += 14; + maxchars -= 2; + } else if ( cgs.clientinfo[cg.clientNum].team != TEAM_SPECTATOR && ci->team == cgs.clientinfo[cg.clientNum].team && cgs.clientinfo[score->client].health == 0 ) { + CG_DrawPic( tempx + 1, y + 1, 10, 10, cgs.media.medicIcon ); + offset += 14; + tempx += 14; + maxchars -= 2; + } + } + + // draw name + CG_DrawStringExt( tempx, y, ci->name, hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, maxchars ); + tempx += INFO_PLAYER_WIDTH - offset; + // dhm - nerve + + if ( ci->team == TEAM_SPECTATOR ) { + const char *s; + int w, totalwidth; + + totalwidth = INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH - 8; + + s = CG_TranslateString( "^3SPECTATOR" ); + w = CG_DrawStrlen( s ) * MINICHAR_WIDTH; + + CG_DrawSmallString( tempx + totalwidth - w, y, s, fade ); + return; + } else if ( cg.snap->ps.persistant[PERS_TEAM] == ci->team ) { + CG_DrawStringExt( tempx, y, CG_TranslateString( BG_ShortClassnameForNumber( score->playerClass ) ), hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, 0 ); +// CG_DrawSmallString( tempx, y, CG_TranslateString( s ), fade ); + } + tempx += INFO_CLASS_WIDTH; + + CG_DrawStringExt( tempx, y, va( "%3i", score->score ), hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, 0 ); + if ( cg_gameType.integer == GT_WOLF_LMS ) { + tempx += INFO_SCORE_WIDTH; + } else { + tempx += INFO_XP_WIDTH; + } + + CG_DrawStringExt( tempx, y, va( "%4i", score->ping ), hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, 0 ); + tempx += INFO_LATENCY_WIDTH; + + if ( cg_gameType.integer != GT_WOLF_LMS ) { + if ( score->respawnsLeft >= 0 ) { + CG_DrawStringExt( tempx, y, va( "%2i", score->respawnsLeft ), hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, 0 ); + } else { + CG_DrawStringExt( tempx, y, " -", hcolor, qfalse, qfalse, MINICHAR_WIDTH, MINICHAR_HEIGHT, 0 ); + } + tempx += INFO_LIVES_WIDTH; + } +} + +static int WM_DrawInfoLine( int x, int y, float fade ) { + int w, defender, winner; + const char *s; + vec4_t tclr = { 0.6f, 0.6f, 0.6f, 1.0f }; + + if ( cg.snap->ps.pm_type != PM_INTERMISSION ) { + return y; + } + + w = 360; +// CG_DrawPic( 320 - w/2, y, w, INFO_LINE_HEIGHT, trap_R_RegisterShaderNoMip( "ui/assets/mp_line_strip.tga" ) ); + + s = CG_ConfigString( CS_MULTI_INFO ); + defender = atoi( Info_ValueForKey( s, "defender" ) ); + + s = CG_ConfigString( CS_MULTI_MAPWINNER ); + winner = atoi( Info_ValueForKey( s, "winner" ) ); + + if ( cgs.currentRound ) { + // first round + s = va( CG_TranslateString( "CLOCK IS NOW SET TO %s!" ), WM_TimeToString( cgs.nextTimeLimit * 60.f * 1000.f ) ); + } else { + // second round + if ( !defender ) { + if ( winner != defender ) { + s = "ALLIES SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "ALLIES COULDN'T BEAT THE CLOCK!"; + } + } else { + if ( winner != defender ) { + s = "AXIS SUCCESSFULLY BEAT THE CLOCK!"; + } else { + s = "AXIS COULDN'T BEAT THE CLOCK!"; + } + } + + s = CG_TranslateString( s ); + } + + CG_FillRect( 320 - w / 2, y, w, 20, clrUiBar ); + CG_DrawRect_FixedBorder( 320 - w / 2, y, w, 20, 1, colorBlack ); + + w = CG_Text_Width_Ext( s, 0.25f, 0, &cgs.media.limboFont1 ); + + CG_Text_Paint_Ext( 320 - w * 0.5f, y + 15, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); +// CG_DrawSmallString( 320 - w/2, ( y + INFO_LINE_HEIGHT / 2 ) - SMALLCHAR_HEIGHT / 2, s, fade ); + return y + INFO_LINE_HEIGHT + 6; +} + +static int WM_TeamScoreboard( int x, int y, team_t team, float fade, int maxrows ) { + vec4_t hcolor; + float tempx, tempy; + int height, width; + int i; + int count = 0; + vec4_t tclr = { 0.6f, 0.6f, 0.6f, 1.0f }; + + height = SMALLCHAR_HEIGHT * maxrows; + width = INFO_PLAYER_WIDTH + INFO_CLASS_WIDTH + INFO_SCORE_WIDTH + INFO_LATENCY_WIDTH; + + CG_FillRect( x - 5, y - 2, width + 5, 21, clrUiBack ); + CG_FillRect( x - 5, y - 2, width + 5, 21, clrUiBar ); + + Vector4Set( hcolor, 0, 0, 0, fade ); + CG_DrawRect_FixedBorder( x - 5, y - 2, width + 5, 21, 1, colorBlack ); + + // draw header + if ( cg_gameType.integer == GT_WOLF_LMS ) { + char *s; + if ( team == TEAM_AXIS ) { + s = va( "%s [%d] (%d %s)", CG_TranslateString( "AXIS" ), cg.teamScores[0], cg.teamPlayers[team], CG_TranslateString( "PLAYERS" ) ); + s = va( "%s ^3%s", s, cg.teamFirstBlood == TEAM_AXIS ? CG_TranslateString( "FIRST BLOOD" ) : "" ); + + CG_Text_Paint_Ext( x, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } else if ( team == TEAM_ALLIES ) { + s = va( "%s [%d] (%d %s)", CG_TranslateString( "ALLIES" ), cg.teamScores[1], cg.teamPlayers[team], CG_TranslateString( "PLAYERS" ) ); + s = va( "%s ^3%s", s, cg.teamFirstBlood == TEAM_ALLIES ? CG_TranslateString( "FIRST BLOOD" ) : "" ); + + CG_Text_Paint_Ext( x, y + 13, 0.25f, 0.25f, tclr, s, 0, 0, 0, &cgs.media.limboFont1 ); + } + } else { + if ( team == TEAM_AXIS ) { + CG_Text_Paint_Ext( x, y + 13, 0.25f, 0.25f, tclr, va( "%s [%d] (%d %s)", CG_TranslateString( "AXIS" ), cg.teamScores[0], cg.teamPlayers[team], CG_TranslateString( "PLAYERS" ) ), 0, 0, 0, &cgs.media.limboFont1 ); + } else if ( team == TEAM_ALLIES ) { + CG_Text_Paint_Ext( x, y + 13, 0.25f, 0.25f, tclr, va( "%s [%d] (%d %s)", CG_TranslateString( "ALLIES" ), cg.teamScores[1], cg.teamPlayers[team], CG_TranslateString( "PLAYERS" ) ), 0, 0, 0, &cgs.media.limboFont1 ); + } + } + + y += SMALLCHAR_HEIGHT + 3; + + // save off y val + tempy = y; + + // draw color bands + for ( i = 0; i <= maxrows; i++ ) { + if ( i % 2 == 0 ) { + VectorSet( hcolor, ( 80.f / 255.f ), ( 80.f / 255.f ), ( 80.f / 255.f ) ); // LIGHT BLUE + } else { + VectorSet( hcolor, ( 0.f / 255.f ), ( 0.f / 255.f ), ( 0.f / 255.f ) ); // DARK BLUE + } + hcolor[3] = fade * 0.3; + + CG_FillRect( x - 5, y, width + 5, SMALLCHAR_HEIGHT + 1, hcolor ); + trap_R_SetColor( colorBlack ); + CG_DrawTopBottom( x - 5, y, width + 5, SMALLCHAR_HEIGHT + 1, 1 ); + trap_R_SetColor( NULL ); + + y += SMALLCHAR_HEIGHT; + } + hcolor[3] = 1; + + y = tempy; + + tempx = x; + + CG_FillRect( x - 5, y - 1, width + 5, 18, clrUiBack ); + //CG_FillRect( x-5, y-1, width+5, 18, clrUiBar ); + trap_R_SetColor( colorBlack ); + CG_DrawTopBottom( x - 5, y - 1, width + 5, 18, 1 ); + trap_R_SetColor( NULL ); + + // draw player info headings + CG_DrawSmallString( tempx, y, CG_TranslateString( "Name" ), fade ); + tempx += INFO_PLAYER_WIDTH; + + CG_DrawSmallString( tempx, y, CG_TranslateString( "Class" ), fade ); + tempx += INFO_CLASS_WIDTH; + + if ( cgs.gametype == GT_WOLF_LMS ) { + CG_DrawSmallString( tempx, y, CG_TranslateString( "Score" ), fade ); + tempx += INFO_SCORE_WIDTH; + } else { + CG_DrawSmallString( tempx + 1 * SMALLCHAR_WIDTH, y, CG_TranslateString( "XP" ), fade ); + tempx += INFO_XP_WIDTH; + } + + CG_DrawSmallString( tempx, y, CG_TranslateString( "Ping" ), fade ); + tempx += INFO_LATENCY_WIDTH; + + if ( cgs.gametype != GT_WOLF_LMS ) { + CG_DrawPicST( tempx + 2, y, INFO_LIVES_WIDTH - 4, 16, 0.f, 0.f, 0.5f, 1.f, team == TEAM_ALLIES ? cgs.media.hudAlliedHelmet : cgs.media.hudAxisHelmet ); + tempx += INFO_LIVES_WIDTH; + } + + + y += SMALLCHAR_HEIGHT; + + // draw player info + VectorSet( hcolor, 1, 1, 1 ); + hcolor[3] = fade; + + cg.teamPlayers[team] = 0; // JPW NERVE + for ( i = 0; i < cg.numScores; i++ ) { + if ( team != cgs.clientinfo[ cg.scores[i].client ].team ) { + continue; + } + + cg.teamPlayers[team]++; + } + + count = 0; + for ( i = 0; i < cg.numScores && count < maxrows; i++ ) { + if ( team != cgs.clientinfo[ cg.scores[i].client ].team ) { + continue; + } + + if ( cg.teamPlayers[team] > maxrows ) { + WM_DrawClientScore_Small( x, y, &cg.scores[i], hcolor, fade ); + y += MINICHAR_HEIGHT; + } else { + WM_DrawClientScore( x, y, &cg.scores[i], hcolor, fade ); + y += SMALLCHAR_HEIGHT; + } + + count++; + } + + // draw spectators + y += SMALLCHAR_HEIGHT; + + for ( i = 0; i < cg.numScores; i++ ) { + if ( cgs.clientinfo[ cg.scores[i].client ].team != TEAM_SPECTATOR ) { + continue; + } + if ( team == TEAM_AXIS && ( i % 2 ) ) { + continue; + } + if ( team == TEAM_ALLIES && ( ( i + 1 ) % 2 ) ) { + continue; + } + + WM_DrawClientScore( x, y, &cg.scores[i], hcolor, fade ); + y += SMALLCHAR_HEIGHT; + } + + return y; +} +// -NERVE - SMF + +/* +================= +CG_DrawScoreboard + +Draw the normal in-game scoreboard +================= +*/ +qboolean CG_DrawScoreboard( void ) { + int x = 0, y = 0, x_right; + float fade; + float *fadeColor; + + x = 20; + y = 10; + + x_right = 640 - x - ( INFO_TOTAL_WIDTH - 5 ); + + // don't draw anything if the menu or console is up + if ( cg_paused.integer ) { + return qfalse; + } + + // don't draw scoreboard during death while warmup up + // OSP - also for pesky scoreboards in demos + if ( ( cg.warmup || ( cg.demoPlayback && cg.snap->ps.pm_type != PM_INTERMISSION ) ) && !cg.showScores ) { + return qfalse; + } + + // don't draw if in cameramode + if ( cg.cameraMode ) { + return qtrue; + } + + if ( cg.showScores || cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + fade = 1.0; + fadeColor = colorWhite; + } else { + fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME ); + + if ( !fadeColor ) { + // next time scoreboard comes up, don't print killer + *cg.killerName = 0; + return qfalse; + } + fade = fadeColor[3]; + } + + y = WM_DrawObjectives( x, y, 640 - 2 * x + 5, fade ); + + if ( cgs.gametype == GT_WOLF_STOPWATCH && ( cg.snap->ps.pm_type == PM_INTERMISSION ) ) { + y = WM_DrawInfoLine( x, 155, fade ); + + WM_TeamScoreboard( x, y, TEAM_AXIS, fade, 8 ); + x = x_right; + WM_TeamScoreboard( x, y, TEAM_ALLIES, fade, 8 ); + } else { + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + WM_TeamScoreboard( x, y, TEAM_AXIS, fade, 9 ); + x = x_right; + WM_TeamScoreboard( x, y, TEAM_ALLIES, fade, 9 ); + } else { + WM_TeamScoreboard( x, y, TEAM_AXIS, fade, 25 ); + x = x_right; + WM_TeamScoreboard( x, y, TEAM_ALLIES, fade, 25 ); + } + } + +/* if(!CG_IsSinglePlayer()) { + qtime_t ct; + + G_showWindowMessages(); + trap_RealTime(&ct); + s = va("^3%02d:%02d:%02d - %02d %s %d", + ct.tm_hour, ct.tm_min, ct.tm_sec, + ct.tm_mday, aMonths[ct.tm_mon], 1900 + ct.tm_year); + CG_DrawStringExt(444, 12, s, colorWhite, qfalse, qtrue, 8, 8, 0); + } +*/ + return qtrue; +} diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c new file mode 100644 index 0000000..c47d130 --- /dev/null +++ b/src/cgame/cg_servercmds.c @@ -0,0 +1,2468 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_servercmds.c -- reliably sequenced text commands sent by the server +// these are processed at snapshot transition time, so there will definately +// be a valid snapshot this frame + +#include "cg_local.h" + +#define SCOREPARSE_COUNT 9 + +void CG_LimboMenu_f(); + + +/* +================= +CG_ParseScores + +================= +*/ +// Gordon: NOTE: team doesnt actually signify team, think i was on drugs that day..... +static void CG_ParseScore( team_t team ) { + int i, j, powerups; + int numScores; + int offset; + + if ( team == TEAM_AXIS ) { + cg.numScores = 0; + + cg.teamScores[0] = atoi( CG_Argv( 1 ) ); + cg.teamScores[1] = atoi( CG_Argv( 2 ) ); + + offset = 4; + } else { + offset = 2; + } + + + numScores = atoi( CG_Argv( offset - 1 ) ); + + for ( j = 0; j < numScores; j++ ) { + i = cg.numScores; + + cg.scores[i].client = atoi( CG_Argv( offset + 0 + ( j * 7 ) ) ); + cg.scores[i].score = atoi( CG_Argv( offset + 1 + ( j * 7 ) ) ); + cg.scores[i].ping = atoi( CG_Argv( offset + 2 + ( j * 7 ) ) ); + cg.scores[i].time = atoi( CG_Argv( offset + 3 + ( j * 7 ) ) ); + powerups = atoi( CG_Argv( offset + 4 + ( j * 7 ) ) ); + cg.scores[i].playerClass = atoi( CG_Argv( offset + 5 + ( j * 7 ) ) ); + cg.scores[i].respawnsLeft = atoi( CG_Argv( offset + 6 + ( j * 7 ) ) ); + + if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) { + cg.scores[i].client = 0; + } + + cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score; + cgs.clientinfo[ cg.scores[i].client ].powerups = powerups; + + cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team; + + cg.numScores++; + } +} + +/* +================= +CG_ParseTeamInfo + +================= +*/ +#define NUMARGS 5 +static void CG_ParseTeamInfo( void ) { + int i; + int client; + int numSortedTeamPlayers; + + numSortedTeamPlayers = atoi( CG_Argv( 1 ) ); + + for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) { + client = atoi( CG_Argv( i * NUMARGS + 2 ) ); + + cgs.clientinfo[ client ].location[0] = atoi( CG_Argv( i * NUMARGS + 3 ) ); + cgs.clientinfo[ client ].location[1] = atoi( CG_Argv( i * NUMARGS + 4 ) ); + cgs.clientinfo[ client ].health = atoi( CG_Argv( i * NUMARGS + 5 ) ); + cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * NUMARGS + 6 ) ); + } +} + +/* +================ +CG_ParseServerinfo + +This is called explicitly when the gamestate is first received, +and whenever the server updates any serverinfo flagged cvars +================ +*/ +void CG_ParseServerinfo( void ) { + const char *info; + char *mapname; + + info = CG_ConfigString( CS_SERVERINFO ); + cg_gameType.integer = cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); + cg_antilag.integer = cgs.antilag = atoi( Info_ValueForKey( info, "g_antilag" ) ); + if ( !cgs.localServer ) { + trap_Cvar_Set( "g_gametype", va( "%i", cgs.gametype ) ); + trap_Cvar_Set( "g_antilag", va( "%i", cgs.antilag ) ); + trap_Cvar_Update( &cg_antilag ); + trap_Cvar_Update( &cg_gameType ); + } + cgs.timelimit = atof( Info_ValueForKey( info, "timelimit" ) ); + cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + mapname = Info_ValueForKey( info, "mapname" ); + Q_strncpyz( cgs.rawmapname, mapname, sizeof( cgs.rawmapname ) ); + Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); + +// prolly should parse all CS_SERVERINFO keys automagically, but I don't want to break anything that might be improperly set for wolf SP, so I'm just parsing MP relevant stuff here + trap_Cvar_Set( "g_redlimbotime",Info_ValueForKey( info,"g_redlimbotime" ) ); + cg_redlimbotime.integer = atoi( Info_ValueForKey( info,"g_redlimbotime" ) ); + trap_Cvar_Set( "g_bluelimbotime",Info_ValueForKey( info,"g_bluelimbotime" ) ); + cg_bluelimbotime.integer = atoi( Info_ValueForKey( info,"g_bluelimbotime" ) ); + cgs.weaponRestrictions = atoi( Info_ValueForKey( info, "g_heavyWeaponRestriction" ) ) * 0.01f; + + + cgs.minclients = atoi( Info_ValueForKey( info, "g_minGameClients" ) ); // NERVE - SMF -- OSP: overloaded for ready counts + + // TTimo - make this available for ingame_callvote + trap_Cvar_Set( "cg_ui_voteFlags", ( ( authLevel.integer == RL_NONE ) ? Info_ValueForKey( info, "voteFlags" ) : "0" ) ); +} + +/* +================== +CG_ParseWarmup +================== +*/ +static void CG_ParseWarmup( void ) { + const char *info; + int warmup; + + info = CG_ConfigString( CS_WARMUP ); + + warmup = atoi( info ); + + if ( warmup == 0 && cg.warmup ) { + + } else if ( warmup > 0 && cg.warmup <= 0 && cgs.gamestate != GS_WARMUP ) { +// if(cg_announcer.integer > 0) trap_S_StartLocalSound( cgs.media.countPrepare, CHAN_ANNOUNCER ); + if ( !cg.demoPlayback && cg_autoAction.integer & AA_DEMORECORD ) { + CG_autoRecord_f(); + } + + if ( cg.warmupCount >= 0 ) { + Pri( "^3All players ready!^7\nMatch starting...\n" ); + CPri( "^3All players ready!^7\nMatch starting..." ); + } + } + + if ( cgs.gamestate != GS_WARMUP || cg.warmup > 0 ) { + cg.warmup = warmup; + } + + cg.warmupCount++; +} + +/* +================== +CG_ParseOIDInfo +================== +*/ + +oidInfo_t* CG_OIDInfoForEntityNum( int num ) { + int i; + + for ( i = 0; i < MAX_OID_TRIGGERS; i++ ) { + if ( cgs.oidInfo[ i ].entityNum == num ) { + return &cgs.oidInfo[ i ]; + } + } + + return NULL; +} + +void CG_ParseOIDInfo( int num ) { + const char* info; + const char* cs; + int index = num - CS_OID_DATA; + + info = CG_ConfigString( num ); + + memset( &cgs.oidInfo[ index ], 0, sizeof( cgs.oidInfo[ 0 ] ) ); + + if ( !info || !*info ) { + return; + } + + cs = Info_ValueForKey( info, "s" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].spawnflags = atoi( cs ); + } + + cs = Info_ValueForKey( info, "cia" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].customimageallies = cgs.gameShaders[atoi( cs )]; + } + + cs = Info_ValueForKey( info, "cix" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].customimageaxis = cgs.gameShaders[atoi( cs )]; + } + + cs = Info_ValueForKey( info, "o" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].objflags = atoi( cs ); + } + + cs = Info_ValueForKey( info, "e" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].entityNum = atoi( cs ); + } + + cs = Info_ValueForKey( info, "n" ); + if ( cs && *cs ) { + Q_strncpyz( cgs.oidInfo[ index ].name, cs, sizeof( cgs.oidInfo[ 0 ].name ) ); + } + + cs = Info_ValueForKey( info, "x" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].origin[0] = atoi( cs ); + } + + cs = Info_ValueForKey( info, "y" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].origin[1] = atoi( cs ); + } + + cs = Info_ValueForKey( info, "z" ); + if ( cs && *cs ) { + cgs.oidInfo[ index ].origin[2] = atoi( cs ); + } +} + +void CG_ParseOIDInfos( void ) { + int i; + + for ( i = 0; i < MAX_OID_TRIGGERS; i++ ) { + CG_ParseOIDInfo( CS_OID_DATA + i ); + } +} + +/* +================== +CG_ParseWolfinfo + +NERVE - SMF +================== +*/ +void CG_ParseWolfinfo( void ) { + int old_gs = cgs.gamestate; + const char *info; + + info = CG_ConfigString( CS_WOLFINFO ); + + cgs.currentRound = atoi( Info_ValueForKey( info, "g_currentRound" ) ); + cgs.nextTimeLimit = atof( Info_ValueForKey( info, "g_nextTimeLimit" ) ); + cgs.gamestate = atoi( Info_ValueForKey( info, "gamestate" ) ); + cgs.currentCampaign = Info_ValueForKey( info, "g_currentCampaign" ); + cgs.currentCampaignMap = atoi( Info_ValueForKey( info, "g_currentCampaignMap" ) ); + + // OSP - Announce game in progress if we are really playing + if ( old_gs != GS_PLAYING && cgs.gamestate == GS_PLAYING ) { +// if(cg_announcer.integer > 0) trap_S_StartLocalSound(cgs.media.countFight, CHAN_ANNOUNCER); + Pri( "^1FIGHT!\n" ); + CPri( "^1FIGHT!\n" ); + } + + if ( !cgs.localServer ) { + trap_Cvar_Set( "gamestate", va( "%i", cgs.gamestate ) ); + } + + if ( old_gs != GS_WARMUP_COUNTDOWN && cgs.gamestate == GS_WARMUP_COUNTDOWN ) { + CG_ParseWarmup(); + } +} + +/* +================== +CG_ParseSpawns +================== +*/ +void CG_ParseSpawns( void ) { + const char *info; + const char *s; + int i; + int newteam; + + info = CG_ConfigString( CS_MULTI_INFO ); + s = Info_ValueForKey( info, "numspawntargets" ); + + if ( !s || !strlen( s ) ) { + return; + } + + // first index is for autopicking + Q_strncpyz( cg.spawnPoints[0], CG_TranslateString( "Auto Pick" ), MAX_SPAWNDESC ); + + cg.spawnCount = atoi( s ) + 1; + + for ( i = 1; i < cg.spawnCount; i++ ) { + info = CG_ConfigString( CS_MULTI_SPAWNTARGETS + i - 1 ); + + s = Info_ValueForKey( info, "spawn_targ" ); + + if ( !s || !strlen( s ) ) { + return; + } + + Q_strncpyz( cg.spawnPoints[i], CG_TranslateString( s ), MAX_SPAWNDESC ); + + s = Info_ValueForKey( info, "x" ); + if ( !s || !strlen( s ) ) { + return; + } + cg.spawnCoordsUntransformed[i][0] = cg.spawnCoords[i][0] = atof( s ); + + s = Info_ValueForKey( info, "y" ); + if ( !s || !strlen( s ) ) { + return; + } + cg.spawnCoordsUntransformed[i][1] = cg.spawnCoords[i][1] = atof( s ); + + if ( cgs.ccLayers ) { + s = Info_ValueForKey( info, "z" ); + if ( !s || !strlen( s ) ) { + return; + } + cg.spawnCoordsUntransformed[i][2] = cg.spawnCoords[i][2] = atof( s ); + } + + CG_TransformToCommandMapCoord( &cg.spawnCoords[i][0], &cg.spawnCoords[i][1] ); + + s = Info_ValueForKey( info, "t" ); + + newteam = atoi( s ); + if ( cg.spawnTeams[i] != newteam ) { + cg.spawnTeams_old[i] = cg.spawnTeams[i]; + cg.spawnTeams_changeTime[i] = cg.time; + cg.spawnTeams[i] = newteam; + } + + s = Info_ValueForKey( info, "c" ); + cg.spawnPlayerCounts[i] = atoi( s ); + } +} + +/* +===================== +CG_ParseScreenFade +===================== +*/ +static void CG_ParseScreenFade( void ) { + const char *info; + char *token; + + info = CG_ConfigString( CS_SCREENFADE ); + + token = COM_Parse( (char **)&info ); + cgs.fadeAlpha = atof( token ); + + token = COM_Parse( (char **)&info ); + cgs.fadeStartTime = atoi( token ); + token = COM_Parse( (char **)&info ); + cgs.fadeDuration = atoi( token ); + + if ( cgs.fadeStartTime + cgs.fadeDuration < cg.time ) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } +} + + +/* +============== +CG_ParseFog + float near dist + float far dist + float density + float[3] r,g,b + int time +============== +*/ +static void CG_ParseFog( void ) { + const char *info; + char *token; + float ne, fa, r, g, b, density; + int time; + + info = CG_ConfigString( CS_FOGVARS ); + + token = COM_Parse( (char **)&info ); ne = atof( token ); + token = COM_Parse( (char **)&info ); fa = atof( token ); + token = COM_Parse( (char **)&info ); density = atof( token ); + token = COM_Parse( (char **)&info ); r = atof( token ); + token = COM_Parse( (char **)&info ); g = atof( token ); + token = COM_Parse( (char **)&info ); b = atof( token ); + token = COM_Parse( (char **)&info ); time = atoi( token ); + + if ( fa ) { // far of '0' from a target_fog means "return to map fog" + trap_R_SetFog( FOG_SERVER, (int)ne, (int)fa, r, g, b, density + .1 ); + trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_SERVER, time, 0, 0, 0, 0 ); + } else { + trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_MAP, time, 0, 0, 0, 0 ); + } +} + +static void CG_ParseGlobalFog( void ) { + const char *info; + char *token; + qboolean restore; + float r, g, b, depthForOpaque; + int duration; + + info = CG_ConfigString( CS_GLOBALFOGVARS ); + + token = COM_Parse( (char **)&info ); restore = atoi( token ); + token = COM_Parse( (char **)&info ); duration = atoi( token ); + + if ( restore ) { + trap_R_SetGlobalFog( qtrue, duration, 0.f, 0.f, 0.f, 0 ); + } else { + token = COM_Parse( (char **)&info ); r = atof( token ); + token = COM_Parse( (char **)&info ); g = atof( token ); + token = COM_Parse( (char **)&info ); b = atof( token ); + token = COM_Parse( (char **)&info ); depthForOpaque = atof( token ); + + trap_R_SetGlobalFog( qfalse, duration, r, g, b, depthForOpaque ); + } +} + +// Parse server version info (for demo playback compatibility) +void CG_ParseServerVersionInfo( const char *pszVersionInfo ) { + // This will expand to a tokenized string, eventually but for + // now we only need to worry about 1 number :) + cgs.game_versioninfo = atoi( pszVersionInfo ); +} + +// Parse reinforcement offsets +void CG_ParseReinforcementTimes( const char *pszReinfSeedString ) { + const char *tmp = pszReinfSeedString, *tmp2; + unsigned int i, j, dwDummy, dwOffset[TEAM_NUM_TEAMS]; + +#define GETVAL( x,y ) if ( ( tmp = strchr( tmp, ' ' ) ) == NULL ) {return;} x = atoi( ++tmp ) / y; + + dwOffset[TEAM_ALLIES] = atoi( pszReinfSeedString ) >> REINF_BLUEDELT; + GETVAL( dwOffset[TEAM_AXIS], ( 1 << REINF_REDDELT ) ); + tmp2 = tmp; + + for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) { + tmp = tmp2; + for ( j = 0; j < MAX_REINFSEEDS; j++ ) { + if ( j == dwOffset[i] ) { + GETVAL( cgs.aReinfOffset[i], aReinfSeeds[j] ); + cgs.aReinfOffset[i] *= 1000; + break; + } + GETVAL( dwDummy, 1 ); + } + } +} + + +/* +================ +CG_SetConfigValues + +Called on load to set the initial values from configure strings +================ +*/ +void CG_SetConfigValues( void ) { + cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) ); + cgs.intermissionStartTime = atoi( CG_ConfigString( CS_INTERMISSION_START_TIME ) ); + cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); + + // rain - set all of this crap in cgs - it won't be set if it doesn't + // change, otherwise. consider: + // vote was called 5 minutes ago for 'Match Reset'. you connect. + // you're sent that value for CS_VOTE_STRING, but ignore it, so + // you have nothing to use if another 'Match Reset' vote is called + // (no update will be sent because the string will be the same.) + + cgs.voteTime = atoi( CG_ConfigString( CS_VOTE_TIME ) ); + cgs.voteYes = atoi( CG_ConfigString( CS_VOTE_YES ) ); + cgs.voteNo = atoi( CG_ConfigString( CS_VOTE_NO ) ); + Q_strncpyz( cgs.voteString, CG_ConfigString( CS_VOTE_STRING ), sizeof( cgs.voteString ) ); + + cg.teamFirstBlood = atoi( CG_ConfigString( CS_FIRSTBLOOD ) ); + // rain - yes, the order is this way on purpose. not my fault! + cg.teamWonRounds[1] = atoi( CG_ConfigString( CS_ROUNDSCORES1 ) ); + cg.teamWonRounds[0] = atoi( CG_ConfigString( CS_ROUNDSCORES2 ) ); + + // OSP + CG_ParseServerVersionInfo( CG_ConfigString( CS_VERSIONINFO ) ); + CG_ParseReinforcementTimes( CG_ConfigString( CS_REINFSEEDS ) ); + // OSP +} + +/* +===================== +CG_ShaderStateChanged +===================== +*/ +void CG_ShaderStateChanged( void ) { + char originalShader[MAX_QPATH]; + char newShader[MAX_QPATH]; + char timeOffset[16]; + const char *o; + char *n,*t; + + o = CG_ConfigString( CS_SHADERSTATE ); + while ( o && *o ) { + n = strstr( o, "=" ); + if ( n && *n ) { + strncpy( originalShader, o, n - o ); + originalShader[n - o] = 0; + n++; + t = strstr( n, ":" ); + if ( t && *t ) { + strncpy( newShader, n, t - n ); + newShader[t - n] = 0; + } else { + break; + } + t++; + o = strstr( t, "@" ); + if ( o ) { + strncpy( timeOffset, t, o - t ); + timeOffset[o - t] = 0; + o++; + trap_R_RemapShader( cgs.gameShaderNames[atoi( originalShader )], + cgs.gameShaderNames[atoi( newShader )], + timeOffset ); + } + } else { + break; + } + } +} + +/* +=============== +CG_ChargeTimesChanged +=============== +*/ +void CG_ChargeTimesChanged( void ) { + const char *info; + + info = CG_ConfigString( CS_CHARGETIMES ); + + cg.soldierChargeTime[0] = atoi( Info_ValueForKey( info, "axs_sld" ) ); + cg.soldierChargeTime[1] = atoi( Info_ValueForKey( info, "ald_sld" ) ); + cg.medicChargeTime[0] = atoi( Info_ValueForKey( info, "axs_mdc" ) ); + cg.medicChargeTime[1] = atoi( Info_ValueForKey( info, "ald_mdc" ) ); + cg.engineerChargeTime[0] = atoi( Info_ValueForKey( info, "axs_eng" ) ); + cg.engineerChargeTime[1] = atoi( Info_ValueForKey( info, "ald_eng" ) ); + cg.ltChargeTime[0] = atoi( Info_ValueForKey( info, "axs_lnt" ) ); + cg.ltChargeTime[1] = atoi( Info_ValueForKey( info, "ald_lnt" ) ); + cg.covertopsChargeTime[0] = atoi( Info_ValueForKey( info, "axs_cvo" ) ); + cg.covertopsChargeTime[1] = atoi( Info_ValueForKey( info, "ald_cvo" ) ); +} + +/* +================ +CG_ConfigStringModified + +================ +*/ +static void CG_ConfigStringModified( void ) { + const char *str; + int num; + + num = atoi( CG_Argv( 1 ) ); + + // get the gamestate from the client system, which will have the + // new configstring already integrated + trap_GetGameState( &cgs.gameState ); + + // look up the individual string that was modified + str = CG_ConfigString( num ); + + // do something with it if necessary + if ( num == CS_MUSIC ) { + CG_StartMusic(); + } else if ( num == CS_MUSIC_QUEUE ) { + CG_QueueMusic(); + } else if ( num == CS_SERVERINFO ) { + CG_ParseServerinfo(); + } else if ( num == CS_WARMUP ) { + CG_ParseWarmup(); + } else if ( num == CS_WOLFINFO ) { // NERVE - SMF + CG_ParseWolfinfo(); + } else if ( num == CS_FIRSTBLOOD ) { + cg.teamFirstBlood = atoi( str ); + } else if ( num == CS_ROUNDSCORES1 ) { + cg.teamWonRounds[1] = atoi( str ); + } else if ( num == CS_ROUNDSCORES2 ) { + cg.teamWonRounds[0] = atoi( str ); + } else if ( num >= CS_MULTI_SPAWNTARGETS && num < CS_MULTI_SPAWNTARGETS + MAX_MULTI_SPAWNTARGETS ) { + CG_ParseSpawns(); + } else if ( num == CS_VERSIONINFO ) { + CG_ParseServerVersionInfo( str ); // OSP - set versioning info for older demo playback + } else if ( num == CS_REINFSEEDS ) { + CG_ParseReinforcementTimes( str ); // OSP - set reinforcement times for each team + } else if ( num == CS_LEVEL_START_TIME ) { + cgs.levelStartTime = atoi( str ); + } else if ( num == CS_INTERMISSION_START_TIME ) { + cgs.intermissionStartTime = atoi( str ); + } else if ( num == CS_VOTE_TIME ) { + cgs.voteTime = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_YES ) { + cgs.voteYes = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_NO ) { + cgs.voteNo = atoi( str ); + cgs.voteModified = qtrue; + } else if ( num == CS_VOTE_STRING ) { + Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) ); + } else if ( num == CS_INTERMISSION ) { + cg.intermissionStarted = atoi( str ); + } else if ( num == CS_SCREENFADE ) { + CG_ParseScreenFade(); + } else if ( num == CS_FOGVARS ) { + CG_ParseFog(); + } else if ( num == CS_GLOBALFOGVARS ) { + CG_ParseGlobalFog(); + } else if ( num >= CS_MODELS && num < CS_MODELS + MAX_MODELS ) { + cgs.gameModels[ num - CS_MODELS ] = trap_R_RegisterModel( str ); + } else if ( num >= CS_SOUNDS && num < CS_SOUNDS + MAX_SOUNDS ) { + if ( str[0] != '*' ) { // player specific sounds don't register here + + // Ridah, register sound scripts seperately + if ( !strstr( str, ".wav" ) ) { + CG_SoundScriptPrecache( str ); + } else { + cgs.gameSounds[ num - CS_SOUNDS] = trap_S_RegisterSound( str, qfalse ); //FIXME: add a compress flag? + } + + } + } else if ( num >= CS_SHADERS && num < CS_SHADERS + MAX_CS_SHADERS ) { + cgs.gameShaders[ num - CS_SHADERS ] = str[0] == '*' ? trap_R_RegisterShader( str + 1 ) : trap_R_RegisterShaderNoMip( str ); + Q_strncpyz( cgs.gameShaderNames[num - CS_SHADERS], str[0] == '*' ? str + 1 : str, MAX_QPATH ); + } else if ( num >= CS_SKINS && num < CS_SKINS + MAX_CS_SKINS ) { + cgs.gameModelSkins[ num - CS_SKINS ] = trap_R_RegisterSkin( str ); + } else if ( num >= CS_CHARACTERS && num < CS_CHARACTERS + MAX_CHARACTERS ) { + if ( !BG_FindCharacter( str ) ) { + cgs.gameCharacters[ num - CS_CHARACTERS ] = BG_FindFreeCharacter( str ); + + Q_strncpyz( cgs.gameCharacters[ num - CS_CHARACTERS ]->characterFile, str, sizeof( cgs.gameCharacters[ num - CS_CHARACTERS ]->characterFile ) ); + + if ( !CG_RegisterCharacter( str, cgs.gameCharacters[ num - CS_CHARACTERS ] ) ) { + CG_Error( "ERROR: CG_ConfigStringModified: failed to load character file '%s'\n", str ); + } + } + } else if ( num >= CS_PLAYERS && num < CS_PLAYERS + MAX_CLIENTS ) { + CG_NewClientInfo( num - CS_PLAYERS ); + } else if ( num >= CS_DLIGHTS && num < CS_DLIGHTS + MAX_DLIGHT_CONFIGSTRINGS ) { + // FIXME - dlight changes ignored! + } else if ( num == CS_SHADERSTATE ) { + CG_ShaderStateChanged(); + } else if ( num == CS_CHARGETIMES ) { + CG_ChargeTimesChanged(); + } else if ( num >= CS_FIRETEAMS && num < CS_FIRETEAMS + MAX_FIRETEAMS ) { + CG_ParseFireteams(); + } else if ( num == CS_SKYBOXORG ) { + CG_ParseSkyBox(); + } else if ( num >= CS_TAGCONNECTS && num < CS_TAGCONNECTS + MAX_TAGCONNECTS ) { + CG_ParseTagConnect( num ); + } else if ( num == CS_ALLIED_MAPS_XP || num == CS_AXIS_MAPS_XP ) { + CG_ParseTeamXPs( num - CS_AXIS_MAPS_XP ); + } else if ( num == CS_FILTERCAMS ) { + cg.filtercams = atoi( str ) ? qtrue : qfalse; + } else if ( num >= CS_OID_DATA && num < CS_OID_DATA + MAX_OID_TRIGGERS ) { + CG_ParseOIDInfo( num ); + } +} + +/* +======================= +CG_AddToTeamChat + +======================= +*/ +static void CG_AddToTeamChat( const char *str, int clientnum ) { + int len; + char *p, *ls; + int lastcolor; + int chatHeight; + + if ( cg_teamChatHeight.integer < TEAMCHAT_HEIGHT ) { + chatHeight = cg_teamChatHeight.integer; + } else { + chatHeight = TEAMCHAT_HEIGHT; + } + + if ( chatHeight <= 0 || cg_teamChatTime.integer <= 0 ) { + // team chat disabled, dump into normal chat + cgs.teamChatPos = cgs.teamLastChatPos = 0; + return; + } + + len = 0; + + p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; + *p = 0; + + lastcolor = '7'; + + ls = NULL; + while ( *str ) { + if ( len > TEAMCHAT_WIDTH - 1 ) { + if ( ls ) { + str -= ( p - ls ); + str++; + p -= ( p - ls ); + } + *p = 0; + + cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; + cgs.teamChatMsgTeams[cgs.teamChatPos % chatHeight] = cgs.clientinfo[ clientnum ].team; + + cgs.teamChatPos++; + p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight]; + *p = 0; + *p++ = Q_COLOR_ESCAPE; + *p++ = lastcolor; + len = 0; + ls = NULL; + } + + if ( Q_IsColorString( str ) ) { + *p++ = *str++; + lastcolor = *str; + *p++ = *str++; + continue; + } + if ( *str == ' ' ) { + ls = p; + } + *p++ = *str++; + len++; + } + *p = 0; + + cgs.teamChatMsgTeams[cgs.teamChatPos % chatHeight] = cgs.clientinfo[ clientnum ].team; + cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time; + cgs.teamChatPos++; + + if ( cgs.teamChatPos - cgs.teamLastChatPos > chatHeight ) { + cgs.teamLastChatPos = cgs.teamChatPos - chatHeight; + } +} + +/* +======================= +CG_AddToNotify + +======================= +*/ +void CG_AddToNotify( const char *str ) { + int len; + char *p, *ls; + int lastcolor; + int chatHeight; + float notifytime; + char var[MAX_TOKEN_CHARS]; + + trap_Cvar_VariableStringBuffer( "con_notifytime", var, sizeof( var ) ); + notifytime = atof( var ) * 1000; + + chatHeight = NOTIFY_HEIGHT; + + if ( chatHeight <= 0 || notifytime <= 0 ) { + // team chat disabled, dump into normal chat + cgs.notifyPos = cgs.notifyLastPos = 0; + return; + } + + len = 0; + + p = cgs.notifyMsgs[cgs.notifyPos % chatHeight]; + *p = 0; + + lastcolor = '7'; + + ls = NULL; + while ( *str ) { + if ( len > NOTIFY_WIDTH - 1 || ( *str == '\n' && ( *( str + 1 ) != 0 ) ) ) { + if ( ls ) { + str -= ( p - ls ); + str++; + p -= ( p - ls ); + } + *p = 0; + + cgs.notifyMsgTimes[cgs.notifyPos % chatHeight] = cg.time; + + cgs.notifyPos++; + p = cgs.notifyMsgs[cgs.notifyPos % chatHeight]; + *p = 0; + *p++ = Q_COLOR_ESCAPE; + *p++ = lastcolor; + len = 0; + ls = NULL; + } + + if ( Q_IsColorString( str ) ) { + *p++ = *str++; + lastcolor = *str; + *p++ = *str++; + continue; + } + if ( *str == ' ' ) { + ls = p; + } + while ( *str == '\n' ) { + // TTimo gcc warning: value computed is not used + // was *str++; + str++; + } + + if ( *str ) { + *p++ = *str++; + len++; + } + } + *p = 0; + + cgs.notifyMsgTimes[cgs.notifyPos % chatHeight] = cg.time; + cgs.notifyPos++; + + if ( cgs.notifyPos - cgs.notifyLastPos > chatHeight ) { + cgs.notifyLastPos = cgs.notifyPos - chatHeight; + } +} + +/* +=============== +CG_MapRestart + +The server has issued a map_restart, so the next snapshot +is completely new and should not be interpolated to. + +A tournement restart will clear everything, but doesn't +require a reload of all the media +=============== +*/ +static void CG_MapRestart( void ) { + if ( cg_showmiss.integer ) { + CG_Printf( "CG_MapRestart\n" ); + } + + memset( &cg.lastWeapSelInBank[0], 0, MAX_WEAP_BANKS_MP * sizeof( int ) ); // clear weapon bank selections + + cg.numbufferedSoundScripts = 0; + + cg.centerPrintTime = 0; // reset centerprint counter so previous messages don't re-appear + cg.itemPickupTime = 0; // reset item pickup counter so previous messages don't re-appear + cg.cursorHintFade = 0; // reset cursor hint timer + + // DHM - Nerve :: Reset complaint system + cgs.complaintClient = -1; + cgs.complaintEndTime = 0; + + CG_LimboPanel_RequestObjective(); + + // (SA) clear zoom (so no warpies) + cg.zoomedBinoc = qfalse; + cg.zoomedScope = qfalse; + cg.zoomTime = 0; + cg.zoomval = 0; + + cgs.complaintEndTime = 0; + cgs.invitationEndTime = 0; + cgs.applicationEndTime = 0; + cgs.propositionEndTime = 0; + cgs.autoFireteamEndTime = 0; + cgs.autoFireteamCreateEndTime = 0; + + // reset fog to world fog (if present) + trap_R_SetFog( FOG_CMD_SWITCHFOG, FOG_MAP,20,0,0,0,0 ); + + // clear pmext + memset( &cg.pmext, 0, sizeof( cg.pmext ) ); + + cg.pmext.bAutoReload = ( cg_autoReload.integer > 0 ); + + numSplinePaths = 0; + numPathCorners = 0; + cg.numOIDtriggers2 = 0; + + cgs.fadeStartTime = 0; + cgs.fadeAlpha = 0; + trap_Cvar_Set( "cg_letterbox", "0" ); + + CG_ParseWolfinfo(); + + CG_ParseEntitiesFromString(); + + CG_LoadObjectiveData(); + + CG_InitLocalEntities(); + CG_InitMarkPolys(); + + cg.editingSpeakers = qfalse; + + BG_BuildSplinePaths(); + + InitSmokeSprites(); + + //Rafael particles + CG_ClearParticles(); + + // Ridah, trails +// CG_ClearTrails (); + // done. + + // Ridah + CG_ClearFlameChunks(); + CG_SoundInit(); + // done. + + cg.intermissionStarted = qfalse; + cg.lightstylesInited = qfalse; + cg.mapRestart = qtrue; + cg.timelimitWarnings = 0; + cgs.voteTime = 0; + cgs.dumpStatsTime = 0; + + CG_StartMusic(); + + trap_S_ClearLoopingSounds(); + trap_S_ClearSounds( qfalse ); + + // ydnar + trap_R_ClearDecals(); + + cg.latchAutoActions = qfalse; + cg.latchVictorySound = qfalse; // NERVE - SMF +// JPW NERVE -- reset render flags + cg_fxflags = 0; +// jpw + + // we really should clear more parts of cg here and stop sounds + cg.v_dmg_time = 0; + cg.v_noFireTime = 0; + cg.v_fireTime = 0; + + cg.filtercams = atoi( CG_ConfigString( CS_FILTERCAMS ) ) ? qtrue : qfalse; + + CG_ChargeTimesChanged(); + + CG_ParseFireteams(); + + CG_ParseOIDInfos(); + + CG_InitPM(); + + CG_ParseSpawns(); + + CG_ParseTagConnects(); + + trap_Cvar_Set( "cg_thirdPerson", "0" ); +} +// NERVE - SMF + + +#define MAX_VOICEFILES 8 + +// TAT - 10/28/2002 we've got some really big VO files now +#define MAX_VOICEFILESIZE 32768 +#define MAX_VOICECHATS 272 +// TAT - NOTE: If we're worried about space - do we really need 96 possible sounds for any one chat? +// I think this is used to allow multiple sound clips for one command, so do we need 96 available selection sounds? +#define MAX_VOICESOUNDS 32 + +#define MAX_CHATSIZE 64 +#define MAX_HEADMODELS 64 + +typedef struct voiceChat_s +{ + char id[64]; + int numSounds; + sfxHandle_t sounds[MAX_VOICESOUNDS]; + char chats[MAX_VOICESOUNDS][MAX_CHATSIZE]; + qhandle_t sprite[MAX_VOICESOUNDS]; // DHM - Nerve +} voiceChat_t; + +typedef struct voiceChatList_s +{ + char name[64]; + int gender; + int numVoiceChats; + voiceChat_t voiceChats[MAX_VOICECHATS]; +} voiceChatList_t; + +typedef struct headModelVoiceChat_s +{ + char headmodel[64]; + int voiceChatNum; +} headModelVoiceChat_t; + +voiceChatList_t voiceChatLists[MAX_VOICEFILES]; +headModelVoiceChat_t headModelVoiceChat[MAX_HEADMODELS]; + +/* +================= +CG_ParseVoiceChats +================= +*/ +int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, int maxVoiceChats ) { + int len, i; + int current = 0; + fileHandle_t f; + char buf[MAX_VOICEFILESIZE]; + char **p, *ptr; + char *token; + voiceChat_t *voiceChats; + qboolean compress; + + compress = qtrue; + if ( cg_buildScript.integer ) { + compress = qfalse; + } + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( S_COLOR_RED "voice chat file not found: %s\n", filename ) ); + return qfalse; + } + if ( len >= MAX_VOICEFILESIZE ) { + trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); + trap_FS_FCloseFile( f ); + return qfalse; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + ptr = buf; + p = &ptr; + + Com_sprintf( voiceChatList->name, sizeof( voiceChatList->name ), "%s", filename ); + voiceChats = voiceChatList->voiceChats; + for ( i = 0; i < maxVoiceChats; i++ ) { + voiceChats[i].id[0] = 0; + } + token = COM_ParseExt( p, qtrue ); + if ( !token || token[0] == 0 ) { + return qtrue; + } + if ( !Q_stricmp( token, "female" ) ) { + voiceChatList->gender = GENDER_FEMALE; + } else if ( !Q_stricmp( token, "male" ) ) { + voiceChatList->gender = GENDER_MALE; + } else if ( !Q_stricmp( token, "neuter" ) ) { + voiceChatList->gender = GENDER_NEUTER; + } else { + trap_Print( va( S_COLOR_RED "expected gender not found in voice chat file: %s\n", filename ) ); + return qfalse; + } + + // Gordon: setting before call so we can load multiple files into one list + // TAT - 10/28/2002 - if you really want to be able to load multiple files, you should take out the loop + // above that clears out all the commands "voiceChats[i].id[0] = 0;" + // We don't even want the MP voice chats in SP, so no need anyway + voiceChatList->numVoiceChats = 0; + while ( 1 ) { + token = COM_ParseExt( p, qtrue ); + if ( !token || token[0] == 0 ) { + return qtrue; + } + + Com_sprintf( voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token ); + token = COM_ParseExt( p, qtrue ); + if ( Q_stricmp( token, "{" ) ) { + trap_Print( va( S_COLOR_RED "expected { found %s in voice chat file: %s\n", token, filename ) ); + return qfalse; + } + voiceChats[voiceChatList->numVoiceChats].numSounds = 0; + current = voiceChats[voiceChatList->numVoiceChats].numSounds; + + while ( 1 ) { + token = COM_ParseExt( p, qtrue ); + if ( !token || token[0] == 0 ) { + return qtrue; + } + if ( !Q_stricmp( token, "}" ) ) { + break; + } + voiceChats[voiceChatList->numVoiceChats].sounds[current] = trap_S_RegisterSound( token, compress ); + token = COM_ParseExt( p, qtrue ); + if ( !token || token[0] == 0 ) { + return qtrue; + } + Com_sprintf( voiceChats[voiceChatList->numVoiceChats].chats[current], MAX_CHATSIZE, "%s", token ); + + // DHM - Nerve :: Specify sprite shader to show above player's head + token = COM_ParseExt( p, qfalse ); + if ( !Q_stricmp( token, "}" ) || !token || token[0] == 0 ) { + voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( "sprites/voiceChat" ); + COM_RestoreParseSession( p ); + } else { + voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( token ); + if ( voiceChats[voiceChatList->numVoiceChats].sprite[current] == 0 ) { + voiceChats[voiceChatList->numVoiceChats].sprite[current] = trap_R_RegisterShader( "sprites/voiceChat" ); + } + } + // dhm - end + + voiceChats[voiceChatList->numVoiceChats].numSounds++; + current = voiceChats[voiceChatList->numVoiceChats].numSounds; + + if ( voiceChats[voiceChatList->numVoiceChats].numSounds >= MAX_VOICESOUNDS ) { + break; + } + } + + voiceChatList->numVoiceChats++; + if ( voiceChatList->numVoiceChats >= maxVoiceChats ) { + return qtrue; + } + } + return qtrue; +} + +/* +================= +CG_LoadVoiceChats +================= +*/ +void CG_LoadVoiceChats( void ) { + int size; + + size = trap_MemoryRemaining(); + voiceChatLists[0].numVoiceChats = 0; + voiceChatLists[1].numVoiceChats = 0; + + CG_ParseVoiceChats( "scripts/wm_axis_chat.voice", &voiceChatLists[0], MAX_VOICECHATS ); + CG_ParseVoiceChats( "scripts/wm_allies_chat.voice", &voiceChatLists[1], MAX_VOICECHATS ); + + CG_Printf( "voice chat memory size = %d\n", size - trap_MemoryRemaining() ); +} + +/* +================= +CG_HeadModelVoiceChats +================= +*/ +int CG_HeadModelVoiceChats( char *filename ) { + int len, i; + fileHandle_t f; + char buf[MAX_VOICEFILESIZE]; + char **p, *ptr; + char *token; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( "voice chat file not found: %s\n", filename ) ); + return -1; + } + if ( len >= MAX_VOICEFILESIZE ) { + trap_Print( va( S_COLOR_RED "voice chat file too large: %s is %i, max allowed is %i", filename, len, MAX_VOICEFILESIZE ) ); + trap_FS_FCloseFile( f ); + return -1; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + ptr = buf; + p = &ptr; + + token = COM_ParseExt( p, qtrue ); + if ( !token || token[0] == 0 ) { + return -1; + } + + for ( i = 0; i < MAX_VOICEFILES; i++ ) { + if ( !Q_stricmp( token, voiceChatLists[i].name ) ) { + return i; + } + } + + //FIXME: maybe try to load the .voice file which name is stored in token? + + return -1; +} + + +/* +================= +CG_GetVoiceChat +================= +*/ +int CG_GetVoiceChat( voiceChatList_t *voiceChatList, const char *id, sfxHandle_t *snd, qhandle_t *sprite, char **chat ) { + int i, rnd; + + for ( i = 0; i < voiceChatList->numVoiceChats; i++ ) { + if ( !Q_stricmp( id, voiceChatList->voiceChats[i].id ) ) { + rnd = random() * voiceChatList->voiceChats[i].numSounds; + *snd = voiceChatList->voiceChats[i].sounds[rnd]; + *sprite = voiceChatList->voiceChats[i].sprite[rnd]; + *chat = voiceChatList->voiceChats[i].chats[rnd]; + return qtrue; + } + } + return qfalse; +} + +/* +================= +CG_VoiceChatListForClient +================= +*/ +voiceChatList_t *CG_VoiceChatListForClient( int clientNum ) { + if ( cgs.clientinfo[ clientNum ].team == TEAM_AXIS ) { + return &voiceChatLists[0]; + } else { + return &voiceChatLists[1]; + } +} + +#define MAX_VOICECHATBUFFER 32 + +typedef struct bufferedVoiceChat_s +{ + int clientNum; + sfxHandle_t snd; + qhandle_t sprite; + int voiceOnly; + char cmd[MAX_SAY_TEXT]; + char message[MAX_SAY_TEXT]; + vec3_t origin; // NERVE - SMF +} bufferedVoiceChat_t; + +bufferedVoiceChat_t voiceChatBuffer[MAX_VOICECHATBUFFER]; + +/* +================= +CG_PlayVoiceChat +================= +*/ +void CG_PlayVoiceChat( bufferedVoiceChat_t *vchat ) { + // if we are going into the intermission, don't start any voices +/* // NERVE - SMF - don't do this in wolfMP + if ( cg.intermissionStarted ) { + return; + } +*/ + + if ( !cg_noVoiceChats.integer ) { + trap_S_StartLocalSound( vchat->snd, CHAN_VOICE ); + + // Arnout: don't show icons for the HQ (clientnum -1) + if ( vchat->clientNum != -1 ) { + // DHM - Nerve :: Show icon above head + if ( vchat->clientNum == cg.snap->ps.clientNum ) { + cg.predictedPlayerEntity.voiceChatSprite = vchat->sprite; + if ( vchat->sprite == cgs.media.voiceChatShader ) { + cg.predictedPlayerEntity.voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer; + } else { + cg.predictedPlayerEntity.voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer * 2; + } + } else { + cg_entities[ vchat->clientNum ].voiceChatSprite = vchat->sprite; + VectorCopy( vchat->origin, cg_entities[ vchat->clientNum ].lerpOrigin ); // NERVE - SMF + if ( vchat->sprite == cgs.media.voiceChatShader ) { + cg_entities[ vchat->clientNum ].voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer; + } else { + cg_entities[ vchat->clientNum ].voiceChatSpriteTime = cg.time + cg_voiceSpriteTime.integer * 2; + } + } + // dhm - end + } + + } + if ( !vchat->voiceOnly && !cg_noVoiceText.integer ) { + CG_AddToTeamChat( vchat->message, vchat->clientNum ); + CG_Printf( va( "[skipnotify]: %s\n", vchat->message ) ); // JPW NERVE + } + voiceChatBuffer[cg.voiceChatBufferOut].snd = 0; +} + +/* +===================== +CG_PlayBufferedVoieChats +===================== +*/ +void CG_PlayBufferedVoiceChats( void ) { + if ( cg.voiceChatTime < cg.time ) { + if ( cg.voiceChatBufferOut != cg.voiceChatBufferIn && voiceChatBuffer[cg.voiceChatBufferOut].snd ) { + // + CG_PlayVoiceChat( &voiceChatBuffer[cg.voiceChatBufferOut] ); + // + cg.voiceChatBufferOut = ( cg.voiceChatBufferOut + 1 ) % MAX_VOICECHATBUFFER; + cg.voiceChatTime = cg.time + 1000; + } + } +} + +/* +===================== +CG_AddBufferedVoiceChat +===================== +*/ +void CG_AddBufferedVoiceChat( bufferedVoiceChat_t *vchat ) { +// JPW NERVE new system doesn't buffer but overwrites vchats FIXME put this on a cvar to choose which to use + memcpy( &voiceChatBuffer[0],vchat,sizeof( bufferedVoiceChat_t ) ); + cg.voiceChatBufferIn = 0; + CG_PlayVoiceChat( &voiceChatBuffer[0] ); +} + +/* +================= +CG_VoiceChatLocal +================= +*/ +void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, const char *cmd, vec3_t origin ) { + char *chat; + voiceChatList_t *voiceChatList; + clientInfo_t *ci; + sfxHandle_t snd; + qhandle_t sprite; + bufferedVoiceChat_t vchat; + const char *loc = " "; // NERVE - SMF + +/* // NERVE - SMF - don't do this in wolfMP + // if we are going into the intermission, don't start any voices + if ( cg.intermissionStarted ) { + return; + } +*/ + + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { + clientNum = 0; + } + ci = &cgs.clientinfo[ clientNum ]; + + cgs.currentVoiceClient = clientNum; + + voiceChatList = CG_VoiceChatListForClient( clientNum ); + + if ( CG_GetVoiceChat( voiceChatList, cmd, &snd, &sprite, &chat ) ) { + // + if ( mode == SAY_TEAM || !cg_teamChatsOnly.integer ) { + vchat.clientNum = clientNum; + vchat.snd = snd; + vchat.sprite = sprite; + vchat.voiceOnly = voiceOnly; + VectorCopy( origin, vchat.origin ); // NERVE - SMF + Q_strncpyz( vchat.cmd, cmd, sizeof( vchat.cmd ) ); + + if ( mode != SAY_ALL ) { + // NERVE - SMF - get location + loc = BG_GetLocationString( origin ); + if ( !loc || !*loc ) { + loc = " "; + } + } + + if ( mode == SAY_TEAM ) { + Com_sprintf( vchat.message, sizeof( vchat.message ), "(%s)%c%c(%s): %c%c%s", + ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, loc, Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) ); + } else if ( mode == SAY_BUDDY ) { + Com_sprintf( vchat.message, sizeof( vchat.message ), "<%s>%c%c<%s>: %c%c%s", + ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, loc, Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) ); + } else { + Com_sprintf( vchat.message, sizeof( vchat.message ), "%s%c%c: %c%c%s", + ci->name, Q_COLOR_ESCAPE, COLOR_YELLOW, Q_COLOR_ESCAPE, color, CG_TranslateString( chat ) ); + } + CG_AddBufferedVoiceChat( &vchat ); + } + } +} + +/* +================= +CG_VoiceChat +================= +*/ +void CG_VoiceChat( int mode ) { + const char *cmd; + int clientNum, color; + qboolean voiceOnly; + vec3_t origin; // NERVE - SMF + + voiceOnly = atoi( CG_Argv( 1 ) ); + clientNum = atoi( CG_Argv( 2 ) ); + color = atoi( CG_Argv( 3 ) ); + + if ( mode != SAY_ALL ) { + // NERVE - SMF - added origin + origin[0] = atoi( CG_Argv( 5 ) ); + origin[1] = atoi( CG_Argv( 6 ) ); + origin[2] = atoi( CG_Argv( 7 ) ); + } + + cmd = CG_Argv( 4 ); + + if ( cg_noTaunt.integer != 0 ) { + if ( !strcmp( cmd, VOICECHAT_KILLINSULT ) || !strcmp( cmd, VOICECHAT_TAUNT ) || \ + !strcmp( cmd, VOICECHAT_DEATHINSULT ) || !strcmp( cmd, VOICECHAT_KILLGAUNTLET ) || \ + !strcmp( cmd, VOICECHAT_PRAISE ) ) { + return; + } + } + + CG_VoiceChatLocal( mode, voiceOnly, clientNum, color, cmd, origin ); +} +// -NERVE - SMF + +/* +================= +CG_RemoveChatEscapeChar +================= +*/ +static void CG_RemoveChatEscapeChar( char *text ) { + int i, l; + + l = 0; + for ( i = 0; text[i]; i++ ) { + if ( text[i] == '\x19' ) { + continue; + } + text[l++] = text[i]; + } + text[l] = '\0'; +} + +/* +================= +CG_LocalizeServerCommand + +NERVE - SMF - localize string sent from server + +- localization is ON by default. +- use [lof] in string to turn OFF +- use [lon] in string to turn back ON +================= +*/ +const char* CG_LocalizeServerCommand( const char *buf ) { + static char token[MAX_TOKEN_CHARS]; + char temp[MAX_TOKEN_CHARS]; + qboolean togloc = qtrue; + const char *s; + int i, prev; + + memset( token, 0, sizeof( token ) ); + s = buf; + prev = 0; + + for ( i = 0; *s; i++, s++ ) { + // TTimo: + // line was: if ( *s == '[' && !Q_strncmp( s, "[lon]", 5 ) || !Q_strncmp( s, "[lof]", 5 ) ) { + // || prevails on &&, gcc warning was 'suggest parentheses around && within ||' + // modified to the correct behaviour + if ( *s == '[' && ( !Q_strncmp( s, "[lon]", 5 ) || !Q_strncmp( s, "[lof]", 5 ) ) ) { + + if ( togloc ) { + memset( temp, 0, sizeof( temp ) ); + strncpy( temp, buf + prev, i - prev ); + strcat( token, CG_TranslateString( temp ) ); + } else { + strncat( token, buf + prev, i - prev ); + } + + if ( s[3] == 'n' ) { + togloc = qtrue; + } else { + togloc = qfalse; + } + + i += 5; + s += 5; + prev = i; + } + } + + if ( togloc ) { + memset( temp, 0, sizeof( temp ) ); + strncpy( temp, buf + prev, i - prev ); + strcat( token, CG_TranslateString( temp ) ); + } else { + strncat( token, buf + prev, i - prev ); + } + + return token; +} +// -NERVE - SMF + + +// OSP +void CG_wstatsParse_cmd( void ) { + if ( cg.showStats ) { + if ( cg.statsWindow == NULL + || cg.statsWindow->id != WID_STATS + || cg.statsWindow->inuse == qfalse + ) { + CG_createStatsWindow(); + } else if ( cg.statsWindow->state == WSTATE_SHUTDOWN ) { + cg.statsWindow->state = WSTATE_START; + cg.statsWindow->time = trap_Milliseconds(); + } + + if ( cg.statsWindow == NULL ) { + cg.showStats = qfalse; + } else { + cg.statsWindow->effects |= WFX_TEXTSIZING; + cg.statsWindow->lineCount = 0; + cg.windowCurrent = cg.statsWindow; + CG_parseWeaponStats_cmd( CG_printWindow ); + } + } +} + +void CG_topshotsParse_cmd( qboolean doBest ) { + int iArg = 1; + int iWeap = atoi( CG_Argv( iArg++ ) ); + topshotStats_t *ts = &cgs.topshots; + + ts->cWeapons = 0; + + while ( iWeap ) { + int cnum = atoi( CG_Argv( iArg++ ) ); + int hits = atoi( CG_Argv( iArg++ ) ); + int atts = atoi( CG_Argv( iArg++ ) ); + int kills = atoi( CG_Argv( iArg++ ) ); + // rain - unused + //int deaths = atoi(CG_Argv(iArg++)); + float acc = ( atts > 0 ) ? (float)( hits * 100 ) / (float)atts : 0.0f; + char name[32]; + + // rain - bump up iArg since we didn't push it into deaths, above + iArg++; + + if ( ts->cWeapons < WS_MAX * 2 ) { + BG_cleanName( cgs.clientinfo[cnum].name, name, 17, qfalse ); + Q_strncpyz( ts->strWS[ts->cWeapons++], + va( "%-12s %5.1f %4d/%-4d %5d %s", + aWeaponInfo[iWeap - 1].pszName, + acc, hits, atts, + kills, + name ), + sizeof( ts->strWS[0] ) ); + } + + iWeap = atoi( CG_Argv( iArg++ ) ); + } +} + +void CG_ParseWeaponStats( void ) { + cgs.ccWeaponShots = atoi( CG_Argv( 1 ) ); + cgs.ccWeaponHits = atoi( CG_Argv( 2 ) ); +} + +void CG_ParsePortalPos( void ) { + int i; + + cgs.ccCurrentCamObjective = atoi( CG_Argv( 1 ) ); + cgs.ccPortalEnt = atoi( CG_Argv( 8 ) ); + + for ( i = 0; i < 3; i++ ) { + cgs.ccPortalPos[i] = atoi( CG_Argv( i + 2 ) ); + } + + for ( i = 0; i < 3; i++ ) { + cgs.ccPortalAngles[i] = atoi( CG_Argv( i + 5 ) ); + } +} + + +// Cached stats +void CG_parseWeaponStatsGS_cmd( void ) { + clientInfo_t *ci; + gameStats_t *gs = &cgs.gamestats; + int i, iArg = 1; + int nClientID = atoi( CG_Argv( iArg++ ) ); + int nRounds = atoi( CG_Argv( iArg++ ) ); + int weaponMask = atoi( CG_Argv( iArg++ ) ); + int skillMask, xp = 0; + + gs->cWeapons = 0; + gs->cSkills = 0; + gs->fHasStats = qfalse; + + gs->nClientID = nClientID; + gs->nRounds = nRounds; + + ci = &cgs.clientinfo[nClientID]; + +// Q_strncpyz(strName, ci->name, sizeof(strName)); +// BG_cleanName(cgs.clientinfo[gs->nClientID].name, strName, sizeof(strName), qfalse); + + if ( weaponMask != 0 ) { + char strName[MAX_STRING_CHARS]; + + for ( i = WS_KNIFE; i < WS_MAX; i++ ) { + if ( weaponMask & ( 1 << i ) ) { + int nHits = atoi( CG_Argv( iArg++ ) ); + int nShots = atoi( CG_Argv( iArg++ ) ); + int nKills = atoi( CG_Argv( iArg++ ) ); + int nDeaths = atoi( CG_Argv( iArg++ ) ); + int nHeadshots = atoi( CG_Argv( iArg++ ) ); + + Q_strncpyz( strName, va( "%-12s ", aWeaponInfo[i].pszName ), sizeof( strName ) ); + if ( nShots > 0 || nHits > 0 ) { + Q_strcat( strName, sizeof( strName ), va( "%5.1f %4d/%-4d ", + ( ( nShots == 0 ) ? 0.0 : (float)( nHits * 100.0 / (float)nShots ) ), + nHits, nShots ) ); + } else { + Q_strcat( strName, sizeof( strName ), va( " " ) ); + } + + Q_strncpyz( gs->strWS[gs->cWeapons++], + va( "%s%5d %6d%s", strName, nKills, nDeaths, ( ( aWeaponInfo[i].fHasHeadShots ) ? va( " %9d", nHeadshots ) : "" ) ), + sizeof( gs->strWS[0] ) ); + + if ( nShots > 0 || nHits > 0 || nKills > 0 || nDeaths ) { + gs->fHasStats = qtrue; + } + } + } + + if ( gs->fHasStats ) { + int dmg_given = atoi( CG_Argv( iArg++ ) ); + int dmg_rcvd = atoi( CG_Argv( iArg++ ) ); + int team_dmg = atoi( CG_Argv( iArg++ ) ); + + Q_strncpyz( gs->strExtra[0], va( "Damage Given: %-6d Team Damage: %d", dmg_given, team_dmg ), sizeof( gs->strExtra[0] ) ); + Q_strncpyz( gs->strExtra[1], va( "Damage Recvd: %d", dmg_rcvd ), sizeof( gs->strExtra[0] ) ); + } + } + + // Derive XP from individual skill XP + skillMask = atoi( CG_Argv( iArg++ ) ); + for ( i = SK_BATTLE_SENSE; i < SK_NUM_SKILLS; i++ ) { + if ( skillMask & ( 1 << i ) ) { + ci->skillpoints[i] = atoi( CG_Argv( iArg++ ) ); + xp += ci->skillpoints[i]; + } + } + + Q_strncpyz( gs->strRank, va( "%-13s %d", ( ( ci->team == TEAM_AXIS ) ? rankNames_Axis : rankNames_Allies )[ci->rank], xp ), sizeof( gs->strRank ) ); + + if ( skillMask != 0 ) { + char *str; + + for ( i = SK_BATTLE_SENSE; i < SK_NUM_SKILLS; i++ ) { + + if ( ( skillMask & ( 1 << i ) ) == 0 ) { + continue; + } + + if ( ci->skill[i] < NUM_SKILL_LEVELS - 1 ) { + str = va( "%4d/%-4d", ci->skillpoints[i], skillLevels[ci->skill[i] + 1] ); + } else { + str = va( "%d", ci->skillpoints[i] ); + } + + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + Q_strncpyz( gs->strSkillz[gs->cSkills++], va( "%-15s %3d %s %12d", skillNames[i], ci->skill[i], str, ci->medals[i] ), sizeof( gs->strSkillz[0] ) ); + } else { + Q_strncpyz( gs->strSkillz[gs->cSkills++], va( "%-15s %3d %s", skillNames[i], ci->skill[i], str ), sizeof( gs->strSkillz[0] ) ); + } + } + } +} + + +// Client-side stat presentation +void CG_parseWeaponStats_cmd( void( txt_dump ) ( char * ) ) { + clientInfo_t *ci; + qboolean fFull = ( txt_dump != CG_printWindow ); + qboolean fHasStats = qfalse; + char strName[MAX_STRING_CHARS]; + int atts, deaths, dmg_given, dmg_rcvd, hits, kills, team_dmg, headshots; + unsigned int i, iArg = 1; + unsigned int nClient = atoi( CG_Argv( iArg++ ) ); + unsigned int nRounds = atoi( CG_Argv( iArg++ ) ); + unsigned int dwWeaponMask = atoi( CG_Argv( iArg++ ) ); + unsigned int dwSkillPointMask, xp = 0; + + ci = &cgs.clientinfo[nClient]; + + Q_strncpyz( strName, ci->name, sizeof( strName ) ); + BG_cleanName( cgs.clientinfo[nClient].name, strName, sizeof( strName ), qfalse ); + txt_dump( va( "^7Overall stats for: ^3%s ^7(^2%d^7 Round%s)\n\n", strName, nRounds, ( ( nRounds != 1 ) ? "s" : "" ) ) ); +// txt_dump(va("^7Overall stats for: ^3%s\n\n", strName)); + + if ( fFull ) { + txt_dump( "Weapon Acrcy Hits/Atts Kills Deaths Headshots\n" ); + txt_dump( "-------------------------------------------------\n" ); + } else { + txt_dump( "Weapon Acrcy Hits/Atts Kll Dth HS\n" ); + //txt_dump( "-------------------------------------\n"); + txt_dump( "\n" ); + } + + if ( !dwWeaponMask ) { + txt_dump( "^3No weapon info available.\n" ); + } else { + for ( i = WS_KNIFE; i < WS_MAX; i++ ) { + if ( dwWeaponMask & ( 1 << i ) ) { + hits = atoi( CG_Argv( iArg++ ) ); + atts = atoi( CG_Argv( iArg++ ) ); + kills = atoi( CG_Argv( iArg++ ) ); + deaths = atoi( CG_Argv( iArg++ ) ); + headshots = atoi( CG_Argv( iArg++ ) ); + + Q_strncpyz( strName, va( "^3%-9s: ", aWeaponInfo[i].pszName ), sizeof( strName ) ); + if ( atts > 0 || hits > 0 ) { + fHasStats = qtrue; + Q_strcat( strName, sizeof( strName ), va( "^7%5.1f ^5%4d/%-4d ", + ( ( atts == 0 ) ? 0.0 : (float)( hits * 100.0 / (float)atts ) ), + hits, atts ) ); + } else { + Q_strcat( strName, sizeof( strName ), va( " " ) ); + if ( kills > 0 || deaths > 0 ) { + fHasStats = qtrue; + } + } + + if ( fFull ) { + txt_dump( va( "%s^2%5d ^1%6d%s\n", strName, kills, deaths, ( ( aWeaponInfo[i].fHasHeadShots ) ? va( " ^3%9d", headshots ) : "" ) ) ); + } else { + txt_dump( va( "%s^2%3d ^1%3d%s\n", strName, kills, deaths, ( ( aWeaponInfo[i].fHasHeadShots ) ? va( " ^3%2d", headshots ) : "" ) ) ); + } + } + } + + if ( fHasStats ) { + dmg_given = atoi( CG_Argv( iArg++ ) ); + dmg_rcvd = atoi( CG_Argv( iArg++ ) ); + team_dmg = atoi( CG_Argv( iArg++ ) ); + + if ( !fFull ) { + txt_dump( "\n\n" ); + } + + txt_dump( va( "\n^3Damage Given: ^7%-6d ^3Team Damage: ^7%d\n", dmg_given, team_dmg ) ); + txt_dump( va( "^3Damage Recvd: ^7%d\n", dmg_rcvd ) ); + } + } + + if ( !fFull ) { + txt_dump( "\n\n\n" ); + } + + // Derive XP from individual skill XP + dwSkillPointMask = atoi( CG_Argv( iArg++ ) ); + for ( i = SK_BATTLE_SENSE; i < SK_NUM_SKILLS; i++ ) { + if ( dwSkillPointMask & ( 1 << i ) ) { + ci->skillpoints[i] = atoi( CG_Argv( iArg++ ) ); + xp += ci->skillpoints[i]; + } + } + + txt_dump( va( "\n^2Rank: ^7%s (%d XP)\n", ( ( ci->team == TEAM_AXIS ) ? rankNames_Axis : rankNames_Allies )[ci->rank], xp ) ); + + if ( !fFull ) { + txt_dump( "\n\n\n" ); + } + + // Medals only in campaign mode + txt_dump( va( "Skills Level/Points%s\n", ( ( cgs.gametype == GT_WOLF_CAMPAIGN ) ? " Medals" : "" ) ) ); + if ( fFull ) { + txt_dump( va( "---------------------------%s\n", ( ( cgs.gametype == GT_WOLF_CAMPAIGN ) ? "--------" : "" ) ) ); + } else { + txt_dump( "\n" ); + } + + if ( dwSkillPointMask == 0 ) { + txt_dump( "^3No skills acquired!\n" ); + } else { + char *str; + + for ( i = SK_BATTLE_SENSE; i < SK_NUM_SKILLS; i++ ) { + + if ( ( dwSkillPointMask & ( 1 << i ) ) == 0 ) { + continue; + } + + if ( ci->skill[i] < NUM_SKILL_LEVELS - 1 ) { + str = va( "%d (%d/%d)", ci->skill[i], ci->skillpoints[i], skillLevels[ci->skill[i] + 1] ); + } else { + str = va( "%d (%d)", ci->skill[i], ci->skillpoints[i] ); + } + + if ( cgs.gametype == GT_WOLF_CAMPAIGN ) { + txt_dump( va( "%-14s ^3%-12s ^2%6d\n", skillNames[i], str, ci->medals[i] ) ); + } else { + txt_dump( va( "%-14s ^3%-12s\n", skillNames[i], str ) ); + } + } + } +} + +void CG_parseBestShotsStats_cmd( qboolean doTop, void( txt_dump ) ( char * ) ) { + int iArg = 1; + qboolean fFull = ( txt_dump != CG_printWindow ); + + int iWeap = atoi( CG_Argv( iArg++ ) ); + if ( !iWeap ) { + txt_dump( va( "^3No qualifying %sshot info available.\n", ( ( doTop ) ? "top" : "bottom" ) ) ); + return; + } + + txt_dump( va( "^2%s Match Accuracies:\n", ( doTop ) ? "BEST" : "WORST" ) ); + if ( fFull ) { + txt_dump( "\n^3WP Acrcy Hits/Atts Kills Deaths\n" ); + txt_dump( "-------------------------------------------------------------\n" ); + } else { + txt_dump( "^3WP Acrcy Hits/Atts Kll Dth\n" ); + // txt_dump( "-------------------------------------------\n"); + txt_dump( "\n" ); + } + + while ( iWeap ) { + int cnum = atoi( CG_Argv( iArg++ ) ); + int hits = atoi( CG_Argv( iArg++ ) ); + int atts = atoi( CG_Argv( iArg++ ) ); + int kills = atoi( CG_Argv( iArg++ ) ); + int deaths = atoi( CG_Argv( iArg++ ) ); + float acc = ( atts > 0 ) ? (float)( hits * 100 ) / (float)atts : 0.0; + char name[32]; + + if ( fFull ) { + BG_cleanName( cgs.clientinfo[cnum].name, name, 30, qfalse ); + txt_dump( va( "^3%s ^7%5.1f ^5%4d/%-4d ^2%5d ^1%6d ^7%s\n", + aWeaponInfo[iWeap - 1].pszCode, acc, hits, atts, kills, deaths, name ) ); + } else { + BG_cleanName( cgs.clientinfo[cnum].name, name, 12, qfalse ); + txt_dump( va( "^3%s ^7%5.1f ^5%4d/%-4d ^2%3d ^1%3d ^7%s\n", + aWeaponInfo[iWeap - 1].pszCode, acc, hits, atts, kills, deaths, name ) ); + } + + iWeap = atoi( CG_Argv( iArg++ ) ); + } +} + +void CG_parseTopShotsStats_cmd( qboolean doTop, void( txt_dump ) ( char * ) ) { + int i, iArg = 1; + int cClients = atoi( CG_Argv( iArg++ ) ); + int iWeap = atoi( CG_Argv( iArg++ ) ); + int wBestAcc = atoi( CG_Argv( iArg++ ) ); + + txt_dump( va( "Weapon accuracies for: ^3%s\n", + ( iWeap >= WS_KNIFE && iWeap < WS_MAX ) ? aWeaponInfo[iWeap].pszName : "UNKNOWN" ) ); + + txt_dump( "\n^3 Acc Hits/Atts Kills Deaths\n" ); + txt_dump( "----------------------------------------------------------\n" ); + + if ( !cClients ) { + txt_dump( "NO QUALIFYING WEAPON INFO AVAILABLE.\n" ); + return; + } + + for ( i = 0; i < cClients; i++ ) { + int cnum = atoi( CG_Argv( iArg++ ) ); + int hits = atoi( CG_Argv( iArg++ ) ); + int atts = atoi( CG_Argv( iArg++ ) ); + int kills = atoi( CG_Argv( iArg++ ) ); + int deaths = atoi( CG_Argv( iArg++ ) ); + float acc = ( atts > 0 ) ? (float)( hits * 100 ) / (float)atts : 0.0; + const char* color = ( ( ( doTop ) ? acc : ( (float)wBestAcc ) + 0.999 ) >= ( ( doTop ) ? wBestAcc : acc ) ) ? "^3" : "^7"; + char name[32]; + + BG_cleanName( cgs.clientinfo[cnum].name, name, 30, qfalse ); + txt_dump( va( "%s%5.1f ^5%4d/%-4d ^2%5d ^1%6d %s%s\n", color, acc, hits, atts, kills, deaths, color, name ) ); + } +} + +void CG_scores_cmd( void ) { + const char *str = CG_Argv( 1 ); + + CG_Printf( "[skipnotify]%s", str ); + if ( cgs.dumpStatsFile > 0 ) { + char s[MAX_STRING_CHARS]; + + BG_cleanName( str, s, sizeof( s ), qtrue ); + trap_FS_Write( s, strlen( s ), cgs.dumpStatsFile ); + } + + if ( trap_Argc() > 2 ) { + if ( cgs.dumpStatsFile > 0 ) { + qtime_t ct; + + trap_RealTime( &ct ); + str = va( "\nStats recorded: %02d:%02d:%02d (%02d %s %d)\n\n\n", + ct.tm_hour, ct.tm_min, ct.tm_sec, + ct.tm_mday, aMonths[ct.tm_mon], 1900 + ct.tm_year ); + + trap_FS_Write( str, strlen( str ), cgs.dumpStatsFile ); + + CG_Printf( "[cgnotify]\n^3>>> Stats recorded to: ^7%s\n\n", cgs.dumpStatsFileName ); + trap_FS_FCloseFile( cgs.dumpStatsFile ); + cgs.dumpStatsFile = 0; + } + cgs.dumpStatsTime = 0; + } +} + +void CG_printFile( char *str ) { + CG_Printf( str ); + if ( cgs.dumpStatsFile > 0 ) { + char s[MAX_STRING_CHARS]; + + BG_cleanName( str, s, sizeof( s ), qtrue ); + trap_FS_Write( s, strlen( s ), cgs.dumpStatsFile ); + } +} + +void CG_dumpStats( void ) { + qtime_t ct; + qboolean fDoScores = qfalse; + const char *info = CG_ConfigString( CS_SERVERINFO ); + char *s = va( "^3>>> %s: ^2%s\n\n", CG_TranslateString( "Map" ), Info_ValueForKey( info, "mapname" ) ); + + trap_RealTime( &ct ); + // /me holds breath (using circular va() buffer) + if ( cgs.dumpStatsFile == 0 ) { + fDoScores = qtrue; + cgs.dumpStatsFileName = va( "stats/%d.%02d.%02d/%02d%02d%02d.txt", + 1900 + ct.tm_year, ct.tm_mon + 1, ct.tm_mday, + ct.tm_hour, ct.tm_min, ct.tm_sec ); + } + + if ( cgs.dumpStatsFile != 0 ) { + trap_FS_FCloseFile( cgs.dumpStatsFile ); + } + trap_FS_FOpenFile( cgs.dumpStatsFileName, &cgs.dumpStatsFile, FS_APPEND ); + + CG_printFile( s ); + CG_parseWeaponStats_cmd( CG_printFile ); + if ( cgs.dumpStatsFile == 0 ) { + CG_Printf( "[cgnotify]\n^3>>> %s: %s\n\n", CG_TranslateString( "Could not create logfile" ), cgs.dumpStatsFileName ); + } + + // Daisy-chain to scores info + // -- we play a game here for a statsall dump: + // 1. we still have more ws entries in the queue to parse + // 2. on the 1st ws entry, go ahead and send out the scores request + // 3. we'll just continue to parse the remaining ws entries and dump them to the log + // before the scores result would ever hit us.. thus, we still keep proper ordering :) + if ( fDoScores ) { + trap_SendClientCommand( "scores" ); + } +} +// -OSP + + +/* +================= +CG_ServerCommand + +The string has been tokenized and can be retrieved with +Cmd_Argc() / Cmd_Argv() +================= +*/ + +void CG_ForceTapOut_f( void ); + +static void CG_ServerCommand( void ) { + const char *cmd; + char text[MAX_SAY_TEXT]; + + cmd = CG_Argv( 0 ); + + if ( !cmd[0] ) { + // server claimed the command + return; + } + + if ( !strcmp( cmd, "tinfo" ) ) { + CG_ParseTeamInfo(); + return; + } + if ( !strcmp( cmd, "sc0" ) ) { + CG_ParseScore( TEAM_AXIS ); + return; + } else if ( !strcmp( cmd, "sc1" ) ) { + CG_ParseScore( TEAM_ALLIES ); + return; + } + + if ( !strcmp( cmd, "WeaponStats" ) ) { + int i, start = 1; + + for ( i = 0; i < WP_NUM_WEAPONS; i++ ) { + + if ( !BG_ValidStatWeapon( i ) ) { + continue; + } + + cgs.playerStats.weaponStats[i].kills = atoi( CG_Argv( start++ ) ); + cgs.playerStats.weaponStats[i].killedby = atoi( CG_Argv( start++ ) ); + cgs.playerStats.weaponStats[i].teamkills = atoi( CG_Argv( start++ ) ); + } + + cgs.playerStats.suicides = atoi( CG_Argv( start++ ) ); + + for ( i = 0; i < HR_NUM_HITREGIONS; i++ ) { + cgs.playerStats.hitRegions[i] = atoi( CG_Argv( start++ ) ); + } + + cgs.numOIDtriggers = atoi( CG_Argv( start++ ) ); + + for ( i = 0; i < cgs.numOIDtriggers; i++ ) { + cgs.playerStats.objectiveStats[i] = atoi( CG_Argv( start++ ) ); + cgs.teamobjectiveStats[i] = atoi( CG_Argv( start++ ) ); + } + + return; + } + + if ( !Q_stricmp( cmd, "cpm" ) ) { + CG_AddPMItem( PM_MESSAGE, CG_LocalizeServerCommand( CG_Argv( 1 ) ), cgs.media.voiceChatShader ); + return; + } + + if ( !Q_stricmp( cmd, "cp" ) ) { + // NERVE - SMF + int args = trap_Argc(); + char *s; + + if ( args >= 3 ) { + s = CG_TranslateString( CG_Argv( 1 ) ); + + if ( args == 4 ) { + s = va( "%s%s", CG_Argv( 3 ), s ); + } + + // OSP - for client logging + if ( cg_printObjectiveInfo.integer > 0 && ( args == 4 || atoi( CG_Argv( 2 ) ) > 1 ) ) { + CG_Printf( "[cgnotify]*** ^3INFO: ^5%s\n", CG_LocalizeServerCommand( CG_Argv( 1 ) ) ); + } + CG_PriorityCenterPrint( s, SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.20 ), SMALLCHAR_WIDTH, atoi( CG_Argv( 2 ) ) ); + } else { + CG_CenterPrint( CG_LocalizeServerCommand( CG_Argv( 1 ) ), SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.20 ), SMALLCHAR_WIDTH ); //----(SA) modified + } + return; + } + + if ( !Q_stricmp( cmd, "reqforcespawn" ) ) { + if ( cg_instanttapout.integer ) { + CG_ForceTapOut_f(); + } else { + if ( cgs.gametype == GT_WOLF_LMS ) { + trap_UI_Popup( UIMENU_WM_TAPOUT_LMS ); + } else { + trap_UI_Popup( UIMENU_WM_TAPOUT ); + } + } + return; + } + + if ( !Q_stricmp( cmd, "sdbg" ) ) { + CG_StatsDebugAddText( CG_Argv( 1 ) ); + return; + } + + if ( !Q_stricmp( cmd, "cs" ) ) { + CG_ConfigStringModified(); + return; + } + + if ( !Q_stricmp( cmd, "print" ) ) { + CG_Printf( "[cgnotify]%s", CG_LocalizeServerCommand( CG_Argv( 1 ) ) ); + return; + } + + if ( !Q_stricmp( cmd, "entnfo" ) ) { + char buffer[16]; + int allied_number, axis_number; + + trap_Argv( 1, buffer, 16 ); + axis_number = atoi( buffer ); + + trap_Argv( 2, buffer, 16 ); + allied_number = atoi( buffer ); + + CG_ParseMapEntityInfo( axis_number, allied_number ); + + return; + } + + if ( !Q_stricmp( cmd, "chat" ) ) { + const char *s; + + if ( cg_teamChatsOnly.integer ) { + return; + } + + if ( atoi( CG_Argv( 3 ) ) ) { + s = CG_LocalizeServerCommand( CG_Argv( 1 ) ); + } else { + s = CG_Argv( 1 ); + } + + Q_strncpyz( text, s, MAX_SAY_TEXT ); + CG_RemoveChatEscapeChar( text ); + CG_AddToTeamChat( text, atoi( CG_Argv( 2 ) ) ); + CG_Printf( "%s\n", text ); + + return; + } + + if ( !Q_stricmp( cmd, "tchat" ) ) { + const char *s; + + if ( atoi( CG_Argv( 3 ) ) ) { + s = CG_LocalizeServerCommand( CG_Argv( 1 ) ); + } else { + s = CG_Argv( 1 ); + } + + Q_strncpyz( text, s, MAX_SAY_TEXT ); + CG_RemoveChatEscapeChar( text ); + CG_AddToTeamChat( text, atoi( CG_Argv( 2 ) ) ); + CG_Printf( "%s\n", text ); // JPW NERVE + + return; + } + + if ( !Q_stricmp( cmd, "vchat" ) ) { + CG_VoiceChat( SAY_ALL ); // NERVE - SMF - enabled support + return; + } + + if ( !Q_stricmp( cmd, "vtchat" ) ) { + CG_VoiceChat( SAY_TEAM ); // NERVE - SMF - enabled support + return; + } + + if ( !Q_stricmp( cmd, "vbchat" ) ) { + CG_VoiceChat( SAY_BUDDY ); + return; + } + + // DHM - Nerve :: Allow client to lodge a complaing + if ( !Q_stricmp( cmd, "complaint" ) && cgs.gamestate == GS_PLAYING ) { + cgs.complaintEndTime = cg.time + 20000; + cgs.complaintClient = atoi( CG_Argv( 1 ) ); + + if ( cgs.complaintClient < 0 ) { + cgs.complaintEndTime = cg.time + 10000; + } + + return; + } + // dhm + + if ( !Q_stricmp( cmd, "map_restart" ) ) { + CG_MapRestart(); + return; + } + + // OSP - match stats + if ( !Q_stricmp( cmd, "sc" ) ) { + CG_scores_cmd(); + return; + } + + // OSP - weapon stats parsing + if ( !Q_stricmp( cmd, "ws" ) ) { + if ( cgs.dumpStatsTime > cg.time ) { + CG_dumpStats(); + } else { + CG_parseWeaponStats_cmd( CG_printConsoleString ); + cgs.dumpStatsTime = 0; + } + + return; + } + if ( !Q_stricmp( cmd, "wws" ) ) { + CG_wstatsParse_cmd(); + return; + } + if ( !Q_stricmp( cmd, "gstats" ) ) { + CG_parseWeaponStatsGS_cmd(); + return; + } + + // OSP - "topshots"-related commands + if ( !Q_stricmp( cmd, "astats" ) ) { + CG_parseTopShotsStats_cmd( qtrue, CG_printConsoleString ); + return; + } + if ( !Q_stricmp( cmd, "astatsb" ) ) { + CG_parseTopShotsStats_cmd( qfalse, CG_printConsoleString ); + return; + } + if ( !Q_stricmp( cmd, "bstats" ) ) { + CG_parseBestShotsStats_cmd( qtrue, CG_printConsoleString ); + return; + } + if ( !Q_stricmp( cmd, "bstatsb" ) ) { + CG_parseBestShotsStats_cmd( qfalse, CG_printConsoleString ); + return; + } +// if(!strcmp(cmd, "wastats")) { +// CG_wtopshotsParse_cmd(qfalse); +// return; +// } + if ( !Q_stricmp( cmd, "wbstats" ) ) { + CG_topshotsParse_cmd( qtrue ); + return; + } + + // Gordon: single weapon stat (requested weapon stats) + if ( !Q_stricmp( cmd, "rws" ) ) { + CG_ParseWeaponStats(); + return; + } + if ( !Q_stricmp( cmd, "portalcampos" ) ) { + CG_ParsePortalPos(); + return; + } + + if ( !Q_stricmp( cmd, "startCam" ) ) { + CG_StartCamera( CG_Argv( 1 ), atoi( CG_Argv( 2 ) ) ); + return; + } + + if ( !Q_stricmp( cmd, "SetInitialCamera" ) ) { + CG_SetInitialCamera( CG_Argv( 1 ), atoi( CG_Argv( 2 ) ) ); + return; + } + + if ( !Q_stricmp( cmd, "stopCam" ) ) { + CG_StopCamera(); + return; + } + + if ( !Q_stricmp( cmd, "setspawnpt" ) ) { + cg.selectedSpawnPoint = atoi( CG_Argv( 1 ) ) + 1; + return; + } + + if ( !strcmp( cmd, "rockandroll" ) ) { // map loaded, game is ready to begin. + // Arnout: FIXME: re-enable when we get menus that deal with fade properly +// CG_Fade(0, 0, 0, 255, cg.time, 0); // go black + //trap_UI_Popup("pregame"); // start pregame menu + //trap_Cvar_Set("cg_norender", "1"); // don't render the world until the player clicks in and the 'playerstart' func has been called (g_main in G_UpdateCvars() ~ilne 949) + + trap_S_FadeAllSound( 1.0f, 1000, qfalse ); // fade sound up + + return; + } + + if ( !Q_stricmp( cmd, "application" ) ) { + cgs.applicationEndTime = cg.time + 20000; + cgs.applicationClient = atoi( CG_Argv( 1 ) ); + + if ( cgs.applicationClient < 0 ) { + cgs.applicationEndTime = cg.time + 10000; + } + + return; + } + + if ( !Q_stricmp( cmd, "invitation" ) ) { + cgs.invitationEndTime = cg.time + 20000; + cgs.invitationClient = atoi( CG_Argv( 1 ) ); + + if ( cgs.invitationClient < 0 ) { + cgs.invitationEndTime = cg.time + 10000; + } + + return; + } + + if ( !Q_stricmp( cmd, "proposition" ) ) { + cgs.propositionEndTime = cg.time + 20000; + cgs.propositionClient = atoi( CG_Argv( 1 ) ); + cgs.propositionClient2 = atoi( CG_Argv( 2 ) ); + + if ( cgs.propositionClient < 0 ) { + cgs.propositionEndTime = cg.time + 10000; + } + + return; + } + + if ( !Q_stricmp( cmd, "aft" ) ) { + cgs.autoFireteamEndTime = cg.time + 20000; + cgs.autoFireteamNum = atoi( CG_Argv( 1 ) ); + + if ( cgs.autoFireteamNum < -1 ) { + cgs.autoFireteamEndTime = cg.time + 10000; + } + return; + } + + if ( !Q_stricmp( cmd, "aftc" ) ) { + cgs.autoFireteamCreateEndTime = cg.time + 20000; + cgs.autoFireteamCreateNum = atoi( CG_Argv( 1 ) ); + + if ( cgs.autoFireteamCreateNum < -1 ) { + cgs.autoFireteamCreateEndTime = cg.time + 10000; + } + return; + } + + if ( !Q_stricmp( cmd, "aftj" ) ) { + cgs.autoFireteamJoinEndTime = cg.time + 20000; + cgs.autoFireteamJoinNum = atoi( CG_Argv( 1 ) ); + + if ( cgs.autoFireteamJoinNum < -1 ) { + cgs.autoFireteamJoinEndTime = cg.time + 10000; + } + return; + } + + if ( Q_stricmp( cmd, "remapShader" ) == 0 ) { + if ( trap_Argc() == 4 ) { + trap_R_RemapShader( CG_Argv( 1 ), CG_Argv( 2 ), CG_Argv( 3 ) ); + } + } + +//GS Copied in code from old source for mu_start, mu_play & mu_stop + // + // music + // + + // loops \/ + if ( !Q_stricmp( cmd, "mu_start" ) ) { // has optional parameter for fade-up time + int fadeTime = 0; // default to instant start + + Q_strncpyz( text, CG_Argv( 2 ), MAX_SAY_TEXT ); + if ( text && strlen( text ) ) { + fadeTime = atoi( text ); + } + + trap_S_StartBackgroundTrack( CG_Argv( 1 ), CG_Argv( 1 ), fadeTime ); + return; + } + // plays once then back to whatever the loop was \/ + if ( !Q_stricmp( cmd, "mu_play" ) ) { // has optional parameter for fade-up time + int fadeTime = 0; // default to instant start + + Q_strncpyz( text, CG_Argv( 2 ), MAX_SAY_TEXT ); + if ( text && strlen( text ) ) { + fadeTime = atoi( text ); + } + + trap_S_StartBackgroundTrack( CG_Argv( 1 ), "onetimeonly", fadeTime ); + return; + } + + if ( !Q_stricmp( cmd, "mu_stop" ) ) { // has optional parameter for fade-down time + int fadeTime = 0; // default to instant stop + + Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); + if ( text && strlen( text ) ) { + fadeTime = atoi( text ); + } + + trap_S_FadeBackgroundTrack( 0.0f, fadeTime, 0 ); + trap_S_StartBackgroundTrack( "", "", -2 ); // '-2' for 'queue looping track' (QUEUED_PLAY_LOOPED) + return; + } + if ( !Q_stricmp( cmd, "mu_fade" ) ) { + trap_S_FadeBackgroundTrack( atof( CG_Argv( 1 ) ), atoi( CG_Argv( 2 ) ), 0 ); + return; + } + + if ( !Q_stricmp( cmd, "snd_fade" ) ) { + trap_S_FadeAllSound( atof( CG_Argv( 1 ) ), atoi( CG_Argv( 2 ) ), atoi( CG_Argv( 3 ) ) ); + return; + } + + if ( !Q_stricmp( cmd, "ftCommands" ) ) { + char info[MAX_INFO_STRING]; + trap_Argv( 1, info, sizeof( info ) ); + + cg.botMenuIcons = atoi( info ); + + return; + } + + // ensure a file gets into a build (mainly for scripted music calls) + if ( !Q_stricmp( cmd, "addToBuild" ) ) { + fileHandle_t f; + + if ( !cg_buildScript.integer ) { + return; + } + + // just open the file so it gets copied to the build dir + //CG_FileTouchForBuild(CG_Argv(1)); + trap_FS_FOpenFile( CG_Argv( 1 ), &f, FS_READ ); + trap_FS_FCloseFile( f ); + return; + } + + // ydnar: bug 267: server sends this command when it's about to kill the current server, before the client can reconnect + if ( !Q_stricmp( cmd, "spawnserver" ) ) { + // print message informing player the server is restarting with a new map + CG_PriorityCenterPrint( va( "%s", CG_TranslateString( "^3Server Restarting" ) ), SCREEN_HEIGHT - ( SCREEN_HEIGHT * 0.25 ), SMALLCHAR_WIDTH, 999999 ); + + // hack here + cg.serverRespawning = qtrue; + + // fade out over the course of 5 seconds, should be enough (nuking: atvi bug 3793) + //% CG_Fade( 0, 0, 0, 255, cg.time, 5000 ); + + return; + } + + if ( CG_Debriefing_ServerCommand( cmd ) ) { + return; + } + + CG_Printf( "Unknown client game command: %s\n", cmd ); +} + + +/* +==================== +CG_ExecuteNewServerCommands + +Execute all of the server commands that were received along +with this this snapshot. +==================== +*/ +void CG_ExecuteNewServerCommands( int latestSequence ) { + while ( cgs.serverCommandSequence < latestSequence ) { + if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) { + CG_ServerCommand(); + } + } +} + diff --git a/src/cgame/cg_snapshot.c b/src/cgame/cg_snapshot.c new file mode 100644 index 0000000..f1c135d --- /dev/null +++ b/src/cgame/cg_snapshot.c @@ -0,0 +1,600 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_snapshot.c -- things that happen on snapshot transition, +// not necessarily every single rendered frame + + + +#include "cg_local.h" +/* +#if __MACOS__ +#ifdef GAMERANGER +#include "GameRanger SDK/GameRanger.h" +#endif +#endif +*/ + +// rain - minor optimization - we only want to reset ents that were valid +// in the last frame +static qboolean oldValid[MAX_GENTITIES]; + +/* +================== +CG_ResetEntity +================== +*/ +static void CG_ResetEntity( centity_t *cent ) { + // if an event is set, assume it is new enough to use + // if the event had timed out, it would have been cleared + // RF, not needed for wolf + // DHM - Nerve :: Wolf is now using this. + cent->previousEvent = 0; + cent->previousEventSequence = cent->currentState.eventSequence; + + cent->trailTime = cg.snap->serverTime; + + // Ridah + cent->headJuncIndex = 0; + cent->headJuncIndex2 = 0; + // done. + + VectorCopy( cent->currentState.origin, cent->lerpOrigin ); + VectorCopy( cent->currentState.angles, cent->lerpAngles ); + if ( cent->currentState.eType == ET_PLAYER ) { + CG_ResetPlayerEntity( cent ); + } + + // rain - reset a bunch of extra stuff + cent->muzzleFlashTime = 0; + cent->overheatTime = 0; + + cent->miscTime = 0; + cent->soundTime = 0; + + VectorClear( cent->rawOrigin ); + VectorClear( cent->rawAngles ); + + cent->lastFuseSparkTime = 0; + cent->highlightTime = 0; + cent->highlighted = qfalse; + + cent->moving = qfalse; + cent->akimboFire = qfalse; +} + + + +/* +=============== +CG_TransitionEntity + +cent->nextState is moved to cent->currentState and events are fired +=============== +*/ +static void CG_TransitionEntity( centity_t *cent ) { + + // Ridah, update the fireDir if it's on fire + if ( CG_EntOnFire( cent ) ) { + vec3_t newDir, newPos, oldPos; + float adjust; + // + BG_EvaluateTrajectory( ¢->nextState.pos, cg.snap->serverTime, newPos, qfalse, cent->currentState.effect2Time ); + BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, oldPos, qfalse, cent->currentState.effect2Time ); + // update the fireRiseDir + VectorSubtract( oldPos, newPos, newDir ); + // fire should go upwards if travelling slow + newDir[2] += 2; + if ( VectorNormalize( newDir ) < 1 ) { + VectorClear( newDir ); + newDir[2] = 1; + } + // now move towards the newDir + adjust = 0.3; + VectorMA( cent->fireRiseDir, adjust, newDir, cent->fireRiseDir ); + if ( VectorNormalize( cent->fireRiseDir ) <= 0.1 ) { + VectorCopy( newDir, cent->fireRiseDir ); + } + } + + cent->currentState = cent->nextState; + cent->currentValid = qtrue; + + // reset if the entity wasn't in the last frame or was teleported + if ( !cent->interpolate ) { + CG_ResetEntity( cent ); + } + + // clear the next state. if will be set by the next CG_SetNextSnap + cent->interpolate = qfalse; + + // check for events + CG_CheckEvents( cent ); +} + + +/* +================== +CG_SetInitialSnapshot + +This will only happen on the very first snapshot, or +on tourney restarts. All other times will use +CG_TransitionSnapshot instead. + +FIXME: Also called by map_restart? +================== +*/ +void CG_SetInitialSnapshot( snapshot_t *snap ) { + int i; + centity_t *cent; + entityState_t *state; + char buff[16]; + + cg.snap = snap; + +// trap_S_ClearSounds( qtrue ); + + BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse ); + + // sort out solid entities + CG_BuildSolidList(); + + CG_ExecuteNewServerCommands( snap->serverCommandSequence ); + + // set our local weapon selection pointer to + // what the server has indicated the current weapon is + CG_Respawn( qfalse ); + + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + state = &cg.snap->entities[ i ]; + cent = &cg_entities[ state->number ]; + + memcpy( ¢->currentState, state, sizeof( entityState_t ) ); + //cent->currentState = *state; + cent->interpolate = qfalse; + cent->currentValid = qtrue; + + CG_ResetEntity( cent ); + + // check for events + CG_CheckEvents( cent ); + } + + cg_fxflags = 0; + + + trap_Cvar_VariableStringBuffer( "r_oldMode", buff, sizeof( buff ) ); + if ( atoi( buff ) ) { + // Arnout: confirmation screen + trap_UI_Popup( UIMENU_INGAME ); + } else if ( cg.demoPlayback ) { + ccInitial = qtrue; + } else { + static char prevmap[64] = { 0 }; + char curmap[64]; + + trap_Cvar_VariableStringBuffer( "mapname", curmap, 64 ); + + if ( Q_stricmp( curmap, prevmap ) ) { + strcpy( prevmap, curmap ); + if ( cgs.campaignInfoLoaded ) { + if ( !cg.showGameView ) { + CG_LimboMenu_f(); + } +/* } else { + ccInitial = qtrue; + + // Start the Initial Camera if specified + CG_StartInitialCamera(); */ + } + } + } + + // OSP - remove motd window + if ( cg.motdWindow != NULL ) { + CG_windowFree( cg.motdWindow ); + cg.motdWindow = NULL; + } + + // Activate alternate input handler during demo playback + if ( cg.demoPlayback ) { + CG_keyOn_f(); + if ( demo_infoWindow.integer > 0 ) { + CG_ShowHelp_On( &cg.demohelpWindow ); + } + } + // OSP + /* +#if __MACOS__ +#ifdef GAMERANGER + // LBO 12/13/04. Add support for GameRanger team voice IDs + GRSetMyTeamID(cg.snap->ps.persistant[PERS_TEAM]); +#endif +#endif + */ +} + + +/* +=================== +CG_TransitionSnapshot + +The transition point from snap to nextSnap has passed +=================== +*/ +static void CG_TransitionSnapshot( void ) { + centity_t *cent; + snapshot_t *oldFrame; + int i, id; + + if ( !cg.snap ) { + CG_Error( "CG_TransitionSnapshot: NULL cg.snap" ); + } + if ( !cg.nextSnap ) { + CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" ); + } + + // execute any server string commands before transitioning entities + CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); + + // if we had a map_restart, set everthing with initial + + if ( !( cg.snap ) || !( cg.nextSnap ) ) { + return; + } + + // rain - I hate doing things like this for enums. Oh well. + memset( &oldValid, 0, sizeof( oldValid ) ); + + // clear the currentValid flag for all entities in the existing snapshot + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + cent = &cg_entities[ cg.snap->entities[ i ].number ]; + cent->currentValid = qfalse; + oldValid[cg.snap->entities[i].number] = qtrue; + } + + // OSP -- check for MV updates from new snapshot info +#ifdef MV_SUPPORT + if ( cg.snap->ps.powerups[PW_MVCLIENTLIST] != cg.mvClientList ) { + CG_mvProcessClientList(); + } +#endif + + // move nextSnap to snap and do the transitions + oldFrame = cg.snap; + cg.snap = cg.nextSnap; + + if ( cg.snap->ps.clientNum == cg.clientNum ) { + if ( cg.xp < cg.snap->ps.stats[STAT_XP] ) { + cg.xpChangeTime = cg.time; + } + cg.xp = cg.snap->ps.stats[STAT_XP]; + } + + BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse ); + cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse; + + for ( i = 0 ; i < cg.snap->numEntities ; i++ ) { + id = cg.snap->entities[ i ].number; + CG_TransitionEntity( &cg_entities[ id ] ); + + // rain - #374 - ent doesn't exist in this frame, reset it. + // this is to fix the silent landmines bug, which is caused + // by a stale miscTime in the cent + if ( cg_entities[id].currentValid == qfalse && oldValid[id] == qtrue ) { + CG_ResetEntity( &cg_entities[id] ); + } + + if ( cg.mvTotalClients > 0 && CG_mvMergedClientLocate( id ) ) { + CG_mvUpdateClientInfo( id ); + } + } + + if ( cg.mvTotalClients > 0 ) { + CG_mvTransitionPlayerState( &cg.snap->ps ); + } + + cg.nextSnap = NULL; + + // check for playerstate transition events + if ( oldFrame ) { + playerState_t *ops, *ps; + + ops = &oldFrame->ps; + ps = &cg.snap->ps; + // teleporting checks are irrespective of prediction + if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) { + cg.thisFrameTeleport = qtrue; // will be cleared by prediction code + } + + // if we are not doing client side movement prediction for any + // reason, then the client events and view changes will be issued now + if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) + || cg_nopredict.integer +#ifdef ALLOW_GSYNC + || cg_synchronousClients.integer +#endif // ALLOW_GSYNC + ) { + CG_TransitionPlayerState( ps, ops ); + } + } + +} + + +/* +=================== +CG_SetNextSnap + +A new snapshot has just been read in from the client system. +=================== +*/ +static void CG_SetNextSnap( snapshot_t *snap ) { + int num; + entityState_t *es; + centity_t *cent; + + cg.nextSnap = snap; + + BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse ); + cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue; + + // check for extrapolation errors + for ( num = 0 ; num < snap->numEntities ; num++ ) { + es = &snap->entities[num]; + cent = &cg_entities[ es->number ]; + + memcpy( ¢->nextState, es, sizeof( entityState_t ) ); + //cent->nextState = *es; + + // if this frame is a teleport, or the entity wasn't in the + // previous frame, don't interpolate + if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) { + cent->interpolate = qfalse; + } else { + cent->interpolate = qtrue; + } + } + + // if the next frame is a teleport for the playerstate, we + // can't interpolate during demos + if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) { + cg.nextFrameTeleport = qtrue; + } else { + cg.nextFrameTeleport = qfalse; + } + + // if changing follow mode, don't interpolate + if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) { + cg.nextFrameTeleport = qtrue; + } + + // if changing server restarts, don't interpolate + if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) { + cg.nextFrameTeleport = qtrue; + } + + // sort out solid entities + CG_BuildSolidList(); +} + + +/* +======================== +CG_ReadNextSnapshot + +This is the only place new snapshots are requested +This may increment cgs.processedSnapshotNum multiple +times if the client system fails to return a +valid snapshot. +======================== +*/ +static snapshot_t *CG_ReadNextSnapshot( void ) { + qboolean r; + snapshot_t *dest; + + if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) { + CG_Printf( "[skipnotify]WARNING: CG_ReadNextSnapshot: way out of range, %i > %i\n", + cg.latestSnapshotNum, cgs.processedSnapshotNum ); + } + + while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) { + // decide which of the two slots to load it into + if ( cg.snap == &cg.activeSnapshots[0] ) { + dest = &cg.activeSnapshots[1]; + } else { + dest = &cg.activeSnapshots[0]; + } + + // try to read the snapshot from the client system + cgs.processedSnapshotNum++; + r = trap_GetSnapshot( cgs.processedSnapshotNum, dest ); + + // FIXME: why would trap_GetSnapshot return a snapshot with the same server time + if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) { + //continue; + } + + // if it succeeded, return + if ( r ) { + CG_AddLagometerSnapshotInfo( dest ); + // server has been restarted + if ( cg.snap && ( dest->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) { + cg.damageTime = 0; + cg.duckTime = -1; + cg.landTime = -1; + cg.stepTime = -1; +#ifdef SAVEGAME_SUPPORT + // savegame: we should use this as our new base snapshot + if ( CG_IsSinglePlayer() ) { + int i; + centity_t backupCent; + CG_SetInitialSnapshot( dest ); + cg.nextFrameTeleport = qtrue; + // loadgame hasn't occured yet, so this is likely wrong + //cg.weaponSelect = cg.snap->ps.weapon; + cg.weaponSelectTime = cg.time; + memset( cg.viewDamage, 0, sizeof( cg.viewDamage ) ); + // memset( cg.cameraShake, 0, sizeof(cg.cameraShake) ); + // go through an reset the cent's + for ( i = 0; i < MAX_GENTITIES; i++ ) { + backupCent = cg_entities[i]; + memset( &cg_entities[i], 0, sizeof( centity_t ) ); + cg_entities[i].currentState = backupCent.currentState; + cg_entities[i].nextState = backupCent.nextState; + cg_entities[i].currentValid = backupCent.currentValid; + cg_entities[i].interpolate = backupCent.interpolate; + } + // reset the predicted cent + memset( &cg.predictedPlayerEntity, 0, sizeof( centity_t ) ); + cg.predictedPlayerEntity.currentState = backupCent.currentState; + cg.predictedPlayerEntity.nextState = backupCent.nextState; + cg.predictedPlayerEntity.currentValid = backupCent.currentValid; + cg.predictedPlayerEntity.interpolate = backupCent.interpolate; + + return NULL; + } +#endif // SAVEGAME_SUPPORT + } +// + return dest; + } + + // a GetSnapshot will return failure if the snapshot + // never arrived, or is so old that its entities + // have been shoved off the end of the circular + // buffer in the client system. + + // record as a dropped packet + CG_AddLagometerSnapshotInfo( NULL ); + + // If there are additional snapshots, continue trying to + // read them. + } + + // nothing left to read + return NULL; +} + + +/* +============ +CG_ProcessSnapshots + +We are trying to set up a renderable view, so determine +what the simulated time is, and try to get snapshots +both before and after that time if available. + +If we don't have a valid cg.snap after exiting this function, +then a 3D game view cannot be rendered. This should only happen +right after the initial connection. After cg.snap has been valid +once, it will never turn invalid. + +Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot +hasn't arrived yet (it becomes an extrapolating situation instead +of an interpolating one) + +============ +*/ +void CG_ProcessSnapshots( void ) { + snapshot_t *snap; + int n; + + // see what the latest snapshot the client system has is + trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime ); + if ( n != cg.latestSnapshotNum ) { + if ( n < cg.latestSnapshotNum ) { + // this should never happen + CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" ); + } + cg.latestSnapshotNum = n; + } + + // If we have yet to receive a snapshot, check for it. + // Once we have gotten the first snapshot, cg.snap will + // always have valid data for the rest of the game + while ( !cg.snap ) { + snap = CG_ReadNextSnapshot(); + if ( !snap ) { + // we can't continue until we get a snapshot + return; + } + + // set our weapon selection to what + // the playerstate is currently using + if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { + CG_SetInitialSnapshot( snap ); + } + } + + // loop until we either have a valid nextSnap with a serverTime + // greater than cg.time to interpolate towards, or we run + // out of available snapshots + do { + // if we don't have a nextframe, try and read a new one in + if ( !cg.nextSnap ) { + snap = CG_ReadNextSnapshot(); + + // if we still don't have a nextframe, we will just have to + // extrapolate + if ( !snap ) { + break; + } + + CG_SetNextSnap( snap ); + + + // if time went backwards, we have a level restart + if ( cg.nextSnap->serverTime < cg.snap->serverTime ) { + CG_Error( "CG_ProcessSnapshots: Server time went backwards" ); + } + } + + // if our time is < nextFrame's, we have a nice interpolating state + if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) { + break; + } + + // we have passed the transition from nextFrame to frame + CG_TransitionSnapshot(); + } while ( 1 ); + + // assert our valid conditions upon exiting + if ( cg.snap == NULL ) { + CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" ); + } + if ( cg.time < cg.snap->serverTime ) { + // this can happen right after a vid_restart + cg.time = cg.snap->serverTime; + cgDC.realTime = cg.time; + } + if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) { + CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" ); + } + +} diff --git a/src/cgame/cg_sound.c b/src/cgame/cg_sound.c new file mode 100644 index 0000000..2d3d6c1 --- /dev/null +++ b/src/cgame/cg_sound.c @@ -0,0 +1,2079 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// Ridah, cg_sound.c - parsing and use of sound script files + +#include "cg_local.h" + +// we have to define these static lists, since we can't alloc memory within the cgame + +#define FILE_HASH_SIZE 1024 +static soundScript_t* hashTable[FILE_HASH_SIZE]; + +#define MAX_SOUND_SCRIPTS 4096 +static soundScript_t soundScripts[MAX_SOUND_SCRIPTS]; +int numSoundScripts = 0; + +#define MAX_SOUND_SCRIPT_SOUNDS 8192 +static soundScriptSound_t soundScriptSounds[MAX_SOUND_SCRIPT_SOUNDS]; +int numSoundScriptSounds = 0; + +/* +================ +return a hash value for the filename +================ +*/ +static long generateHashValue( const char *fname ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while ( fname[i] != '\0' ) { + letter = tolower( fname[i] ); + if ( letter == '.' ) { + break; // don't include extension + } + if ( letter == '\\' ) { + letter = '/'; // damn path names + } + hash += (long)( letter ) * ( i + 119 ); + i++; + } + hash &= ( FILE_HASH_SIZE - 1 ); + return hash; +} + +/* +============== +CG_SoundScriptPrecache + + returns the index+1 of the script in the global list, for fast calling +============== +*/ +int CG_SoundScriptPrecache( const char *name ) { + soundScriptSound_t *scriptSound; + long hash; + char *s; + soundScript_t *sound; +// byte buf[1024]; + int i; + + if ( !name || !name[0] ) { + return 0; + } + + hash = generateHashValue( name ); + + s = (char *)name; + for ( sound = hashTable[hash]; sound; sound = sound->nextHash ) { + if ( !Q_stricmp( s, sound->name ) ) { + // found a match, precache these sounds + scriptSound = sound->soundList; + if ( !sound->streaming ) { + for ( ; scriptSound; scriptSound = scriptSound->next ) { + for ( i = 0; i < scriptSound->numsounds; i++ ) { + scriptSound->sounds[i].sfxHandle = 0; +// scriptSound->sounds[i].sfxHandle = trap_S_RegisterSound( scriptSound->sounds[i].filename, qfalse ); // FIXME: make compressed settable through the soundscript + } + } + } else /*if (cg_buildScript.integer)*/ { // Enabled this permanently so that streaming sounds get touched within file system on startup + for ( ; scriptSound; scriptSound = scriptSound->next ) { + for ( i = 0; i < scriptSound->numsounds; i++ ) { + // just open the file so it gets copied to the build dir +/* fileHandle_t f; + trap_FS_FOpenFile( scriptSound->sounds[i].filename, &f, FS_READ ); + trap_FS_Read( buf, sizeof( buf ), f ); // read a few bytes so the operating system does a better job of caching it for us + trap_FS_FCloseFile( f );*/ + } + } + } + return sound->index + 1; + } + } + + return 0; +} + +/* +============== +CG_SoundPickOldestRandomSound +============== +*/ +int CG_SoundPickOldestRandomSound( soundScript_t *sound, vec3_t org, int entnum ) { + int oldestTime = 0; // TTimo: init + soundScriptSound_t *oldestSound; + soundScriptSound_t *scriptSound; + + oldestSound = NULL; + scriptSound = sound->soundList; + while ( scriptSound ) { + if ( !oldestSound || ( scriptSound->lastPlayed < oldestTime ) ) { + oldestTime = scriptSound->lastPlayed; + oldestSound = scriptSound; + } + scriptSound = scriptSound->next; + } + + if ( oldestSound ) { + int pos = rand() % oldestSound->numsounds; + + // play this sound + if ( !sound->streaming ) { + if ( !oldestSound->sounds[pos].sfxHandle ) { + oldestSound->sounds[pos].sfxHandle = trap_S_RegisterSound( oldestSound->sounds[pos].filename, qfalse ); // FIXME: make compressed settable through the soundscript + } + trap_S_StartSound( org, entnum, sound->channel, oldestSound->sounds[pos].sfxHandle ); + return trap_S_GetSoundLength( oldestSound->sounds[pos].sfxHandle ); + } else { + return trap_S_StartStreamingSound( oldestSound->sounds[pos].filename, sound->looping ? oldestSound->sounds[pos].filename : NULL, entnum, sound->channel, sound->attenuation ); + } + oldestSound->lastPlayed = cg.time; + } else { + CG_Error( "Unable to locate a valid sound for soundScript: %s\n", sound->name ); + } + + return 0; +} + +void CG_AddBufferedSoundScript( soundScript_t* sound ) { + if ( cg.numbufferedSoundScripts >= MAX_BUFFERED_SOUNDSCRIPTS ) { + return; + } + + cg.bufferSoundScripts[cg.numbufferedSoundScripts++] = sound; + + if ( cg.numbufferedSoundScripts == 1 ) { + cg.bufferedSoundScriptEndTime = cg.time + CG_SoundPickOldestRandomSound( cg.bufferSoundScripts[0], NULL, -1 ); + } +} + +void CG_UpdateBufferedSoundScripts( void ) { + int i; + + if ( !cg.numbufferedSoundScripts ) { + return; + } + + if ( cg.time > cg.bufferedSoundScriptEndTime ) { + for ( i = 1; i < MAX_BUFFERED_SOUNDSCRIPTS; i++ ) { + cg.bufferSoundScripts[i - 1] = cg.bufferSoundScripts[i]; + } + + cg.numbufferedSoundScripts--; + + if ( !cg.numbufferedSoundScripts ) { + return; + } + + cg.bufferedSoundScriptEndTime = cg.time + CG_SoundPickOldestRandomSound( cg.bufferSoundScripts[0], NULL, -1 ); + } +} + +/* +============== +CG_SoundPlaySoundScript + + returns qtrue is a script is found +============== +*/ +int CG_SoundPlaySoundScript( const char *name, vec3_t org, int entnum, qboolean buffer ) { + long hash; + char *s; + soundScript_t *sound; + + if ( !name || !name[0] ) { + return qfalse; + } + + hash = generateHashValue( name ); + + s = (char *)name; + sound = hashTable[hash]; + while ( sound ) { + if ( !Q_stricmp( s, sound->name ) ) { + if ( buffer ) { + CG_AddBufferedSoundScript( sound ); + return 1; + + } else { + // found a match, pick the oldest sound + return CG_SoundPickOldestRandomSound( sound, org, entnum ); + } + } + sound = sound->nextHash; + } + + CG_Printf( S_COLOR_YELLOW "WARNING: CG_SoundPlaySoundScript: cannot find sound script '%s'\n", name ); + return 0; +} + +/* +============== +CG_SoundPlayIndexedScript + + returns qtrue is a script is found +============== +*/ +void CG_SoundPlayIndexedScript( int index, vec3_t org, int entnum ) { + soundScript_t *sound; + + if ( !index ) { + return; + } + + if ( index > numSoundScripts ) { + return; + } + + sound = &soundScripts[index - 1]; + // pick the oldest sound + CG_SoundPickOldestRandomSound( sound, org, entnum ); +} + +/* +=============== +CG_SoundParseSounds +=============== +*/ +static void CG_SoundParseSounds( char *filename, char *buffer ) { + char *token, **text; + int s; + long hash; + soundScript_t sound; // the current sound being read + soundScriptSound_t *scriptSound = NULL; + qboolean inSound, wantSoundName; + + s = 0; + inSound = qfalse; + wantSoundName = qtrue; + text = &buffer; + + while ( 1 ) { + token = COM_ParseExt( text, qtrue ); + if ( !*token ) { + if ( inSound ) { + CG_Error( "no concluding '}' in sound %s, file %s\n", sound.name, filename ); + } + return; + } + + if ( !Q_stricmp( token, "{" ) ) { + if ( inSound ) { + CG_Error( "no concluding '}' in sound %s, file %s\n", sound.name, filename ); + } + if ( wantSoundName ) { + CG_Error( "'{' found but not expected, after %s, file %s\n", sound.name, filename ); + } + inSound = qtrue; + + // grab a free scriptSound + scriptSound = &soundScriptSounds[numSoundScriptSounds++]; + + if ( numSoundScripts == MAX_SOUND_SCRIPT_SOUNDS ) { + CG_Error( "MAX_SOUND_SCRIPT_SOUNDS exceeded.\nReduce number of sound scripts.\n" ); + } + + scriptSound->lastPlayed = 0; + scriptSound->next = sound.soundList; + scriptSound->numsounds = 0; + sound.soundList = scriptSound; + + continue; + } + + if ( !Q_stricmp( token, "}" ) ) { + if ( !inSound ) { + CG_Error( "'}' unexpected after sound %s, file %s\n", sound.name, filename ); + } + + // end of a sound, copy it to the global list and stick it in the hashTable + hash = generateHashValue( sound.name ); + sound.nextHash = hashTable[hash]; + soundScripts[numSoundScripts] = sound; + hashTable[hash] = &soundScripts[numSoundScripts++]; + + if ( numSoundScripts == MAX_SOUND_SCRIPTS ) { + CG_Error( "MAX_SOUND_SCRIPTS exceeded.\nReduce number of sound scripts.\n" ); + } + + inSound = qfalse; + wantSoundName = qtrue; + + CG_SoundScriptPrecache( sound.name ); + + continue; + } + + if ( !inSound ) { + // this is the identifier for a new sound + if ( !wantSoundName ) { + CG_Error( "'%s' unexpected after sound %s, file %s\n", token, sound.name, filename ); + } + memset( &sound, 0, sizeof( sound ) ); + Q_strncpyz( sound.name, token, sizeof( sound.name ) ); + wantSoundName = qfalse; + sound.index = numSoundScripts; + // setup the new sound defaults + sound.channel = CHAN_AUTO; + sound.attenuation = 1; // default to fade away with distance (for streaming sounds) + // + continue; + } + + // we are inside a sound script + + if ( !Q_stricmp( token, "channel" ) ) { + // ignore this now, just look for the channel identifiers explicitly + continue; + } + if ( !Q_stricmp( token, "local" ) ) { + sound.channel = CHAN_LOCAL; + continue; + } else if ( !Q_stricmp( token, "announcer" ) ) { + sound.channel = CHAN_ANNOUNCER; + continue; + } else if ( !Q_stricmp( token, "body" ) ) { + sound.channel = CHAN_BODY; + continue; + } else if ( !Q_stricmp( token, "voice" ) ) { + sound.channel = CHAN_VOICE; + continue; + } else if ( !Q_stricmp( token, "weapon" ) ) { + sound.channel = CHAN_WEAPON; + continue; + } else if ( !Q_stricmp( token, "item" ) ) { + sound.channel = CHAN_ITEM; + continue; + } else if ( !Q_stricmp( token, "auto" ) ) { + sound.channel = CHAN_AUTO; + continue; + } + if ( !Q_stricmp( token, "global" ) ) { + sound.attenuation = 0; + continue; + } + if ( !Q_stricmp( token, "streaming" ) ) { + sound.streaming = qtrue; + continue; + } + if ( !Q_stricmp( token, "looping" ) ) { + sound.looping = qtrue; + continue; + } + if ( !Q_stricmp( token, "sound" ) ) { + + if ( scriptSound->numsounds >= MAX_SOUNDSCRIPT_SOUNDS ) { + CG_Error( "Too many sounds for soundscript %s\n" ); + } + + token = COM_ParseExt( text, qtrue ); + + + Q_strncpyz( scriptSound->sounds[scriptSound->numsounds].filename, token, sizeof( scriptSound->sounds[0].filename ) ); + scriptSound->numsounds++; + + continue; + } + } +} + +/* +=============== +CG_SoundLoadSoundFiles +=============== +*/ +extern char bigTextBuffer[100000]; // we got it anyway, might as well use it + +#define MAX_SOUND_FILES 128 +static void CG_SoundLoadSoundFiles( void ) { + char soundFiles[MAX_SOUND_FILES][MAX_QPATH]; + char *text; + char filename[MAX_QPATH]; + fileHandle_t f; + int numSounds; + int i, len; + char *token; + + // scan for sound files + Com_sprintf( filename, MAX_QPATH, "sound/scripts/filelist.txt" ); + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + CG_Printf( S_COLOR_RED "WARNING: no sound files found (filelist.txt not found in sound/scripts)\n" ); + return; + } + if ( len > sizeof( bigTextBuffer ) ) { + CG_Error( "%s is too big, make it smaller (max = %i bytes)\n", filename, sizeof( bigTextBuffer ) ); + } + // load the file into memory + trap_FS_Read( bigTextBuffer, len, f ); + bigTextBuffer[len] = 0; + trap_FS_FCloseFile( f ); + // parse the list + text = bigTextBuffer; + numSounds = 0; + while ( 1 ) { + token = COM_ParseExt( &text, qtrue ); + if ( !token[0] ) { + break; + } + Com_sprintf( soundFiles[numSounds++], MAX_QPATH, token ); + } + + // add the map specific soundfile + Com_sprintf( soundFiles[numSounds++], MAX_QPATH, "%s.sounds", cgs.rawmapname ); + + if ( !numSounds ) { + CG_Printf( S_COLOR_RED "WARNING: no sound files found\n" ); + return; + } + + // load and parse sound files + for ( i = 0; i < numSounds; i++ ) + { + Com_sprintf( filename, sizeof( filename ), "sound/scripts/%s", soundFiles[i] ); + CG_Printf( "...loading '%s'\n", filename ); + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + if ( i != ( numSounds - 1 ) ) { + CG_Error( "Couldn't load %s", filename ); + } + continue; + } + if ( len > sizeof( bigTextBuffer ) ) { + CG_Error( "%s is too big, make it smaller (max = %i bytes)\n", filename, sizeof( bigTextBuffer ) ); + } + memset( bigTextBuffer, 0, sizeof( bigTextBuffer ) ); + trap_FS_Read( bigTextBuffer, len, f ); + trap_FS_FCloseFile( f ); + CG_SoundParseSounds( filename, bigTextBuffer ); + } +} + +/* +============== +CG_SoundInit +============== +*/ +void CG_SoundInit( void ) { + + if ( numSoundScripts ) { + // keep all the information, just reset the vars + int i, j; + + for ( i = 0; i < numSoundScriptSounds; i++ ) { + soundScriptSounds[i].lastPlayed = 0; + + for ( j = 0; j < soundScriptSounds[i].numsounds; j++ ) { + soundScriptSounds[i].sounds[j].sfxHandle = 0; + } + } + } else { + CG_Printf( "\n.........................\n" + "Initializing Sound Scripts\n" ); + CG_SoundLoadSoundFiles(); + CG_Printf( "done.\n" ); + } + +} + +// +// Script Speakers +// + +// Editing + +typedef struct editHandle_s { + vec3_t origin; + vec3_t oldOrigin; + + int activeAxis; +} editHandle_t; + +static qhandle_t speakerShader = 0; +static qhandle_t speakerShaderGrayScale = 0; +static bg_speaker_t *editSpeaker = NULL; +static bg_speaker_t undoSpeaker; +static int undoSpeakerIndex; +static qboolean editSpeakerActive = qfalse; +static editHandle_t editSpeakerHandle; +static int numSpeakersInPvs; + +static const char *s_lt_string[] = { + "no", + "on", + "off" +}; + +static const char *s_bt_string[] = { + "no", + "global", + "nopvs" +}; + +qboolean CG_SaveSpeakersToScript( void ) { + int i; + bg_speaker_t *speaker; + fileHandle_t fh; + char *s; + + if ( trap_FS_FOpenFile( va( "sound/maps/%s.sps", cgs.rawmapname ), &fh, FS_WRITE ) < 0 ) { + CG_Printf( S_COLOR_RED "ERROR: failed to save speakers to 'sound/maps/%s.sps'\n", cgs.rawmapname ); + return qfalse; + } + + s = "speakerScript\n{"; + trap_FS_Write( s, strlen( s ), fh ); + + for ( i = 0; i < BG_NumScriptSpeakers(); i++ ) { + char filenameStr[96] = ""; + char originStr[96]; + char targetnameStr[56] = ""; + char loopedStr[32]; + char broadcastStr[32]; + char waitStr[32] = ""; + char randomStr[32] = ""; + char volumeStr[32] = ""; + char rangeStr[32] = ""; + + speaker = BG_GetScriptSpeaker( i ); + + if ( *speaker->filename ) { + Com_sprintf( filenameStr, sizeof( filenameStr ), "\t\tnoise \"%s\"\n", speaker->filename ); + } + + Com_sprintf( originStr, sizeof( originStr ), "\t\torigin %.2f %.2f %.2f\n", speaker->origin[0], speaker->origin[1], speaker->origin[2] ); + + if ( *speaker->targetname ) { + Com_sprintf( targetnameStr, sizeof( targetnameStr ), "\t\ttargetname \"%s\"\n", speaker->targetname ); + } + + Com_sprintf( loopedStr, sizeof( loopedStr ), "\t\tlooped \"%s\"\n", s_lt_string[speaker->loop] ); + + Com_sprintf( broadcastStr, sizeof( broadcastStr ), "\t\tbroadcast \"%s\"\n", s_bt_string[speaker->broadcast] ); + + if ( speaker->wait ) { + Com_sprintf( waitStr, sizeof( waitStr ), "\t\twait %i\n", speaker->wait ); + } + + if ( speaker->random ) { + Com_sprintf( randomStr, sizeof( randomStr ), "\t\trandom %i\n", speaker->random ); + } + + if ( speaker->volume ) { + Com_sprintf( volumeStr, sizeof( volumeStr ), "\t\tvolume %i\n", speaker->volume ); + } + + if ( speaker->range ) { + Com_sprintf( rangeStr, sizeof( rangeStr ), "\t\trange %i\n", speaker->range ); + } + + s = va( "\n\tspeakerDef {\n%s%s%s%s%s%s%s%s%s\t}\n", + filenameStr, + originStr, + targetnameStr, + loopedStr, + broadcastStr, + waitStr, + randomStr, + volumeStr, + rangeStr ); + + trap_FS_Write( s, strlen( s ), fh ); + } + + s = "}\n"; + trap_FS_Write( s, strlen( s ), fh ); + + trap_FS_FCloseFile( fh ); + + CG_Printf( "Saved %i speakers to 'sound/maps/%s.sps'\n", BG_NumScriptSpeakers(), cgs.rawmapname ); + + return qtrue; +} + +void CG_AddLineToScene( vec3_t start, vec3_t end, vec4_t colour ) { + refEntity_t re; + + memset( &re, 0, sizeof( re ) ); + re.reType = RT_RAIL_CORE; + re.customShader = cgs.media.railCoreShader; + VectorCopy( start, re.origin ); + VectorCopy( end, re.oldorigin ); + re.shaderRGBA[0] = colour[0] * 0xff; + re.shaderRGBA[1] = colour[1] * 0xff; + re.shaderRGBA[2] = colour[2] * 0xff; + re.shaderRGBA[3] = colour[3] * 0xff; + + trap_R_AddRefEntityToScene( &re ); +} + +void CG_SetViewanglesForSpeakerEditor( void ) { + vec3_t vec; + + if ( !editSpeakerActive ) { + return; + } + + VectorSubtract( editSpeakerHandle.origin, cg.refdef_current->vieworg, vec ); + vectoangles( vec, cg.refdefViewAngles ); +} + +static void CG_RenderScriptSpeakers( void ) { + int i, j, closest; + float dist, minDist; + vec3_t vec; + refEntity_t re; + bg_speaker_t *speaker; + + closest = -1; + minDist = Square( 8.f ); + + /*{ + float r, u; + vec3_t axisOrg, dir; + + closest = -1; + minDist = Square( 32.f ); + + r = -(cg.refdef_current->fov_x / 90.f) * (float)(cgs.cursorX - 320) / 320; + u = -(cg.refdef_current->fov_y / 90.f) * (float)(cgs.cursorY - 240) / 240; + + for( i = 0; i < 3; i++ ) { + dir[i] = cg.refdef_current->viewaxis[0][i] * 1.f + + cg.refdef_current->viewaxis[1][i] * r + + cg.refdef_current->viewaxis[2][i] * u; + } + VectorNormalizeFast( dir ); + + VectorMA( cg.refdef_current->vieworg, 512, dir, vec ); + //VectorCopy( cg.refdef_current->vieworg, axisOrg ); + //axisOrg[2]+=.1f; + VectorMA( cg.refdef_current->vieworg, .1f, cg.refdef_current->viewaxis[0], axisOrg ); + CG_AddLineToScene( vec, axisOrg, colorOrange ); + + }*/ + + numSpeakersInPvs = 0; + + for ( i = 0; i < BG_NumScriptSpeakers(); i++ ) { + speaker = BG_GetScriptSpeaker( i ); + + if ( editSpeakerActive && editSpeaker == speaker ) { + vec4_t colour; + for ( j = 0; j < 3; j++ ) { + VectorClear( colour ); + colour[3] = 1.f; + if ( editSpeakerHandle.activeAxis >= 0 ) { + if ( editSpeakerHandle.activeAxis == j ) { + colour[j] = 1.f; + } else { + colour[j] = .3f; + } + } else { + colour[j] = 1.f; + } + VectorClear( vec ); + vec[j] = 1.f; + VectorMA( editSpeakerHandle.origin, 32, vec, vec ); + CG_AddLineToScene( editSpeakerHandle.origin, vec, colour ); + + memset( &re, 0, sizeof( re ) ); + re.reType = RT_SPRITE; + VectorCopy( vec, re.origin ); + VectorCopy( vec, re.oldorigin ); + re.radius = 3; + re.customShader = cgs.media.waterBubbleShader; + re.shaderRGBA[0] = colour[0] * 0xff; + re.shaderRGBA[1] = colour[1] * 0xff; + re.shaderRGBA[2] = colour[2] * 0xff; + re.shaderRGBA[3] = colour[3] * 0xff; + trap_R_AddRefEntityToScene( &re ); + } + + if ( trap_R_inPVS( cg.refdef_current->vieworg, speaker->origin ) ) { + numSpeakersInPvs++; + } + } else if ( !trap_R_inPVS( cg.refdef_current->vieworg, speaker->origin ) ) { + continue; + } else { + numSpeakersInPvs++; + } + + memset( &re, 0, sizeof( re ) ); + re.reType = RT_SPRITE; + VectorCopy( speaker->origin, re.origin ); + VectorCopy( speaker->origin, re.oldorigin ); + re.radius = 8; + re.customShader = speakerShader; + + if ( editSpeaker ) { + re.customShader = speakerShaderGrayScale; + + if ( editSpeaker == speaker ) { + re.shaderRGBA[0] = 0xff; + re.shaderRGBA[1] = 0xaa; + re.shaderRGBA[2] = 0xaa; + re.shaderRGBA[3] = 0xff; + } else { + re.shaderRGBA[0] = 0x3f; + re.shaderRGBA[1] = 0x3f; + re.shaderRGBA[2] = 0x3f; + re.shaderRGBA[3] = 0xff; + } + } else { + re.customShader = speakerShader; + + re.shaderRGBA[0] = 0xff; + re.shaderRGBA[1] = 0xff; + re.shaderRGBA[2] = 0xff; + re.shaderRGBA[3] = 0xff; + } + trap_R_AddRefEntityToScene( &re ); + + // see which one is closest to our cursor + if ( !editSpeakerActive ) { + VectorSubtract( speaker->origin, cg.refdef_current->vieworg, vec ); + dist = DotProduct( vec, cg.refdef_current->viewaxis[0] ); + VectorMA( cg.refdef_current->vieworg, dist, cg.refdef_current->viewaxis[0], vec ); + VectorSubtract( speaker->origin, vec, vec ); + dist = VectorLengthSquared( vec ); + if ( dist <= minDist ) { + minDist = dist; + closest = i; + } + } + } + + if ( !editSpeakerActive ) { + if ( closest >= 0 ) { + editSpeaker = BG_GetScriptSpeaker( closest ); + } else { + editSpeaker = NULL; + } + } +} + +void CG_SpeakerInfo_Text( panel_button_t* button ) { + char *s, *ptr, *strptr; + float y, wMax, w, h; + vec4_t colour; + char originStr[96]; + char filenameStr[96] = ""; + char targetnameStr[56] = ""; + char loopedStr[32]; + char broadcastStr[32]; + char waitStr[32] = ""; + char randomStr[32] = ""; + char volumeStr[32] = ""; + char rangeStr[32] = ""; + + if ( !button->font ) { + return; + } + + Com_sprintf( originStr, sizeof( originStr ), "Speaker at %.2f %.2f %.2f\n", editSpeaker->origin[0], editSpeaker->origin[1], editSpeaker->origin[2] ); + wMax = CG_Text_Width_Ext( originStr, button->font->scalex, 0, button->font->font ); + h = 8.5f; + + if ( *editSpeaker->filename ) { + Com_sprintf( filenameStr, sizeof( filenameStr ), "noise: %s\n", editSpeaker->filename ); + w = CG_Text_Width_Ext( filenameStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + if ( *editSpeaker->targetname ) { + Com_sprintf( targetnameStr, sizeof( targetnameStr ), "targetname: %s\n", editSpeaker->targetname ); + w = CG_Text_Width_Ext( targetnameStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + Com_sprintf( loopedStr, sizeof( loopedStr ), "looped: %s\n", s_lt_string[editSpeaker->loop] ); + w = CG_Text_Width_Ext( loopedStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + + Com_sprintf( broadcastStr, sizeof( broadcastStr ), "broadcast: %s\n", s_bt_string[editSpeaker->broadcast] ); + w = CG_Text_Width_Ext( broadcastStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + + if ( editSpeaker->wait ) { + Com_sprintf( waitStr, sizeof( waitStr ), "wait: %i\n", editSpeaker->wait ); + w = CG_Text_Width_Ext( waitStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + if ( editSpeaker->random ) { + Com_sprintf( randomStr, sizeof( randomStr ), "random: %i\n", editSpeaker->random ); + w = CG_Text_Width_Ext( randomStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + if ( editSpeaker->volume ) { + Com_sprintf( volumeStr, sizeof( volumeStr ), "volume: %i\n", editSpeaker->volume ); + w = CG_Text_Width_Ext( volumeStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + if ( editSpeaker->range ) { + Com_sprintf( rangeStr, sizeof( rangeStr ), "range: %i\n", editSpeaker->range ); + w = CG_Text_Width_Ext( rangeStr, button->font->scalex, 0, button->font->font ); + if ( w > wMax ) { + wMax = w; + } + h += 8.5f; + } + + VectorCopy( colorMdBlue, colour ); + colour[3] = .5f; + CG_FillRect( button->rect.x - 2, button->rect.y - 2, wMax + 4, h + 4, colour ); + VectorCopy( colorBlue, colour ); + CG_DrawRect( button->rect.x - 2, button->rect.y - 2, wMax + 4, h + 4, 1.f, colour ); + + s = va( "%s%s%s%s%s%s%s%s%s", + originStr, + filenameStr, + targetnameStr, + loopedStr, + broadcastStr, + waitStr, + randomStr, + volumeStr, + rangeStr ); + + y = button->rect.y + 8; + + for ( strptr = ptr = s; *ptr; ptr++ ) { + if ( *ptr == '\n' ) { + *ptr = '\0'; + CG_Text_Paint_Ext( button->rect.x, y, button->font->scalex, button->font->scaley, button->font->colour, strptr, 0, 0, button->font->style, button->font->font ); + y += 8; + strptr = ptr + 1; + } + } +} + +panel_button_text_t speakerEditorTxt = { + 0.2f, 0.2f, + { 1.0f, 1.0f, 1.0f, 0.5f }, + ITEM_TEXTSTYLE_SHADOWED, 0, + &cgs.media.limboFont2, +}; + +panel_button_t speakerInfo = { + NULL, + NULL, + { 344, 184, 272, 72 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerInfo_Text, + NULL, +}; + +static panel_button_t *speakerInfoButtons[] = { + &speakerInfo, + NULL +}; + + +void CG_SpeakerEditor_RenderEdit( panel_button_t* button ) { + vec4_t colour; + + if ( button == BG_PanelButtons_GetFocusButton() ) { + VectorCopy( colorYellow, colour ); + colour[3] = .3f; + } else { + VectorCopy( colorWhite, colour ); + colour[3] = .3f; + } + CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colour ); + + button->rect.x += 2.f; + button->rect.h -= 3.f; + BG_PanelButton_RenderEdit( button ); + button->rect.x -= 2.f; + button->rect.h += 3.f; +} + +void CG_SpeakerEditor_RenderButton( panel_button_t* button ) { + vec4_t colour; + float oldX; + + if ( button == BG_PanelButtons_GetFocusButton() ) { + VectorCopy( colorMdBlue, colour ); + colour[3] = .5f; + } else if ( BG_PanelButtons_GetFocusButton() == NULL && BG_CursorInRect( &button->rect ) ) { + VectorCopy( colorWhite, colour ); + colour[3] = .5f; + } else { + VectorCopy( colorWhite, colour ); + colour[3] = .3f; + } + CG_FillRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, colour ); + + VectorCopy( colorBlue, colour ); + CG_DrawRect( button->rect.x, button->rect.y, button->rect.w, button->rect.h, 1.f, colour ); + + oldX = button->rect.x; + button->rect.x = button->rect.x + ( button->rect.w - CG_Text_Width_Ext( button->text, button->font->scalex, 0, button->font->font ) ) / 2.f; + button->rect.y += 9.f; + BG_PanelButtonsRender_Text( button ); + button->rect.x = oldX; + button->rect.y -= 9.f; +} + +char *CG_GetStrFromStrArray( const char *in, const int index ) { + char *ptr, *s; + int i; + + s = ptr = (char *)in; + i = 0; + while ( 1 ) { + if ( i == index ) { + return s; + } + + while ( *ptr ) { + ptr++; + } + ptr++; + + s = ptr; + i++; + } +} + +void CG_SpeakerEditor_RenderDropdown( panel_button_t* button ) { + vec4_t colour; + float textboxW; + rectDef_t rect; + int i; + char *s; + + memcpy( &rect, &button->rect, sizeof( rect ) ); + + textboxW = button->rect.w - button->rect.h; + rect.x += textboxW; + rect.w = rect.h; + + if ( button == BG_PanelButtons_GetFocusButton() ) { + VectorCopy( colorYellow, colour ); + colour[3] = .3f; + } else { + VectorCopy( colorWhite, colour ); + colour[3] = .3f; + } + CG_FillRect( button->rect.x, button->rect.y, textboxW, button->rect.h, colour ); + VectorCopy( colorBlue, colour ); + CG_DrawRect( button->rect.x, button->rect.y, textboxW, button->rect.h, 1.f, colour ); + + if ( button == BG_PanelButtons_GetFocusButton() ) { + VectorCopy( colorYellow, colour ); + colour[3] = .3f; + } else if ( BG_PanelButtons_GetFocusButton() == NULL && BG_CursorInRect( &button->rect ) ) { + VectorCopy( colorWhite, colour ); + colour[3] = .5f; + } else { + VectorCopy( colorWhite, colour ); + colour[3] = .3f; + } + + CG_FillRect( rect.x, rect.y, rect.w, rect.h, colour ); + VectorCopy( colorBlue, colour ); + CG_DrawRect( rect.x, rect.y, rect.w, rect.h, 1.f, colour ); + + VectorCopy( button->font->colour, colour ); + CG_Text_Paint_Ext( rect.x + ( rect.w - CG_Text_Width_Ext( "V", button->font->scalex, 0, button->font->font ) ) / 2.f, + button->rect.y + 9.f, + button->font->scalex, + button->font->scaley, + colour, + "V", + 0, + 0, + 0, + button->font->font ); + + s = CG_GetStrFromStrArray( button->text, button->data[1] ); + + CG_Text_Paint_Ext( button->rect.x + ( textboxW - CG_Text_Width_Ext( s, button->font->scalex, 0, button->font->font ) ) / 2.f, + button->rect.y + 9.f, + button->font->scalex, + button->font->scaley, + button->font->colour, + s, + 0, + 0, + button->font->style, + button->font->font ); + + if ( button == BG_PanelButtons_GetFocusButton() ) { + memcpy( &rect, &button->rect, sizeof( rect ) ); + + for ( i = 0; i < button->data[0]; i++ ) { + if ( i == button->data[1] ) { + continue; + } + + rect.y += 12.f; + + if ( BG_CursorInRect( &rect ) ) { + VectorScale( colorYellow, .3f, colour ); + colour[3] = 1.f; + } else { + VectorScale( colorWhite, .3f, colour ); + colour[3] = 1.f; + } + + CG_FillRect( rect.x, rect.y, rect.w, rect.h, colour ); + + s = CG_GetStrFromStrArray( button->text, i ); + + CG_Text_Paint_Ext( rect.x + ( textboxW - CG_Text_Width_Ext( s, button->font->scalex, 0, button->font->font ) ) / 2.f, + rect.y + 9.f, + button->font->scalex, + button->font->scaley, + button->font->colour, + s, + 0, + 0, + button->font->style, + button->font->font ); + } + + VectorCopy( colorBlue, colour ); + colour[3] = .3f; + CG_DrawRect( button->rect.x, button->rect.y + 12.f, button->rect.w, rect.y - button->rect.y, 1.f, colour ); + } +} + +void CG_SpeakerEditor_Back( panel_button_t* button ) { + vec4_t colour; + + VectorCopy( colorMdBlue, colour ); + colour[3] = .5f; + CG_FillRect( button->rect.x - 2, button->rect.y - 2, button->rect.w + 4, button->rect.h + 4, colour ); + VectorCopy( colorBlue, colour ); + CG_DrawRect( button->rect.x - 2, button->rect.y - 2, button->rect.w + 4, button->rect.h + 4, 1.f, colour ); +} + +void CG_SpeakerEditor_LocInfo( panel_button_t* button ) { + CG_Text_Paint_Ext( button->rect.x, button->rect.y, button->font->scalex, button->font->scaley, button->font->colour, + va( "Speaker at %.2f %.2f %.2f", editSpeaker->origin[0], editSpeaker->origin[1], editSpeaker->origin[2] ), + 0, 0, button->font->style, button->font->font ); +} + +static char noiseMatchString[MAX_QPATH]; +static int noiseMatchCount; +static int noiseMatchIndex; + +qboolean CG_SpeakerEditor_NoiseEdit_KeyDown( panel_button_t* button, int key ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + if ( key == K_TAB ) { + char dirname[MAX_QPATH]; + char filename[MAX_QPATH]; + char match[MAX_QPATH]; + int i, numfiles, filelen; + char *fileptr; + + COM_StripFilename( (char *)button->text, dirname ); + Q_strncpyz( filename, COM_SkipPath( (char *)button->text ), sizeof( filename ) ); + + if ( !Q_stricmp( button->text, dirname ) ) { + return qtrue; + } + + numfiles = trap_FS_GetFileList( dirname, "", bigTextBuffer, sizeof( bigTextBuffer ) ); + // FIXME: autocomplete directories? + + fileptr = bigTextBuffer; + + if ( !*noiseMatchString || Q_stricmpn( noiseMatchString, filename, strlen( noiseMatchString ) ) ) { + Q_strncpyz( noiseMatchString, filename, sizeof( noiseMatchString ) ); + noiseMatchCount = 0; + noiseMatchIndex = 0; + + for ( i = 0; i < numfiles; i++, fileptr += filelen + 1 ) { + filelen = strlen( fileptr ); + if ( Q_stricmpn( fileptr, filename, strlen( filename ) ) ) { + continue; + } + + noiseMatchCount++; + if ( noiseMatchCount == 1 ) { + Q_strncpyz( match, fileptr, sizeof( match ) ); + continue; + } + + /*if( strlen(fileptr) < strlen(match) ) { + Q_strncpyz( match, fileptr, sizeof(match) ); + noiseMatchIndex++; + continue; + }*/ + } + } else { + if ( noiseMatchCount == 1 ) { + return qtrue; + } else { + int findMatchIndex = 0; + + noiseMatchIndex++; + if ( noiseMatchIndex == noiseMatchCount ) { + noiseMatchIndex = 0; + } + + for ( i = 0; i < numfiles; i++, fileptr += filelen + 1 ) { + filelen = strlen( fileptr ); + if ( Q_stricmpn( fileptr, noiseMatchString, strlen( noiseMatchString ) ) ) { + continue; + } + + if ( findMatchIndex == noiseMatchIndex ) { + Q_strncpyz( match, fileptr, sizeof( match ) ); + break; + } + + findMatchIndex++; + } + } + } + + if ( noiseMatchCount == 0 ) { + noiseMatchString[0] = '\0'; + return qtrue; + } + + Com_sprintf( (char *)button->text, button->data[0], "%s%s", dirname, match ); + + return qtrue; + } else { + if ( key & K_CHAR_FLAG ) { + int localkey = key; + localkey &= ~K_CHAR_FLAG; + if ( localkey == 'h' - 'a' + 1 || localkey >= 32 ) { + noiseMatchString[0] = '\0'; + } + } + } + } + + return BG_PanelButton_EditClick( button, key ); +} + +void CG_SpeakerEditor_NoiseEditFinish( panel_button_t* button ) { + Q_strncpyz( editSpeaker->filename, button->text, sizeof( editSpeaker->filename ) ); + + if ( *editSpeaker->filename ) { + editSpeaker->noise = trap_S_RegisterSound( editSpeaker->filename, qfalse ); + } else { + editSpeaker->noise = 0; + } +} + +void CG_SpeakerEditor_TargetnameEditFinish( panel_button_t* button ) { + Q_strncpyz( editSpeaker->targetname, button->text, sizeof( editSpeaker->targetname ) ); +} + +qboolean CG_SpeakerEditor_Dropdown_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + BG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Looped_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + rectDef_t rect; + int i; + + memcpy( &rect, &button->rect, sizeof( rect ) ); + + for ( i = 0; i < 3; i++ ) { + if ( i == editSpeaker->loop ) { + continue; + } + + rect.y += 12.f; + + if ( BG_CursorInRect( &rect ) ) { + button->data[1] = editSpeaker->loop = i; + break; + } + } + + if ( editSpeaker->loop == S_LT_LOOPED_ON ) { + editSpeaker->activated = qtrue; + } else { + editSpeaker->activated = qfalse; + } + + BG_PanelButtons_SetFocusButton( NULL ); + + return qtrue; + } + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Broadcast_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + rectDef_t rect; + int i; + + memcpy( &rect, &button->rect, sizeof( rect ) ); + + for ( i = 0; i < 3; i++ ) { + if ( i == editSpeaker->broadcast ) { + continue; + } + + rect.y += 12.f; + + if ( BG_CursorInRect( &rect ) ) { + button->data[1] = editSpeaker->broadcast = i; + break; + } + } + + BG_PanelButtons_SetFocusButton( NULL ); + + return qtrue; + } + } + + return qfalse; +} + +void CG_SpeakerEditor_WaitEditFinish( panel_button_t* button ) { + if ( *button->text ) { + editSpeaker->wait = atoi( button->text ); + if ( editSpeaker->wait < 0 ) { + editSpeaker->wait = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->range ); + } + } else { + editSpeaker->wait = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->wait ); + } +} + +void CG_SpeakerEditor_RandomEditFinish( panel_button_t* button ) { + if ( *button->text ) { + editSpeaker->random = atoi( button->text ); + if ( editSpeaker->random < 0 ) { + editSpeaker->random = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->random ); + } + } else { + editSpeaker->random = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->random ); + } +} + +void CG_SpeakerEditor_VolumeEditFinish( panel_button_t* button ) { + if ( *button->text ) { + editSpeaker->volume = atoi( button->text ); + if ( editSpeaker->volume < 0 ) { + editSpeaker->volume = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->volume ); + } else if ( editSpeaker->volume > 65535 ) { + editSpeaker->volume = 65535; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->volume ); + } + } else { + editSpeaker->volume = 127; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->volume ); + } +} + +void CG_SpeakerEditor_RangeEditFinish( panel_button_t* button ) { + if ( *button->text ) { + editSpeaker->range = atoi( button->text ); + if ( editSpeaker->range < 0 ) { + editSpeaker->range = 0; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->range ); + } + } else { + editSpeaker->range = 1250; + Com_sprintf( (char*)button->text, sizeof( button->text ), "%i", editSpeaker->range ); + } +} + +qboolean CG_SpeakerEditor_Ok_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + BG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Ok_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + BG_PanelButtons_SetFocusButton( NULL ); + + if ( BG_CursorInRect( &button->rect ) ) { + CG_SaveSpeakersToScript(); + + editSpeakerActive = qfalse; + CG_EventHandling( -CGAME_EVENT_SPEAKEREDITOR, qtrue ); + } + return qtrue; + } + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Cancel_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + BG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Cancel_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + BG_PanelButtons_SetFocusButton( NULL ); + + if ( BG_CursorInRect( &button->rect ) ) { + memcpy( editSpeaker, &undoSpeaker, sizeof( *editSpeaker ) ); + undoSpeakerIndex = -2; + editSpeaker = NULL; + editSpeakerActive = qfalse; + CG_EventHandling( -CGAME_EVENT_SPEAKEREDITOR, qtrue ); + } + return qtrue; + } + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Delete_KeyDown( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + BG_PanelButtons_SetFocusButton( button ); + return qtrue; + } + + return qfalse; +} + +qboolean CG_SpeakerEditor_Delete_KeyUp( panel_button_t* button, int key ) { + if ( key == K_MOUSE1 ) { + if ( button == BG_PanelButtons_GetFocusButton() ) { + BG_PanelButtons_SetFocusButton( NULL ); + + if ( BG_CursorInRect( &button->rect ) ) { + undoSpeakerIndex = -1; + BG_SS_DeleteSpeaker( BG_GetIndexForSpeaker( editSpeaker ) ); + CG_SaveSpeakersToScript(); + editSpeaker = NULL; + editSpeakerActive = qfalse; + CG_EventHandling( -CGAME_EVENT_SPEAKEREDITOR, qtrue ); + } + return qtrue; + } + } + + return qfalse; +} + +panel_button_t speakerEditorBack = { + NULL, + NULL, + { 360, 330, 272, 142 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + NULL, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_Back, + NULL, +}; + +panel_button_t speakerEditorLocInfo = { + NULL, + NULL, + { 361, 330 + 9, 272, 10 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_LocInfo, + NULL, +}; + +panel_button_t speakerEditorNoiseLabel = { + NULL, + "Noise:", + { 361, 344 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char noiseEditBuffer[MAX_QPATH]; + +panel_button_t speakerEditorNoiseEdit = { + NULL, + noiseEditBuffer, + { 430, 344, 200, 12 }, + { MAX_QPATH, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_NoiseEdit_KeyDown, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_NoiseEditFinish, +}; + +panel_button_t speakerEditorTargetnameLabel = { + NULL, + "Targetname:", + { 361, 358 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char targetnameEditBuffer[32]; + +panel_button_t speakerEditorTargetnameEdit = { + NULL, + targetnameEditBuffer, + { 430, 358, 200, 12 }, + { 32, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_TargetnameEditFinish, +}; + +panel_button_t speakerEditorLoopedLabel = { + NULL, + "Looped:", + { 361, 372 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t speakerEditorLoopedDropdown = { + NULL, + "no\0on\0off", + { 430, 372, 60, 12 }, + { 3, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_Dropdown_KeyDown, /* keyDown */ + CG_SpeakerEditor_Looped_KeyUp, /* keyUp */ + CG_SpeakerEditor_RenderDropdown, + NULL, +}; + +panel_button_t speakerEditorBroadcastLabel = { + NULL, + "Broadcast:", + { 361, 386 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +panel_button_t speakerEditorBroadcastDropdown = { + NULL, + "no\0global\0nopvs", + { 430, 386, 60, 12 }, + { 3, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_Dropdown_KeyDown, /* keyDown */ + CG_SpeakerEditor_Broadcast_KeyUp, /* keyUp */ + CG_SpeakerEditor_RenderDropdown, + NULL, +}; + +panel_button_t speakerEditorWaitLabel = { + NULL, + "Wait:", + { 361, 400 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char waitEditBuffer[12]; + +panel_button_t speakerEditorWaitEdit = { + NULL, + waitEditBuffer, + { 430, 400, 200, 12 }, + { 12, 2, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_WaitEditFinish, +}; + +panel_button_t speakerEditorRandomLabel = { + NULL, + "Random:", + { 361, 414 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char randomEditBuffer[12]; + +panel_button_t speakerEditorRandomEdit = { + NULL, + randomEditBuffer, + { 430, 414, 200, 12 }, + { 12, 2, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_RandomEditFinish, +}; + +panel_button_t speakerEditorVolumeLabel = { + NULL, + "Volume:", + { 361, 428 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char volumeEditBuffer[12]; + +panel_button_t speakerEditorVolumeEdit = { + NULL, + volumeEditBuffer, + { 430, 428, 200, 12 }, + { 12, 2, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_VolumeEditFinish, +}; + +panel_button_t speakerEditorRangeLabel = { + NULL, + "Range:", + { 361, 442 + 9, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + NULL, /* keyDown */ + NULL, /* keyUp */ + BG_PanelButtonsRender_Text, + NULL, +}; + +char rangeEditBuffer[12]; + +panel_button_t speakerEditorRangeEdit = { + NULL, + rangeEditBuffer, + { 430, 442, 200, 12 }, + { 12, 2, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + BG_PanelButton_EditClick, /* keyDown */ + NULL, /* keyUp */ + CG_SpeakerEditor_RenderEdit, + CG_SpeakerEditor_RangeEditFinish, +}; + +panel_button_t speakerEditorOkButton = { + NULL, + "Ok", + { 376, 458, 70, 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_Ok_KeyDown, /* keyDown */ + CG_SpeakerEditor_Ok_KeyUp, /* keyUp */ + CG_SpeakerEditor_RenderButton, + NULL, +}; + +panel_button_t speakerEditorCancelButton = { + NULL, + "Cancel", + { 461, 458, 70, 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_Cancel_KeyDown, /* keyDown */ + CG_SpeakerEditor_Cancel_KeyUp, /* keyUp */ + CG_SpeakerEditor_RenderButton, + NULL, +}; + +panel_button_t speakerEditorDeleteButton = { + NULL, + "Delete", + { 546, 458, 70, 12 }, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + &speakerEditorTxt, /* font */ + CG_SpeakerEditor_Delete_KeyDown, /* keyDown */ + CG_SpeakerEditor_Delete_KeyUp, /* keyUp */ + CG_SpeakerEditor_RenderButton, + NULL, +}; + +static panel_button_t *speakerEditorButtons[] = { + &speakerEditorBack, + &speakerEditorLocInfo, + &speakerEditorNoiseLabel, + &speakerEditorNoiseEdit, + &speakerEditorTargetnameLabel, + &speakerEditorTargetnameEdit, + &speakerEditorLoopedLabel, + &speakerEditorBroadcastLabel, + &speakerEditorWaitLabel, + &speakerEditorWaitEdit, + &speakerEditorRandomLabel, + &speakerEditorRandomEdit, + &speakerEditorVolumeLabel, + &speakerEditorVolumeEdit, + &speakerEditorRangeLabel, + &speakerEditorRangeEdit, + &speakerEditorOkButton, + &speakerEditorCancelButton, + &speakerEditorDeleteButton, + + // Below here all components that should draw on top + &speakerEditorBroadcastDropdown, + &speakerEditorLoopedDropdown, + NULL +}; + +void CG_SpeakerEditorDraw( void ) { + if ( !cg.editingSpeakers ) { + return; + } + + if ( !editSpeakerActive ) { + int bindingkey[2]; + char binding[2][32]; + vec4_t colour; + float x, y, w, h; + + VectorCopy( colorWhite, colour ); + colour[3] = .8f; + + if ( undoSpeakerIndex == -2 ) { + y = 452; + } else { + y = 442; + } + + CG_Text_Paint_Ext( 8, y, .2f, .2f, colour, + va( "Current amount of speakers in map: %i (inpvs: %i max in map: %i)", + BG_NumScriptSpeakers(), + numSpeakersInPvs, + 256 ), + 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + + trap_Key_KeysForBinding( "dumpspeaker", &bindingkey[0], &bindingkey[1] ); + trap_Key_KeynumToStringBuf( bindingkey[0], binding[0], sizeof( binding[0] ) ); + trap_Key_KeynumToStringBuf( bindingkey[1], binding[1], sizeof( binding[1] ) ); + Q_strupr( binding[0] ); + Q_strupr( binding[1] ); + CG_Text_Paint_Ext( 8, y + 10, .2f, .2f, colour, + va( "Create new speaker: %s%s", bindingkey[0] != -1 ? binding[0] : "???", + bindingkey[1] != -1 ? va( " or %s", binding[1] ) : "" ), + 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + + trap_Key_KeysForBinding( "modifyspeaker", &bindingkey[0], &bindingkey[1] ); + trap_Key_KeynumToStringBuf( bindingkey[0], binding[0], sizeof( binding[0] ) ); + trap_Key_KeynumToStringBuf( bindingkey[1], binding[1], sizeof( binding[1] ) ); + Q_strupr( binding[0] ); + Q_strupr( binding[1] ); + CG_Text_Paint_Ext( 8, y + 20, .2f, .2f, colour, + va( "Modify target speaker: %s%s", bindingkey[0] != -1 ? binding[0] : "???", + bindingkey[1] != -1 ? va( " or %s", binding[1] ) : "" ), + 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + + if ( undoSpeakerIndex != -2 ) { + trap_Key_KeysForBinding( "undospeaker", &bindingkey[0], &bindingkey[1] ); + trap_Key_KeynumToStringBuf( bindingkey[0], binding[0], sizeof( binding[0] ) ); + trap_Key_KeynumToStringBuf( bindingkey[1], binding[1], sizeof( binding[1] ) ); + Q_strupr( binding[0] ); + Q_strupr( binding[1] ); + CG_Text_Paint_Ext( 8, y + 30, .2f, .2f, colour, + va( "Undo %s speaker: %s%s", undoSpeakerIndex == -1 ? "remove" : "modify", + bindingkey[0] != -1 ? binding[0] : "???", + bindingkey[1] != -1 ? va( " or %s", binding[1] ) : "" ), + 0, 0, ITEM_TEXTSTYLE_SHADOWED, &cgs.media.limboFont2 ); + } + + // render crosshair + + x = cg_crosshairX.integer; + y = cg_crosshairY.integer; + w = h = cg_crosshairSize.value; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), + y + 0.5 * ( cg.refdef_current->height - h ), + w, h, 0, 0, 1, 1, cgs.media.crosshairShader[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ); + + if ( cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ) { + trap_R_DrawStretchPic( x + 0.5 * ( cg.refdef_current->width - w ), + y + 0.5 * ( cg.refdef_current->height - h ), + w, h, 0, 0, 1, 1, cg.crosshairShaderAlt[ cg_drawCrosshair.integer % NUM_CROSSHAIRS ] ); + } + + + if ( editSpeaker ) { + // render interface + BG_PanelButtonsRender( speakerInfoButtons ); + } + + } else { + // render interface + BG_PanelButtonsRender( speakerEditorButtons ); + + // render cursor + trap_R_SetColor( NULL ); + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + } +} + +void CG_SpeakerEditor_KeyHandling( int key, qboolean down ) { + if ( !BG_PanelButtonsKeyEvent( key, down, speakerEditorButtons ) ) { + switch ( key ) { + case K_MOUSE1: if ( !down ) { + editSpeakerHandle.activeAxis = -1; + break; + } else if ( editSpeakerHandle.activeAxis == -1 ) { + int i, closest; + float dist, minDist, r, u; + vec3_t vec, axisOrg, dir; + + closest = -1; + minDist = Square( 16.f ); + + r = -( cg.refdef_current->fov_x / 90.f ) * (float)( cgs.cursorX - 320 ) / 320; + u = -( cg.refdef_current->fov_y / 90.f ) * (float)( cgs.cursorY - 240 ) / 240; + + for ( i = 0; i < 3; i++ ) { + dir[i] = cg.refdef_current->viewaxis[0][i] * 1.f + + cg.refdef_current->viewaxis[1][i] * r + + cg.refdef_current->viewaxis[2][i] * u; + } + VectorNormalizeFast( dir ); + + for ( i = 0; i < 3; i++ ) { + VectorClear( vec ); + vec[i] = 1.f; + VectorMA( editSpeakerHandle.origin, 32, vec, axisOrg ); + + // see which one is closest to our cursor + VectorSubtract( axisOrg, cg.refdef_current->vieworg, vec ); + dist = DotProduct( vec, dir ); + VectorMA( cg.refdef_current->vieworg, dist, dir, vec ); + dist = DistanceSquared( axisOrg, vec ); + if ( dist <= minDist ) { + minDist = dist; + closest = i; + } + } + + editSpeakerHandle.activeAxis = closest; + + if ( editSpeakerHandle.activeAxis >= 0 ) { + VectorCopy( editSpeakerHandle.origin, editSpeakerHandle.oldOrigin ); + } + ; + } + break; + case K_ESCAPE: BG_PanelButtons_SetFocusButton( NULL ); + CG_SaveSpeakersToScript(); + editSpeakerActive = qfalse; + CG_EventHandling( -CGAME_EVENT_SPEAKEREDITOR, qtrue ); + break; + } + } +} + +void CG_SpeakerEditorMouseMove_Handling( int x, int y ) { + if ( !cg.editingSpeakers ) { + return; + } + + if ( editSpeakerActive ) { + if ( editSpeakerHandle.activeAxis >= 0 ) { + if ( editSpeakerHandle.activeAxis == 0 ) { + // this one and the next one are quite nasty, so do it the hacky way + if ( cgs.cursorX - x < 320 ) { + editSpeaker->origin[0] -= x; + } else { + editSpeaker->origin[0] += x; + } + } else if ( editSpeakerHandle.activeAxis == 1 ) { + if ( cgs.cursorX - x < 320 ) { + editSpeaker->origin[1] -= x; + } else { + editSpeaker->origin[1] += x; + } + } else if ( editSpeakerHandle.activeAxis == 2 ) { + // but this one is easy + editSpeaker->origin[2] -= y; + } + + cgs.cursorX -= x; + cgs.cursorY -= y; + + VectorCopy( editSpeakerHandle.origin, editSpeakerHandle.oldOrigin ); + VectorCopy( editSpeaker->origin, editSpeakerHandle.origin ); + } + } +} + +void CG_ActivateEditSoundMode( void ) { + CG_Printf( "Activating Speaker Edit mode.\n" ); + cg.editingSpeakers = qtrue; + + editSpeaker = NULL; + editSpeakerActive = qfalse; + editSpeakerHandle.activeAxis = -1; + undoSpeakerIndex = -2; + + if ( !speakerShader ) { + speakerShader = trap_R_RegisterShader( "gfx/misc/speaker" ); + speakerShaderGrayScale = trap_R_RegisterShader( "gfx/misc/speaker_gs" ); + + BG_PanelButtonsSetup( speakerInfoButtons ); + BG_PanelButtonsSetup( speakerEditorButtons ); + } +} + +void CG_DeActivateEditSoundMode( void ) { + CG_Printf( "De-activating Speaker Edit mode.\n" ); + cg.editingSpeakers = qfalse; + + if ( editSpeakerActive ) { + CG_EventHandling( -CGAME_EVENT_SPEAKEREDITOR, qtrue ); + } + + editSpeaker = NULL; + editSpeakerActive = qfalse; + editSpeakerHandle.activeAxis = -1; + undoSpeakerIndex = -2; +} + +void CG_ModifyEditSpeaker( void ) { + if ( !editSpeaker || editSpeakerActive ) { + return; + } + + CG_EventHandling( CGAME_EVENT_SPEAKEREDITOR, qfalse ); + + editSpeakerActive = qtrue; + memcpy( &undoSpeaker, editSpeaker, sizeof( undoSpeaker ) ); + undoSpeakerIndex = BG_GetIndexForSpeaker( editSpeaker ); + + VectorCopy( editSpeaker->origin, editSpeakerHandle.origin ); + VectorCopy( editSpeaker->origin, editSpeakerHandle.oldOrigin ); + + Q_strncpyz( noiseEditBuffer, editSpeaker->filename, sizeof( noiseEditBuffer ) ); + Q_strncpyz( targetnameEditBuffer, editSpeaker->targetname, sizeof( targetnameEditBuffer ) ); + speakerEditorLoopedDropdown.data[1] = editSpeaker->loop; + speakerEditorBroadcastDropdown.data[1] = editSpeaker->broadcast; + Com_sprintf( waitEditBuffer, sizeof( waitEditBuffer ), "%i", editSpeaker->wait ); + Com_sprintf( randomEditBuffer, sizeof( randomEditBuffer ), "%i", editSpeaker->random ); + Com_sprintf( volumeEditBuffer, sizeof( volumeEditBuffer ), "%i", editSpeaker->volume ); + Com_sprintf( rangeEditBuffer, sizeof( rangeEditBuffer ), "%i", editSpeaker->range ); +} + +void CG_UndoEditSpeaker( void ) { + if ( undoSpeakerIndex == -2 ) { + return; + } + + if ( undoSpeakerIndex == -1 ) { + if ( !BG_SS_StoreSpeaker( &undoSpeaker ) ) { + CG_Printf( S_COLOR_YELLOW "UNDO: restoring deleted speaker failed, no storage memory for speaker\n" ); + } else { + CG_Printf( "UNDO: restored deleted speaker at %.2f %.2f %.2f.\n", undoSpeaker.origin[0], undoSpeaker.origin[1], undoSpeaker.origin[2] ); + } + } else { + bg_speaker_t *speaker = BG_GetScriptSpeaker( undoSpeakerIndex ); + memcpy( speaker, &undoSpeaker, sizeof( *speaker ) ); + CG_Printf( "UNDO: restoring modified settings of speaker at %.2f %.2f %.2f.\n", undoSpeaker.origin[0], undoSpeaker.origin[1], undoSpeaker.origin[2] ); + } + + CG_SaveSpeakersToScript(); + undoSpeakerIndex = -2; +} + +// Normal Use + +void CG_ToggleActiveOnScriptSpeaker( int index ) { + bg_speaker_t *speaker = BG_GetScriptSpeaker( index ); + + if ( speaker ) { + speaker->activated = !speaker->activated; + } +} + +void CG_UnsetActiveOnScriptSpeaker( int index ) { + bg_speaker_t *speaker = BG_GetScriptSpeaker( index ); + + if ( speaker ) { + speaker->activated = qfalse; + } +} + +void CG_SetActiveOnScriptSpeaker( int index ) { + bg_speaker_t *speaker = BG_GetScriptSpeaker( index ); + + if ( speaker ) { + speaker->activated = qtrue; + } +} + +static void CG_PlayScriptSpeaker( bg_speaker_t *speaker, qboolean global ) { + switch ( speaker->loop ) { + case S_LT_NOT_LOOPED: if ( global ) { + trap_S_StartLocalSound( speaker->noise, CHAN_ITEM ); + } else { + trap_S_StartSoundVControl( speaker->origin, -1, CHAN_ITEM, speaker->noise, speaker->volume ); + } + break; + case S_LT_LOOPED_ON: + case S_LT_LOOPED_OFF: if ( speaker->soundTime == 0 ) { + speaker->soundTime = trap_S_GetCurrentSoundTime(); + } + trap_S_AddRealLoopingSound( speaker->origin, vec3_origin, speaker->noise, speaker->range, speaker->volume, speaker->soundTime ); + break; + } +} + +void CG_AddScriptSpeakers( void ) { + int i; + bg_speaker_t *speaker; + + if ( cg.editingSpeakers ) { + CG_RenderScriptSpeakers(); + } + + for ( i = 0; i < BG_NumScriptSpeakers(); i++ ) { + speaker = BG_GetScriptSpeaker( i ); + + // don't bother playing missing sounds + if ( !speaker->noise ) { + continue; + } + + // activate if needed + if ( speaker->loop == S_LT_NOT_LOOPED ) { + if ( cg.time >= speaker->nextActivateTime && ( speaker->wait || speaker->random ) ) { + speaker->activated = qtrue; + speaker->nextActivateTime = cg.time + speaker->wait + speaker->random * crandom(); + } + } + + if ( !speaker->activated ) { + speaker->soundTime = 0; + continue; + } + + switch ( speaker->broadcast ) { + case S_BT_LOCAL: if ( trap_R_inPVS( cg.refdef_current->vieworg, speaker->origin ) ) { + CG_PlayScriptSpeaker( speaker, qfalse ); + } + break; + case S_BT_GLOBAL: CG_PlayScriptSpeaker( speaker, qtrue ); + break; + case S_BT_NOPVS: CG_PlayScriptSpeaker( speaker, qfalse ); + break; + } + + if ( speaker->loop == S_LT_NOT_LOOPED ) { + speaker->activated = qfalse; + } + } +} diff --git a/src/cgame/cg_spawn.c b/src/cgame/cg_spawn.c new file mode 100644 index 0000000..5cbe4e1 --- /dev/null +++ b/src/cgame/cg_spawn.c @@ -0,0 +1,509 @@ +/* +=========================================================================== + +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: cg_spawn.c + * + * desc: Client sided only map entities +*/ + +#include "cg_local.h" + +qboolean CG_SpawnString( const char *key, const char *defaultString, char **out ) { + int i; + + if ( !cg.spawning ) { + *out = (char *)defaultString; + CG_Error( "CG_SpawnString() called while not spawning" ); + } + + for ( i = 0 ; i < cg.numSpawnVars ; i++ ) { + if ( !strcmp( key, cg.spawnVars[i][0] ) ) { + *out = cg.spawnVars[i][1]; + return qtrue; + } + } + + *out = (char *)defaultString; + return qfalse; +} + +qboolean CG_SpawnFloat( const char *key, const char *defaultString, float *out ) { + char *s; + qboolean present; + + present = CG_SpawnString( key, defaultString, &s ); + *out = atof( s ); + return present; +} + +qboolean CG_SpawnInt( const char *key, const char *defaultString, int *out ) { + char *s; + qboolean present; + + present = CG_SpawnString( key, defaultString, &s ); + *out = atoi( s ); + return present; +} + +qboolean CG_SpawnVector( const char *key, const char *defaultString, float *out ) { + char *s; + qboolean present; + + present = CG_SpawnString( key, defaultString, &s ); + sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] ); + return present; +} + +qboolean CG_SpawnVector2D( const char *key, const char *defaultString, float *out ) { + char *s; + qboolean present; + + present = CG_SpawnString( key, defaultString, &s ); + sscanf( s, "%f %f", &out[0], &out[1] ); + return present; +} + +/* +============= +VectorToString + +This is just a convenience function +for printing vectors +============= +*/ +char *vtos( const vec3_t v ) { + static int index; + static char str[8][32]; + char *s; + + // use an array so that multiple vtos won't collide + s = str[index]; + index = ( index + 1 ) & 7; + + Com_sprintf( s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2] ); + + return s; +} + +void SP_path_corner_2( void ) { + char* targetname; + vec3_t origin; + + CG_SpawnString( "targetname", "", &targetname ); + CG_SpawnVector( "origin", "0 0 0", origin ); + + if ( !*targetname ) { + CG_Error( "path_corner_2 with no targetname at %s\n", vtos( origin ) ); + return; + } + + if ( numPathCorners >= MAX_PATH_CORNERS ) { + CG_Error( "Maximum path_corners hit\n" ); + return; + } + + BG_AddPathCorner( targetname, origin ); +} + +void SP_info_train_spline_main( void ) { + char* targetname; + char* target; + char* control; + vec3_t origin; + int i; + char* end; + splinePath_t* spline; + + if ( !CG_SpawnVector( "origin", "0 0 0", origin ) ) { + CG_Error( "info_train_spline_main with no origin\n" ); + } + + if ( !CG_SpawnString( "targetname", "", &targetname ) ) { + CG_Error( "info_train_spline_main with no targetname at %s\n", vtos( origin ) ); + } + + CG_SpawnString( "target", "", &target ); + + spline = BG_AddSplinePath( targetname, target, origin ); + + if ( CG_SpawnString( "end", "", &end ) ) { + spline->isEnd = qtrue; + } else if ( CG_SpawnString( "start", "", &end ) ) { + spline->isStart = qtrue; + } + + for ( i = 1;; i++ ) { + if ( !CG_SpawnString( i == 1 ? va( "control" ) : va( "control%i", i ), "", &control ) ) { + break; + } + + BG_AddSplineControl( spline, control ); + } +} + +void SP_misc_gamemodel( void ) { + char* model; + vec_t angle; + vec3_t angles; + + vec_t scale; + vec3_t vScale; + + vec3_t org; + + cg_gamemodel_t* gamemodel; + + int i; + + if ( CG_SpawnString( "targetname", "", &model ) || CG_SpawnString( "scriptname", "", &model ) || CG_SpawnString( "spawnflags", "", &model ) ) { + // Gordon: this model may not be static, so let the server handle it + return; + } + + if ( cg.numMiscGameModels >= MAX_STATIC_GAMEMODELS ) { + CG_Error( "^1MAX_STATIC_GAMEMODELS(%i) hit", MAX_STATIC_GAMEMODELS ); + } + + CG_SpawnString( "model", "", &model ); + + CG_SpawnVector( "origin", "0 0 0", org ); + + if ( !CG_SpawnVector( "angles", "0 0 0", angles ) ) { + if ( CG_SpawnFloat( "angle", "0", &angle ) ) { + angles[YAW] = angle; + } + } + + if ( !CG_SpawnVector( "modelscale_vec", "1 1 1", vScale ) ) { + if ( CG_SpawnFloat( "modelscale", "1", &scale ) ) { + VectorSet( vScale, scale, scale, scale ); + } + } + + gamemodel = &cgs.miscGameModels[cg.numMiscGameModels++]; + gamemodel->model = trap_R_RegisterModel( model ); + AnglesToAxis( angles, gamemodel->axes ); + for ( i = 0; i < 3; i++ ) { + VectorScale( gamemodel->axes[i], vScale[i], gamemodel->axes[i] ); + } + VectorCopy( org, gamemodel->org ); + + if ( gamemodel->model ) { + vec3_t mins, maxs; + + trap_R_ModelBounds( gamemodel->model, mins, maxs ); + + for ( i = 0; i < 3; i++ ) { + mins[i] *= vScale[i]; + maxs[i] *= vScale[i]; + } + + gamemodel->radius = RadiusFromBounds( mins, maxs ); + } else { + gamemodel->radius = 0; + } +} + +void SP_trigger_objective_info( void ) { + char* temp; + + CG_SpawnString( "infoAllied", "^1No Text Supplied", &temp ); + Q_strncpyz( cg.oidTriggerInfoAllies[cg.numOIDtriggers2], temp, 256 ); + + CG_SpawnString( "infoAxis", "^1No Text Supplied", &temp ); + Q_strncpyz( cg.oidTriggerInfoAxis[cg.numOIDtriggers2], temp, 256 ); + + cg.numOIDtriggers2++; +} + +typedef struct { + char *name; + void ( *spawn )( void ); +} spawn_t; + +spawn_t spawns[] = { + {0, 0}, + {"path_corner_2", SP_path_corner_2}, + {"info_train_spline_main", SP_info_train_spline_main}, + {"info_train_spline_control", SP_path_corner_2}, + + {"trigger_objective_info", SP_trigger_objective_info}, + {"misc_gamemodel", SP_misc_gamemodel}, +}; + +#define NUMSPAWNS ( sizeof( spawns ) / sizeof( spawn_t ) ) + +/* +=================== +CG_ParseEntityFromSpawnVars + +Spawn an entity and fill in all of the level fields from +cg.spawnVars[], then call the class specfic spawn function +=================== +*/ +void CG_ParseEntityFromSpawnVars( void ) { + int i; + char *classname; + + // check for "notteam" / "notfree" flags + CG_SpawnInt( "notteam", "0", &i ); + if ( i ) { + return; + } + + if ( CG_SpawnString( "classname", "", &classname ) ) { + for ( i = 0; i < NUMSPAWNS; i++ ) { + if ( !Q_stricmp( spawns[i].name, classname ) ) { + spawns[i].spawn(); + break; + } + } + } + +} + +/* +==================== +CG_AddSpawnVarToken +==================== +*/ +char *CG_AddSpawnVarToken( const char *string ) { + int l; + char *dest; + + l = strlen( string ); + if ( cg.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS ) { + CG_Error( "CG_AddSpawnVarToken: MAX_SPAWN_VARS" ); + } + + dest = cg.spawnVarChars + cg.numSpawnVarChars; + memcpy( dest, string, l + 1 ); + + cg.numSpawnVarChars += l + 1; + + return dest; +} + +/* +==================== +CG_ParseSpawnVars + +Parses a brace bounded set of key / value pairs out of the +level's entity strings into cg.spawnVars[] + +This does not actually spawn an entity. +==================== +*/ +qboolean CG_ParseSpawnVars( void ) { + char keyname[MAX_TOKEN_CHARS]; + char com_token[MAX_TOKEN_CHARS]; + + cg.numSpawnVars = 0; + cg.numSpawnVarChars = 0; + + // parse the opening brace + if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { + // end of spawn string + return qfalse; + } + if ( com_token[0] != '{' ) { + CG_Error( "CG_ParseSpawnVars: found %s when expecting {",com_token ); + } + + // go through all the key / value pairs + while ( 1 ) { + // parse key + if ( !trap_GetEntityToken( keyname, sizeof( keyname ) ) ) { + CG_Error( "CG_ParseSpawnVars: EOF without closing brace" ); + } + + if ( keyname[0] == '}' ) { + break; + } + + // parse value + if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { + CG_Error( "CG_ParseSpawnVars: EOF without closing brace" ); + } + + if ( com_token[0] == '}' ) { + CG_Error( "CG_ParseSpawnVars: closing brace without data" ); + } + if ( cg.numSpawnVars == MAX_SPAWN_VARS ) { + CG_Error( "CG_ParseSpawnVars: MAX_SPAWN_VARS" ); + } + cg.spawnVars[ cg.numSpawnVars ][0] = CG_AddSpawnVarToken( keyname ); + cg.spawnVars[ cg.numSpawnVars ][1] = CG_AddSpawnVarToken( com_token ); + cg.numSpawnVars++; + } + + return qtrue; +} + +void SP_worldspawn( void ) { + char *s; + int i; + + CG_SpawnString( "classname", "", &s ); + if ( Q_stricmp( s, "worldspawn" ) ) { + CG_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); + } + + cgs.ccLayers = 0; + + if ( CG_SpawnVector2D( "mapcoordsmins", "-128 128", cg.mapcoordsMins ) && // top left + CG_SpawnVector2D( "mapcoordsmaxs", "128 -128", cg.mapcoordsMaxs ) ) { // bottom right + cg.mapcoordsValid = qtrue; + } else { + cg.mapcoordsValid = qfalse; + } + + CG_ParseSpawns(); + + CG_SpawnString( "cclayers", "0", &s ); + cgs.ccLayers = atoi( s ); + + for ( i = 0; i < cgs.ccLayers; i++ ) { + CG_SpawnString( va( "cclayerceil%i",i ), "0", &s ); + cgs.ccLayerCeils[i] = atoi( s ); + } + + cg.mapcoordsScale[0] = 1 / ( cg.mapcoordsMaxs[0] - cg.mapcoordsMins[0] ); + cg.mapcoordsScale[1] = 1 / ( cg.mapcoordsMaxs[1] - cg.mapcoordsMins[1] ); + + BG_InitLocations( cg.mapcoordsMins, cg.mapcoordsMaxs ); + + CG_SpawnString( "atmosphere", "", &s ); + CG_EffectParse( s ); + + cg.fiveMinuteSound_g[0] = \ + cg.fiveMinuteSound_a[0] = \ + cg.twoMinuteSound_g[0] = \ + cg.twoMinuteSound_a[0] = \ + cg.thirtySecondSound_g[0] = \ + cg.thirtySecondSound_a[0] = '\0'; + + CG_SpawnString( "twoMinuteSound_axis", "axis_hq_5minutes", &s ); + Q_strncpyz( cg.fiveMinuteSound_g, s, sizeof( cg.fiveMinuteSound_g ) ); + CG_SpawnString( "twoMinuteSound_allied", "allies_hq_5minutes", &s ); + Q_strncpyz( cg.fiveMinuteSound_a, s, sizeof( cg.fiveMinuteSound_a ) ); + + CG_SpawnString( "twoMinuteSound_axis", "axis_hq_2minutes", &s ); + Q_strncpyz( cg.twoMinuteSound_g, s, sizeof( cg.twoMinuteSound_g ) ); + CG_SpawnString( "twoMinuteSound_allied", "allies_hq_2minutes", &s ); + Q_strncpyz( cg.twoMinuteSound_a, s, sizeof( cg.twoMinuteSound_a ) ); + + CG_SpawnString( "thirtySecondSound_axis", "axis_hq_30seconds", &s ); + Q_strncpyz( cg.thirtySecondSound_g, s, sizeof( cg.thirtySecondSound_g ) ); + CG_SpawnString( "thirtySecondSound_allied", "allies_hq_30seconds", &s ); + Q_strncpyz( cg.thirtySecondSound_a, s, sizeof( cg.thirtySecondSound_a ) ); + + // 5 minute axis + if ( !*cg.fiveMinuteSound_g ) { + cgs.media.fiveMinuteSound_g = 0; + } else if ( strstr( cg.fiveMinuteSound_g, ".wav" ) ) { + cgs.media.fiveMinuteSound_g = trap_S_RegisterSound( cg.fiveMinuteSound_g, qtrue ); + } else { + cgs.media.fiveMinuteSound_g = -1; + } + + // 5 minute allied + if ( !*cg.fiveMinuteSound_a ) { + cgs.media.fiveMinuteSound_a = 0; + } else if ( strstr( cg.fiveMinuteSound_a, ".wav" ) ) { + cgs.media.fiveMinuteSound_a = trap_S_RegisterSound( cg.fiveMinuteSound_a, qtrue ); + } else { + cgs.media.fiveMinuteSound_a = -1; + } + + // 2 minute axis + if ( !*cg.twoMinuteSound_g ) { + cgs.media.twoMinuteSound_g = 0; + } else if ( strstr( cg.twoMinuteSound_g, ".wav" ) ) { + cgs.media.twoMinuteSound_g = trap_S_RegisterSound( cg.twoMinuteSound_g, qtrue ); + } else { + cgs.media.twoMinuteSound_g = -1; + } + + // 2 minute allied + if ( !*cg.twoMinuteSound_a ) { + cgs.media.twoMinuteSound_a = 0; + } else if ( strstr( cg.twoMinuteSound_a, ".wav" ) ) { + cgs.media.twoMinuteSound_a = trap_S_RegisterSound( cg.twoMinuteSound_a, qtrue ); + } else { + cgs.media.twoMinuteSound_a = -1; + } + + // 30 seconds axis + if ( !*cg.thirtySecondSound_g ) { + cgs.media.thirtySecondSound_g = 0; + } else if ( strstr( cg.thirtySecondSound_g, ".wav" ) ) { + cgs.media.thirtySecondSound_g = trap_S_RegisterSound( cg.thirtySecondSound_g, qtrue ); + } else { + cgs.media.thirtySecondSound_g = -1; + } + + // 30 seconds allied + if ( !*cg.thirtySecondSound_a ) { + cgs.media.thirtySecondSound_a = 0; + } else if ( strstr( cg.thirtySecondSound_a, ".wav" ) ) { + cgs.media.thirtySecondSound_a = trap_S_RegisterSound( cg.thirtySecondSound_a, qtrue ); + } else { + cgs.media.thirtySecondSound_a = -1; + } +} + +/* +============== +CG_ParseEntitiesFromString + +Parses textual entity definitions out of an entstring and spawns gentities. +============== +*/ +void CG_ParseEntitiesFromString( void ) { + // allow calls to CG_Spawn*() + cg.spawning = qtrue; + cg.numSpawnVars = 0; + cg.numMiscGameModels = 0; + + // the worldspawn is not an actual entity, but it still + // has a "spawn" function to perform any global setup + // needed by a level (setting configstrings or cvars, etc) + if ( !CG_ParseSpawnVars() ) { + CG_Error( "ParseEntities: no entities" ); + } + SP_worldspawn(); + + // parse ents + while ( CG_ParseSpawnVars() ) { + CG_ParseEntityFromSpawnVars(); + } + + cg.spawning = qfalse; // any future calls to CG_Spawn*() will be errors +} diff --git a/src/cgame/cg_statsranksmedals.c b/src/cgame/cg_statsranksmedals.c new file mode 100644 index 0000000..32ef9f6 --- /dev/null +++ b/src/cgame/cg_statsranksmedals.c @@ -0,0 +1,85 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "cg_local.h" + +rankicon_t rankicons[NUM_EXPERIENCE_LEVELS][2] = { + { + { 0, "gfx/hud/ranks/rank1", 128, 128 }, + { 0, "models/players/temperate/common/rank1", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank2", 128, 128 }, + { 0, "models/players/temperate/common/rank2", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank3", 128, 128 }, + { 0, "models/players/temperate/common/rank3", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank4", 128, 128 }, + { 0, "models/players/temperate/common/rank4", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank5", 128, 128 }, + { 0, "models/players/temperate/common/rank5", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank6", 128, 128 }, + { 0, "models/players/temperate/common/rank6", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank7", 128, 128 }, + { 0, "models/players/temperate/common/rank7", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank8", 128, 128 }, + { 0, "models/players/temperate/common/rank8", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank9", 128, 128 }, + { 0, "models/players/temperate/common/rank9", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank10", 128, 128 }, + { 0, "models/players/temperate/common/rank10", 128, 128 } + }, + { + { 0, "gfx/hud/ranks/rank11", 128, 128 }, + { 0, "models/players/temperate/common/rank11", 128, 128 } + }, +}; + +void CG_LoadRankIcons( void ) { + int i; + + for ( i = 1; i < NUM_EXPERIENCE_LEVELS; i++ ) { + rankicons[i][0].shader = trap_R_RegisterShaderNoMip( rankicons[i][0].iconname ); + rankicons[i][1].shader = trap_R_RegisterShaderNoMip( rankicons[i][1].iconname ); + } +} diff --git a/src/cgame/cg_syscalls.c b/src/cgame/cg_syscalls.c new file mode 100644 index 0000000..1057277 --- /dev/null +++ b/src/cgame/cg_syscalls.c @@ -0,0 +1,891 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_syscalls.c -- this file is only included when building a dll +// cg_syscalls.asm is included instead when building a qvm +#include "cg_local.h" + +static int ( QDECL * syscall )( int arg, ... ) = ( int ( QDECL * )( int, ... ) ) - 1; + +#if __GNUC__ >= 4 +#pragma GCC visibility push(default) +#endif +void dllEntry( int ( QDECL *syscallptr )( int arg,... ) ) { + syscall = syscallptr; +} +#if __GNUC__ >= 4 +#pragma GCC visibility pop +#endif + +/*int PASSFLOAT( float x ) { + float floatTemp; + floatTemp = x; + return *(int *)&floatTemp; +}*/ + + +#define PASSFLOAT( x ) ( *(int*)&x ) + +void trap_PumpEventLoop( void ) { + if ( !cgs.initing ) { + return; + } + syscall( CG_PUMPEVENTLOOP ); +} + + +void trap_Print( const char *fmt ) { + syscall( CG_PRINT, fmt ); +} + +void trap_Error( const char *fmt ) { + syscall( CG_ERROR, fmt ); +} + +int trap_Milliseconds( void ) { + return syscall( CG_MILLISECONDS ); +} + +void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) { + syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags ); +} + +void trap_Cvar_Update( vmCvar_t *vmCvar ) { + syscall( CG_CVAR_UPDATE, vmCvar ); +} + +void trap_Cvar_Set( const char *var_name, const char *value ) { + syscall( CG_CVAR_SET, var_name, value ); +} + +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { + syscall( CG_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize ); +} + +void trap_Cvar_LatchedVariableStringBuffer( const char *var_name, char *buffer, int bufsize ) { + syscall( CG_CVAR_LATCHEDVARIABLESTRINGBUFFER, var_name, buffer, bufsize ); +} + +int trap_Argc( void ) { + return syscall( CG_ARGC ); +} + +void trap_Argv( int n, char *buffer, int bufferLength ) { + syscall( CG_ARGV, n, buffer, bufferLength ); +} + +void trap_Args( char *buffer, int bufferLength ) { + syscall( CG_ARGS, buffer, bufferLength ); +} + +int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) { + return syscall( CG_FS_FOPENFILE, qpath, f, mode ); +} + +void trap_FS_Read( void *buffer, int len, fileHandle_t f ) { + syscall( CG_FS_READ, buffer, len, f ); +} + +void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) { + syscall( CG_FS_WRITE, buffer, len, f ); +} + +void trap_FS_FCloseFile( fileHandle_t f ) { + syscall( CG_FS_FCLOSEFILE, f ); +} + +int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize ) { + return syscall( CG_FS_GETFILELIST, path, extension, listbuf, bufsize ); +} + +int trap_FS_Delete( const char *filename ) { + return syscall( CG_FS_DELETEFILE, filename ); +} + +void trap_SendConsoleCommand( const char *text ) { + syscall( CG_SENDCONSOLECOMMAND, text ); +} + +void trap_AddCommand( const char *cmdName ) { + syscall( CG_ADDCOMMAND, cmdName ); +} + +void trap_SendClientCommand( const char *s ) { + syscall( CG_SENDCLIENTCOMMAND, s ); +} + +void trap_UpdateScreen( void ) { + syscall( CG_UPDATESCREEN ); +} + +/*void trap_CM_LoadMap( const char *mapname ) { + CG_DrawInformation(); + syscall( CG_CM_LOADMAP, mapname ); +}*/ + +int trap_CM_NumInlineModels( void ) { + return syscall( CG_CM_NUMINLINEMODELS ); +} + +clipHandle_t trap_CM_InlineModel( int index ) { + return syscall( CG_CM_INLINEMODEL, index ); +} + +clipHandle_t trap_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) { + return syscall( CG_CM_TEMPBOXMODEL, mins, maxs ); +} + +clipHandle_t trap_CM_TempCapsuleModel( const vec3_t mins, const vec3_t maxs ) { + return syscall( CG_CM_TEMPCAPSULEMODEL, mins, maxs ); +} + +int trap_CM_PointContents( const vec3_t p, clipHandle_t model ) { + return syscall( CG_CM_POINTCONTENTS, p, model ); +} + +int trap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) { + return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles ); +} + +void trap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask ) { + syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask ); +} + +void trap_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask, + const vec3_t origin, const vec3_t angles ) { + syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); +} + +void trap_CM_CapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask ) { + syscall( CG_CM_CAPSULETRACE, results, start, end, mins, maxs, model, brushmask ); +} + +void trap_CM_TransformedCapsuleTrace( trace_t *results, const vec3_t start, const vec3_t end, + const vec3_t mins, const vec3_t maxs, + clipHandle_t model, int brushmask, + const vec3_t origin, const vec3_t angles ) { + syscall( CG_CM_TRANSFORMEDCAPSULETRACE, results, start, end, mins, maxs, model, brushmask, origin, angles ); +} + +int trap_CM_MarkFragments( int numPoints, const vec3_t *points, + const vec3_t projection, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer ) { + return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer ); +} + +// ydnar +void trap_R_ProjectDecal( qhandle_t hShader, int numPoints, vec3_t *points, vec4_t projection, vec4_t color, int lifeTime, int fadeTime ) { + syscall( CG_R_PROJECTDECAL, hShader, numPoints, points, projection, color, lifeTime, fadeTime ); +} + +void trap_R_ClearDecals( void ) { + syscall( CG_R_CLEARDECALS ); +} + + +void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) { + syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx, 127 /* Gordon: default volume always for the moment*/ ); +} + +void trap_S_StartSoundVControl( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int volume ) { + syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx, volume ); +} + +//----(SA) added +void trap_S_StartSoundEx( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int flags ) { + syscall( CG_S_STARTSOUNDEX, origin, entityNum, entchannel, sfx, flags, 127 /* Gordon: default volume always for the moment*/ ); +} +//----(SA) end + +void trap_S_StartSoundExVControl( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx, int flags, int volume ) { + syscall( CG_S_STARTSOUNDEX, origin, entityNum, entchannel, sfx, flags, volume ); +} + +void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) { + syscall( CG_S_STARTLOCALSOUND, sfx, channelNum, 127 /* Gordon: default volume always for the moment*/ ); +} + +void trap_S_ClearLoopingSounds( void ) { + syscall( CG_S_CLEARLOOPINGSOUNDS ); +} + +void trap_S_ClearSounds( qboolean killmusic ) { + syscall( CG_S_CLEARSOUNDS, killmusic ); +} + +void trap_S_AddLoopingSound( const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, int volume, int soundTime ) { + syscall( CG_S_ADDLOOPINGSOUND, origin, velocity, 1250, sfx, volume, soundTime ); // volume was previously removed from CG_S_ADDLOOPINGSOUND. I added 'range' +} + +void trap_S_AddRealLoopingSound( const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, int range, int volume, int soundTime ) { + syscall( CG_S_ADDREALLOOPINGSOUND, origin, velocity, range, sfx, volume, soundTime ); +} + +void trap_S_StopStreamingSound( int entityNum ) { + syscall( CG_S_STOPSTREAMINGSOUND, entityNum ); +} + +void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { + syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin ); +} + +// Ridah, talking animations +int trap_S_GetVoiceAmplitude( int entityNum ) { + return syscall( CG_S_GETVOICEAMPLITUDE, entityNum ); +} +// done. + +void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) { + syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater ); +} + +/*sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { + CG_DrawInformation(); + return syscall( CG_S_REGISTERSOUND, sample, compressed ); +}*/ + +int trap_S_GetSoundLength( sfxHandle_t sfx ) { + return syscall( CG_S_GETSOUNDLENGTH, sfx ); +} + +// ydnar: for timing looped sounds +int trap_S_GetCurrentSoundTime( void ) { + return syscall( CG_S_GETCURRENTSOUNDTIME ); +} + +void trap_S_StartBackgroundTrack( const char *intro, const char *loop, int fadeupTime ) { + syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop, fadeupTime ); +} + +void trap_S_FadeBackgroundTrack( float targetvol, int time, int num ) { // yes, i know. fadebackground coming in, fadestreaming going out. will have to see where functionality leads... + syscall( CG_S_FADESTREAMINGSOUND, PASSFLOAT( targetvol ), time, num ); // 'num' is '0' if it's music, '1' if it's "all streaming sounds" +} + +void trap_S_FadeAllSound( float targetvol, int time, qboolean stopsounds ) { + syscall( CG_S_FADEALLSOUNDS, PASSFLOAT( targetvol ), time, stopsounds ); +} + +int trap_S_StartStreamingSound( const char *intro, const char *loop, int entnum, int channel, int attenuation ) { + return syscall( CG_S_STARTSTREAMINGSOUND, intro, loop, entnum, channel, attenuation ); +} + +/*void trap_R_LoadWorldMap( const char *mapname ) { + CG_DrawInformation(); + syscall( CG_R_LOADWORLDMAP, mapname ); +} + +qhandle_t trap_R_RegisterModel( const char *name ) { + CG_DrawInformation(); + return syscall( CG_R_REGISTERMODEL, name ); +}*/ + +//----(SA) added +qboolean trap_R_GetSkinModel( qhandle_t skinid, const char *type, char *name ) { + return syscall( CG_R_GETSKINMODEL, skinid, type, name ); +} + +qhandle_t trap_R_GetShaderFromModel( qhandle_t modelid, int surfnum, int withlightmap ) { + return syscall( CG_R_GETMODELSHADER, modelid, surfnum, withlightmap ); +} +//----(SA) end + +/*qhandle_t trap_R_RegisterSkin( const char *name ) { + CG_DrawInformation(); + return syscall( CG_R_REGISTERSKIN, name ); +} + +qhandle_t trap_R_RegisterShader( const char *name ) { + CG_DrawInformation(); + return syscall( CG_R_REGISTERSHADER, name ); +} + +qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { + CG_DrawInformation(); + return syscall( CG_R_REGISTERSHADERNOMIP, name ); +} + +void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { + syscall(CG_R_REGISTERFONT, fontName, pointSize, font ); +}*/ + +void trap_R_ClearScene( void ) { + syscall( CG_R_CLEARSCENE ); +} + +void trap_R_AddRefEntityToScene( const refEntity_t *re ) { + syscall( CG_R_ADDREFENTITYTOSCENE, re ); +} + +void trap_R_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts ) { + syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts ); +} + +void trap_R_AddPolyBufferToScene( polyBuffer_t* pPolyBuffer ) { + syscall( CG_R_ADDPOLYBUFFERTOSCENE, pPolyBuffer ); +} + +// Ridah +void trap_R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { + syscall( CG_R_ADDPOLYSTOSCENE, hShader, numVerts, verts, numPolys ); +} +// done. + +// ydnar: new dlight system +//% void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b, int overdraw ) { +//% syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b), overdraw ); +//% } +void trap_R_AddLightToScene( const vec3_t org, float radius, float intensity, float r, float g, float b, qhandle_t hShader, int flags ) { + syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT( radius ), PASSFLOAT( intensity ), + PASSFLOAT( r ), PASSFLOAT( g ), PASSFLOAT( b ), hShader, flags ); +} + +//----(SA) +void trap_R_AddCoronaToScene( const vec3_t org, float r, float g, float b, float scale, int id, qboolean visible ) { + syscall( CG_R_ADDCORONATOSCENE, org, PASSFLOAT( r ), PASSFLOAT( g ), PASSFLOAT( b ), PASSFLOAT( scale ), id, visible ); +} +//----(SA) + +//----(SA) +void trap_R_SetFog( int fogvar, int var1, int var2, float r, float g, float b, float density ) { + syscall( CG_R_SETFOG, fogvar, var1, var2, PASSFLOAT( r ), PASSFLOAT( g ), PASSFLOAT( b ), PASSFLOAT( density ) ); +} +//----(SA) + +void trap_R_SetGlobalFog( qboolean restore, int duration, float r, float g, float b, float depthForOpaque ) { + syscall( CG_R_SETGLOBALFOG, restore, duration, PASSFLOAT( r ), PASSFLOAT( g ), PASSFLOAT( b ), PASSFLOAT( depthForOpaque ) ); +} + +void trap_R_RenderScene( const refdef_t *fd ) { + syscall( CG_R_RENDERSCENE, fd ); +} + +// Mad Doctor I, 11/4/2002. +void trap_R_SaveViewParms() { + syscall( CG_R_SAVEVIEWPARMS ); +} + +// Mad Doctor I, 11/4/2002. +void trap_R_RestoreViewParms() { + syscall( CG_R_RESTOREVIEWPARMS ); +} + +void trap_R_SetColor( const float *rgba ) { + syscall( CG_R_SETCOLOR, rgba ); +} + +void trap_R_DrawStretchPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ) { + syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT( x ), PASSFLOAT( y ), PASSFLOAT( w ), PASSFLOAT( h ), PASSFLOAT( s1 ), PASSFLOAT( t1 ), PASSFLOAT( s2 ), PASSFLOAT( t2 ), hShader ); +} + +void trap_R_DrawRotatedPic( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader, float angle ) { + syscall( CG_R_DRAWROTATEDPIC, PASSFLOAT( x ), PASSFLOAT( y ), PASSFLOAT( w ), PASSFLOAT( h ), PASSFLOAT( s1 ), PASSFLOAT( t1 ), PASSFLOAT( s2 ), PASSFLOAT( t2 ), hShader, PASSFLOAT( 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 ) { + syscall( CG_R_DRAWSTRETCHPIC_GRADIENT, PASSFLOAT( x ), PASSFLOAT( y ), PASSFLOAT( w ), PASSFLOAT( h ), PASSFLOAT( s1 ), PASSFLOAT( t1 ), PASSFLOAT( s2 ), PASSFLOAT( t2 ), hShader, gradientColor, gradientType ); +} + +void trap_R_Add2dPolys( polyVert_t* verts, int numverts, qhandle_t hShader ) { + syscall( CG_R_DRAW2DPOLYS, verts, numverts, hShader ); +} + + +void trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { + syscall( CG_R_MODELBOUNDS, model, mins, maxs ); +} + +int trap_R_LerpTag( orientation_t *tag, const refEntity_t *refent, const char *tagName, int startIndex ) { + return syscall( CG_R_LERPTAG, tag, refent, tagName, startIndex ); +} + +void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) { + syscall( CG_R_REMAP_SHADER, oldShader, newShader, timeOffset ); +} + +void trap_GetGlconfig( glconfig_t *glconfig ) { + syscall( CG_GETGLCONFIG, glconfig ); +} + +void trap_GetGameState( gameState_t *gamestate ) { + syscall( CG_GETGAMESTATE, gamestate ); +} + +#ifdef _DEBUG +//#define FAKELAG +#ifdef FAKELAG +#define MAX_SNAPSHOT_BACKUP 256 +#define MAX_SNAPSHOT_MASK ( MAX_SNAPSHOT_BACKUP - 1 ) + +static snapshot_t snaps[MAX_SNAPSHOT_BACKUP]; +static int curSnapshotNumber; +int snapshotDelayTime; +static qboolean skiponeget; +#endif // FAKELAG +#endif // _DEBUG + +void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { + syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime ); + +#ifdef FAKELAG + { + char s[MAX_STRING_CHARS]; + int fakeLag; + + trap_Cvar_VariableStringBuffer( "g_fakelag", s, sizeof( s ) ); + fakeLag = atoi( s ); + if ( fakeLag < 0 ) { + fakeLag = 0; + } + + if ( fakeLag ) { + if ( curSnapshotNumber < cg.latestSnapshotNum ) { + *snapshotNumber = cg.latestSnapshotNum + 1; + curSnapshotNumber = cg.latestSnapshotNum + 2; // skip one ahead and we're good to go on the next frame + skiponeget = qtrue; + } else { + *snapshotNumber = curSnapshotNumber; + } + } + } +#endif // FAKELAG +} + +qboolean trap_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { +#ifndef FAKELAG + return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); +#else + { + char s[MAX_STRING_CHARS]; + int fakeLag; + + if ( skiponeget ) { + syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); + } + + trap_Cvar_VariableStringBuffer( "g_fakelag", s, sizeof( s ) ); + fakeLag = atoi( s ); + if ( fakeLag < 0 ) { + fakeLag = 0; + } + + if ( fakeLag ) { + int i; + int realsnaptime, thissnaptime; + + // store our newest usercmd + curSnapshotNumber++; + memcpy( &snaps[curSnapshotNumber & MAX_SNAPSHOT_MASK], snapshot, sizeof( snapshot_t ) ); + + // find a usercmd that is fakeLag msec behind + i = curSnapshotNumber & MAX_SNAPSHOT_MASK; + realsnaptime = snaps[i].serverTime; + i--; + do { + thissnaptime = snaps[i & MAX_SNAPSHOT_MASK].serverTime; + + if ( realsnaptime - thissnaptime > fakeLag ) { + // found the right one + snapshotDelayTime = realsnaptime - thissnaptime; + snapshot = &snaps[i & MAX_SNAPSHOT_MASK]; + //*snapshotNumber = i & MAX_SNAPSHOT_MASK; + return qtrue; + } + + i--; + } while ( ( i & MAX_SNAPSHOT_MASK ) != ( curSnapshotNumber & MAX_SNAPSHOT_MASK ) ); + + // didn't find a proper one, just use the oldest one we have + snapshotDelayTime = realsnaptime - thissnaptime; + snapshot = &snaps[( curSnapshotNumber - 1 ) & MAX_SNAPSHOT_MASK]; + //*snapshotNumber = (curSnapshotNumber - 1) & MAX_SNAPSHOT_MASK; + return qtrue; + } else { + return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); + } + } +#endif // FAKELAG +} + +qboolean trap_GetServerCommand( int serverCommandNumber ) { + return syscall( CG_GETSERVERCOMMAND, serverCommandNumber ); +} + +int trap_GetCurrentCmdNumber( void ) { + return syscall( CG_GETCURRENTCMDNUMBER ); +} + +qboolean trap_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { + return syscall( CG_GETUSERCMD, cmdNumber, ucmd ); +} + +void trap_SetUserCmdValue( int stateValue, int flags, float sensitivityScale, int mpIdentClient ) { + syscall( CG_SETUSERCMDVALUE, stateValue, flags, PASSFLOAT( sensitivityScale ), mpIdentClient ); +} + +void trap_SetClientLerpOrigin( float x, float y, float z ) { + syscall( CG_SETCLIENTLERPORIGIN, PASSFLOAT( x ), PASSFLOAT( y ), PASSFLOAT( z ) ); +} + +void testPrintInt( char *string, int i ) { + syscall( CG_TESTPRINTINT, string, i ); +} + +void testPrintFloat( char *string, float f ) { + syscall( CG_TESTPRINTFLOAT, string, PASSFLOAT( f ) ); +} + +int trap_MemoryRemaining( void ) { + return syscall( CG_MEMORY_REMAINING ); +} + +qboolean trap_loadCamera( int camNum, const char *name ) { + return syscall( CG_LOADCAMERA, camNum, name ); +} + +void trap_startCamera( int camNum, int time ) { + syscall( CG_STARTCAMERA, camNum, time ); +} + +void trap_stopCamera( int camNum ) { + syscall( CG_STOPCAMERA, camNum ); +} + +qboolean trap_getCameraInfo( int camNum, int time, vec3_t *origin, vec3_t *angles, float *fov ) { + return syscall( CG_GETCAMERAINFO, camNum, time, origin, angles, fov ); +} + + +qboolean trap_Key_IsDown( int keynum ) { + return syscall( CG_KEY_ISDOWN, keynum ); +} + +int trap_Key_GetCatcher( void ) { + return syscall( CG_KEY_GETCATCHER ); +} + +qboolean trap_Key_GetOverstrikeMode( void ) { + return syscall( CG_KEY_GETOVERSTRIKEMODE ); +} + +void trap_Key_SetOverstrikeMode( qboolean state ) { + syscall( CG_KEY_SETOVERSTRIKEMODE, state ); +} + +// binding MUST be lower case +void trap_Key_KeysForBinding( const char* binding, int* key1, int* key2 ) { + syscall( CG_KEY_BINDINGTOKEYS, binding, key1, key2 ); +} + +void trap_Key_SetCatcher( int catcher ) { + syscall( CG_KEY_SETCATCHER, catcher ); +} + +int trap_Key_GetKey( const char *binding ) { + return syscall( CG_KEY_GETKEY, binding ); +} + + +int trap_PC_AddGlobalDefine( char *define ) { + return syscall( CG_PC_ADD_GLOBAL_DEFINE, define ); +} + +int trap_PC_LoadSource( const char *filename ) { + return syscall( CG_PC_LOAD_SOURCE, filename ); +} + +int trap_PC_FreeSource( int handle ) { + return syscall( CG_PC_FREE_SOURCE, handle ); +} + +int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { + return syscall( CG_PC_READ_TOKEN, handle, pc_token ); +} + +int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { + return syscall( CG_PC_SOURCE_FILE_AND_LINE, handle, filename, line ); +} + +int trap_PC_UnReadToken( int handle ) { + return syscall( CG_PC_UNREAD_TOKEN, handle ); +} + +void trap_S_StopBackgroundTrack( void ) { + syscall( CG_S_STOPBACKGROUNDTRACK ); +} + +int trap_RealTime( qtime_t *qtime ) { + return syscall( CG_REAL_TIME, qtime ); +} + +void trap_SnapVector( float *v ) { + syscall( CG_SNAPVECTOR, v ); +} + +// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate) +int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits ) { + return syscall( CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits ); +} + +// stops playing the cinematic and ends it. should always return FMV_EOF +// cinematics must be stopped in reverse order of when they are started +e_status trap_CIN_StopCinematic( int handle ) { + return syscall( CG_CIN_STOPCINEMATIC, handle ); +} + + +// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached. +e_status trap_CIN_RunCinematic( int handle ) { + return syscall( CG_CIN_RUNCINEMATIC, handle ); +} + + +// draws the current frame +void trap_CIN_DrawCinematic( int handle ) { + syscall( CG_CIN_DRAWCINEMATIC, handle ); +} + + +// allows you to resize the animation dynamically +void trap_CIN_SetExtents( int handle, int x, int y, int w, int h ) { + syscall( CG_CIN_SETEXTENTS, handle, x, y, w, h ); +} + +qboolean trap_GetEntityToken( char *buffer, int bufferSize ) { + return syscall( CG_GET_ENTITY_TOKEN, buffer, bufferSize ); +} + +//----(SA) added +// bring up a popup menu +extern void Menus_OpenByName( const char *p ); + +//void trap_UI_Popup( const char *arg0) { +void trap_UI_Popup( int arg0 ) { + syscall( CG_INGAME_POPUP, arg0 ); +} + +void trap_UI_ClosePopup( const char *arg0 ) { + syscall( CG_INGAME_CLOSEPOPUP, arg0 ); +} + +void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ) { + syscall( CG_KEY_GETBINDINGBUF, keynum, buf, buflen ); +} + +void trap_Key_SetBinding( int keynum, const char *binding ) { + syscall( CG_KEY_SETBINDING, keynum, binding ); +} + +void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) { + syscall( CG_KEY_KEYNUMTOSTRINGBUF, keynum, buf, buflen ); +} + +void trap_TranslateString( const char *string, char *buf ) { + syscall( CG_TRANSLATE_STRING, string, buf ); +} +// -NERVE - SMF + +// Media register functions +#ifdef _DEBUG +#define DEBUG_REGISTERPROFILE_INIT int dbgTime = trap_Milliseconds(); +#define DEBUG_REGISTERPROFILE_EXEC( f,n ) if ( developer.integer ) {CG_Printf( "%s : loaded %s in %i msec\n", f, n, trap_Milliseconds() - dbgTime );} +sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { + sfxHandle_t snd; + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + snd = syscall( CG_S_REGISTERSOUND, sample, qfalse /* compressed */ ); + if ( !*sample ) { + Com_Printf( "^1Warning: Null Sample filename\n" ); + } + if ( snd == 0 ) { + Com_Printf( "^1Warning: Failed to load sound: %s\n", sample ); + } + DEBUG_REGISTERPROFILE_EXEC( "trap_S_RegisterSound",sample ) + trap_PumpEventLoop(); + return snd; +} + +qhandle_t trap_R_RegisterModel( const char *name ) { + qhandle_t handle; + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + handle = syscall( CG_R_REGISTERMODEL, name ); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_RegisterModel",name ) + trap_PumpEventLoop(); + return handle; +} + +qhandle_t trap_R_RegisterSkin( const char *name ) { + qhandle_t handle; + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + handle = syscall( CG_R_REGISTERSKIN, name ); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_RegisterSkin",name ) + trap_PumpEventLoop(); + return handle; +} + +qhandle_t trap_R_RegisterShader( const char *name ) { + qhandle_t handle; + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + handle = syscall( CG_R_REGISTERSHADER, name ); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_RegisterShader",name ) + trap_PumpEventLoop(); + return handle; +} + +qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { + qhandle_t handle; + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + handle = syscall( CG_R_REGISTERSHADERNOMIP, name ); + trap_PumpEventLoop(); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_RegisterShaderNpMip", name ); + return handle; +} + +void trap_R_RegisterFont( const char *fontName, int pointSize, fontInfo_t *font ) { + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + syscall( CG_R_REGISTERFONT, fontName, pointSize, font ); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_RegisterFont",fontName ) + trap_PumpEventLoop(); +} + +void trap_CM_LoadMap( const char *mapname ) { + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + syscall( CG_CM_LOADMAP, mapname ); + DEBUG_REGISTERPROFILE_EXEC( "trap_CM_LoadMap",mapname ) + trap_PumpEventLoop(); +} + +void trap_R_LoadWorldMap( const char *mapname ) { + DEBUG_REGISTERPROFILE_INIT + CG_DrawInformation( qtrue ); + syscall( CG_R_LOADWORLDMAP, mapname ); + DEBUG_REGISTERPROFILE_EXEC( "trap_R_LoadWorldMap",mapname ) + trap_PumpEventLoop(); +} +#else +sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + return syscall( CG_S_REGISTERSOUND, sample, qfalse /* compressed */ ); +} + +qhandle_t trap_R_RegisterModel( const char *name ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + return syscall( CG_R_REGISTERMODEL, name ); +} + +qhandle_t trap_R_RegisterSkin( const char *name ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + return syscall( CG_R_REGISTERSKIN, name ); +} + +qhandle_t trap_R_RegisterShader( const char *name ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + return syscall( CG_R_REGISTERSHADER, name ); +} + +qhandle_t trap_R_RegisterShaderNoMip( const char *name ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + return syscall( CG_R_REGISTERSHADERNOMIP, name ); +} + +void trap_R_RegisterFont( const char *fontName, int pointSize, fontInfo_t *font ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + syscall( CG_R_REGISTERFONT, fontName, pointSize, font ); +} + +void trap_CM_LoadMap( const char *mapname ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + syscall( CG_CM_LOADMAP, mapname ); +} + +void trap_R_LoadWorldMap( const char *mapname ) { + CG_DrawInformation( qtrue ); + trap_PumpEventLoop(); + syscall( CG_R_LOADWORLDMAP, mapname ); +} +#endif // _DEBUG + +qboolean trap_R_inPVS( const vec3_t p1, const vec3_t p2 ) { + return syscall( CG_R_INPVS, p1, p2 ); +} + +void trap_GetHunkData( int* hunkused, int* hunkexpected ) { + syscall( CG_GETHUNKDATA, hunkused, hunkexpected ); +} + +//zinx - binary message channel +void trap_SendMessage( char *buf, int buflen ) { + syscall( CG_SENDMESSAGE, buf, buflen ); +} + +messageStatus_t trap_MessageStatus( void ) { + return syscall( CG_MESSAGESTATUS ); +} + +//bani - dynamic shaders +qboolean trap_R_LoadDynamicShader( const char *shadername, const char *shadertext ) { + return syscall( CG_R_LOADDYNAMICSHADER, shadername, shadertext ); +} + +// fretn - render to texture +void trap_R_RenderToTexture( int textureid, int x, int y, int w, int h ) { + syscall( CG_R_RENDERTOTEXTURE, textureid, x, y, w, h ); +} + +int trap_R_GetTextureId( const char *name ) { + return syscall( CG_R_GETTEXTUREID, name ); +} + +// bani - sync rendering +void trap_R_Finish( void ) { + syscall( CG_R_FINISH ); +} + diff --git a/src/cgame/cg_trails.c b/src/cgame/cg_trails.c new file mode 100644 index 0000000..fc20f0d --- /dev/null +++ b/src/cgame/cg_trails.c @@ -0,0 +1,821 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// Ridah, cg_trails.c - draws a trail using multiple junction points + +#include "cg_local.h" + +typedef struct trailJunc_s +{ + struct trailJunc_s *nextGlobal, *prevGlobal; // next junction in the global list it is in (free or used) + struct trailJunc_s *nextJunc; // next junction in the trail + struct trailJunc_s *nextHead, *prevHead; // next head junc in the world + + void *usedby; // rain - zinx's trail fix + qboolean inuse, freed; + int ownerIndex; + qhandle_t shader; + + int sType; + int flags; + float sTex; + vec3_t pos; + int spawnTime, endTime; + float alphaStart, alphaEnd; + vec3_t colorStart, colorEnd; + float widthStart, widthEnd; + + // current settings + float alpha; + float width; + vec3_t color; + +} trailJunc_t; + +#define MAX_TRAILJUNCS 4096 + +trailJunc_t trailJuncs[MAX_TRAILJUNCS]; +trailJunc_t *freeTrails, *activeTrails; +trailJunc_t *headTrails; + +qboolean initTrails = qfalse; + +int numTrailsInuse; + +/* +=============== +CG_ClearTrails +=============== +*/ +void CG_ClearTrails( void ) { + int i; + + memset( trailJuncs, 0, sizeof( trailJunc_t ) * MAX_TRAILJUNCS ); + + freeTrails = trailJuncs; + activeTrails = NULL; + headTrails = NULL; + + for ( i = 0 ; i < MAX_TRAILJUNCS ; i++ ) + { + trailJuncs[i].nextGlobal = &trailJuncs[i + 1]; + + if ( i > 0 ) { + trailJuncs[i].prevGlobal = &trailJuncs[i - 1]; + } else { + trailJuncs[i].prevGlobal = NULL; + } + + trailJuncs[i].inuse = qfalse; + } + trailJuncs[MAX_TRAILJUNCS - 1].nextGlobal = NULL; + + initTrails = qtrue; + numTrailsInuse = 0; +} + +/* +=============== +CG_SpawnTrailJunc +=============== +*/ +trailJunc_t *CG_SpawnTrailJunc( trailJunc_t *headJunc ) { + trailJunc_t *j; + + if ( !freeTrails ) { + return NULL; + } + + if ( cg_paused.integer ) { + return NULL; + } + + // select the first free trail, and remove it from the list + j = freeTrails; + freeTrails = j->nextGlobal; + if ( freeTrails ) { + freeTrails->prevGlobal = NULL; + } + + j->nextGlobal = activeTrails; + if ( activeTrails ) { + activeTrails->prevGlobal = j; + } + activeTrails = j; + j->prevGlobal = NULL; + j->inuse = qtrue; + j->freed = qfalse; + + // if this owner has a headJunc, add us to the start + if ( headJunc ) { + // remove the headJunc from the list of heads + if ( headJunc == headTrails ) { + headTrails = headJunc->nextHead; + if ( headTrails ) { + headTrails->prevHead = NULL; + } + } else { + if ( headJunc->nextHead ) { + headJunc->nextHead->prevHead = headJunc->prevHead; + } + if ( headJunc->prevHead ) { + headJunc->prevHead->nextHead = headJunc->nextHead; + } + } + headJunc->prevHead = NULL; + headJunc->nextHead = NULL; + } + // make us the headTrail + if ( headTrails ) { + headTrails->prevHead = j; + } + j->nextHead = headTrails; + j->prevHead = NULL; + headTrails = j; + + j->nextJunc = headJunc; // if headJunc is NULL, then we'll just be the end of the list + + numTrailsInuse++; + + // debugging +// CG_Printf( "NumTrails: %i\n", numTrailsInuse ); + + return j; +} + + +/* +=============== +CG_AddTrailJunc + + returns the index of the trail junction created + + Used for generic trails +=============== +*/ +int CG_AddTrailJunc( int headJuncIndex, void *usedby, qhandle_t shader, int spawnTime, int sType, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth, int flags, vec3_t colorStart, vec3_t colorEnd, float sRatio, float animSpeed ) { + trailJunc_t *j, *headJunc; + + if ( headJuncIndex < 0 || headJuncIndex >= MAX_TRAILJUNCS ) { + return 0; + } + + if ( headJuncIndex > 0 ) { + headJunc = &trailJuncs[headJuncIndex - 1]; + + // rain - zinx's trail fix + if ( !headJunc->inuse || headJunc->usedby != usedby ) { + headJunc = NULL; + } + } else { + headJunc = NULL; + } + + j = CG_SpawnTrailJunc( headJunc ); + if ( !j ) { +// CG_Printf("couldnt spawn trail junc\n"); + return 0; + } + + // rain - zinx's trail fix - mark who's using this trail so that + // we can handle the someone-else-stole-our-trail case + j->usedby = usedby; + + if ( alphaStart > 1.0 ) { + alphaStart = 1.0; + } + if ( alphaStart < 0.0 ) { + alphaStart = 0.0; + } + if ( alphaEnd > 1.0 ) { + alphaEnd = 1.0; + } + if ( alphaEnd < 0.0 ) { + alphaEnd = 0.0; + } + + // setup the trail junction + j->shader = shader; + j->sType = sType; + VectorCopy( pos, j->pos ); + j->flags = flags; + + j->spawnTime = spawnTime; + j->endTime = spawnTime + trailLife; + + VectorCopy( colorStart, j->colorStart ); + VectorCopy( colorEnd, j->colorEnd ); + + j->alphaStart = alphaStart; + j->alphaEnd = alphaEnd; + + j->widthStart = startWidth; + j->widthEnd = endWidth; + + if ( sType == STYPE_REPEAT ) { + if ( headJunc ) { + j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / sRatio ) / j->widthEnd ); + } else { + // FIXME: need a way to specify offset timing + j->sTex = ( animSpeed * ( 1.0 - ( (float)( cg.time % 1000 ) / 1000.0 ) ) ) / ( sRatio ); +// j->sTex = 0; + } + } + + return ( (int)( j - trailJuncs ) + 1 ); +} + +/* +=============== +CG_AddSparkJunc + + returns the index of the trail junction created +=============== +*/ +int CG_AddSparkJunc( int headJuncIndex, void *usedby, qhandle_t shader, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth ) { + trailJunc_t *j, *headJunc; + + if ( headJuncIndex < 0 || headJuncIndex >= MAX_TRAILJUNCS ) { + return 0; + } + + if ( headJuncIndex > 0 ) { + headJunc = &trailJuncs[headJuncIndex - 1]; + + // rain - zinx's trail fix + if ( !headJunc->inuse || headJunc->usedby != usedby ) { + headJunc = NULL; + } + } else { + headJunc = NULL; + } + + j = CG_SpawnTrailJunc( headJunc ); + if ( !j ) { + return 0; + } + + j->usedby = usedby; + + // setup the trail junction + j->shader = shader; + j->sType = STYPE_STRETCH; + VectorCopy( pos, j->pos ); + j->flags = TJFL_NOCULL; // don't worry about fading up close + + j->spawnTime = cg.time; + j->endTime = cg.time + trailLife; + + VectorSet( j->colorStart, 1.0, 0.8 + 0.2 * alphaStart, 0.4 + 0.4 * alphaStart ); + VectorSet( j->colorEnd, 1.0, 0.8 + 0.2 * alphaEnd, 0.4 + 0.4 * alphaEnd ); +// VectorScale( j->colorStart, alphaStart, j->colorStart ); +// VectorScale( j->colorEnd, alphaEnd, j->colorEnd ); + + j->alphaStart = alphaStart * 2; + j->alphaEnd = alphaEnd * 2; +// j->alphaStart = 1.0; +// j->alphaEnd = 1.0; + + j->widthStart = startWidth; + j->widthEnd = endWidth; + + return ( (int)( j - trailJuncs ) + 1 ); +} + +/* +=============== +CG_AddSmokeJunc + + returns the index of the trail junction created +=============== +*/ +int CG_AddSmokeJunc( int headJuncIndex, void *usedby, qhandle_t shader, vec3_t pos, int trailLife, float alpha, float startWidth, float endWidth ) { +#define ST_RATIO 4.0 // sprite image: width / height + trailJunc_t *j, *headJunc; + + if ( headJuncIndex < 0 || headJuncIndex >= MAX_TRAILJUNCS ) { + return 0; + } + + if ( headJuncIndex > 0 ) { + headJunc = &trailJuncs[headJuncIndex - 1]; + + // rain - zinx's trail fix + if ( !headJunc->inuse || headJunc->usedby != usedby ) { + headJunc = NULL; + } + } else { + headJunc = NULL; + } + + j = CG_SpawnTrailJunc( headJunc ); + if ( !j ) { + return 0; + } + + j->usedby = usedby; + + // setup the trail junction + j->shader = shader; + j->sType = STYPE_REPEAT; + VectorCopy( pos, j->pos ); + j->flags = TJFL_FADEIN; + + j->spawnTime = cg.time; + j->endTime = cg.time + trailLife; + + VectorSet( j->colorStart, 0.7, 0.7, 0.7 ); + VectorSet( j->colorEnd, 0.0, 0.0, 0.0 ); + + j->alphaStart = alpha; + j->alphaEnd = 0.0; + + j->widthStart = startWidth; + j->widthEnd = endWidth; + + if ( headJunc ) { + j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / ST_RATIO ) / j->widthEnd ); + } else { + // first junction, so this will become the "tail" very soon, make it fade out + j->sTex = 0; + j->alphaStart = 0.0; + j->alphaEnd = 0.0; + } + + return ( (int)( j - trailJuncs ) + 1 ); +} + +void CG_KillTrail( trailJunc_t *t ); + +/* +=========== +CG_FreeTrailJunc +=========== +*/ +void CG_FreeTrailJunc( trailJunc_t *junc ) { + // kill any juncs after us, so they aren't left hanging + if ( junc->nextJunc ) { + CG_KillTrail( junc ); + } + + // make it non-active + junc->inuse = qfalse; + junc->freed = qtrue; + if ( junc->nextGlobal ) { + junc->nextGlobal->prevGlobal = junc->prevGlobal; + } + if ( junc->prevGlobal ) { + junc->prevGlobal->nextGlobal = junc->nextGlobal; + } + if ( junc == activeTrails ) { + activeTrails = junc->nextGlobal; + } + + // if it's a head, remove it + if ( junc == headTrails ) { + headTrails = junc->nextHead; + } + if ( junc->nextHead ) { + junc->nextHead->prevHead = junc->prevHead; + } + if ( junc->prevHead ) { + junc->prevHead->nextHead = junc->nextHead; + } + junc->nextHead = NULL; + junc->prevHead = NULL; + + // stick it in the free list + junc->prevGlobal = NULL; + junc->nextGlobal = freeTrails; + if ( freeTrails ) { + freeTrails->prevGlobal = junc; + } + freeTrails = junc; + + numTrailsInuse--; +} + +/* +=========== +CG_KillTrail +=========== +*/ +void CG_KillTrail( trailJunc_t *t ) { + trailJunc_t *next; + if ( !t->inuse && t->freed ) { + return; + } + next = t->nextJunc; + if ( next < &trailJuncs[0] || next >= &trailJuncs[MAX_TRAILJUNCS] ) { + next = NULL; + } + t->nextJunc = NULL; + if ( next->nextJunc && next->nextJunc == t ) { + next->nextJunc = NULL; + } + if ( next ) { + CG_FreeTrailJunc( next ); + } +} + +/* +============== +CG_AddTrailToScene + + TODO: this can do with some major optimization +============== +*/ +static vec3_t vforward, vright, vup; +#define MAX_TRAIL_VERTS 2048 +static polyVert_t verts[MAX_TRAIL_VERTS]; +static polyVert_t outVerts[MAX_TRAIL_VERTS * 3]; + +void CG_AddTrailToScene( trailJunc_t *trail, int iteration, int numJuncs ) { + int k, i, n, l, numOutVerts; + polyVert_t mid; + float mod[4]; + float sInc, s; + trailJunc_t *j, *jNext; + vec3_t fwd, up, p, v; + // clipping vars + #define TRAIL_FADE_CLOSE_DIST 64.0 + #define TRAIL_FADE_FAR_SCALE 4.0 + vec3_t viewProj; + float viewDist, fadeAlpha; + + // add spark shader at head position + if ( trail->flags & TJFL_SPARKHEADFLARE ) { + polyBuffer_t* pPolyBuffer = CG_PB_FindFreePolyBuffer( cgs.media.sparkFlareShader, 4, 6 ); + if ( pPolyBuffer ) { + int pos = pPolyBuffer->numVerts; + + j = trail; + + VectorCopy( j->pos, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], -j->width * 2, vup, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], -j->width * 2, vright, pPolyBuffer->xyz[pos] ); + pPolyBuffer->st[pos][0] = 0; + pPolyBuffer->st[pos][1] = 0; + pos++; + + VectorCopy( j->pos, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], -j->width * 2, vup, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], j->width * 2, vright, pPolyBuffer->xyz[pos] ); + pPolyBuffer->st[pos][0] = 0; + pPolyBuffer->st[pos][1] = 1; + pos++; + + VectorCopy( j->pos, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], j->width * 2, vup, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], j->width * 2, vright, pPolyBuffer->xyz[pos] ); + pPolyBuffer->st[pos][0] = 1; + pPolyBuffer->st[pos][1] = 1; + pos++; + + VectorCopy( j->pos, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], j->width * 2, vup, pPolyBuffer->xyz[pos] ); + VectorMA( pPolyBuffer->xyz[pos], -j->width * 2, vright, pPolyBuffer->xyz[pos] ); + pPolyBuffer->st[pos][0] = 1; + pPolyBuffer->st[pos][1] = 0; + pos++; + + for ( i = 0; i < 4; i++ ) { + pPolyBuffer->color[pPolyBuffer->numVerts + i][0] = 255; + pPolyBuffer->color[pPolyBuffer->numVerts + i][1] = 255; + pPolyBuffer->color[pPolyBuffer->numVerts + i][2] = 255; + pPolyBuffer->color[pPolyBuffer->numVerts + i][3] = ( unsigned char )( j->alpha * 255.0 ); + } + + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 0] = pPolyBuffer->numVerts + 0; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 1] = pPolyBuffer->numVerts + 1; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 2] = pPolyBuffer->numVerts + 2; + + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 3] = pPolyBuffer->numVerts + 2; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 4] = pPolyBuffer->numVerts + 3; + pPolyBuffer->indicies[pPolyBuffer->numIndicies + 5] = pPolyBuffer->numVerts + 0; + + pPolyBuffer->numVerts += 4; + pPolyBuffer->numIndicies += 6; + } + } + +// if (trail->flags & TJFL_CROSSOVER && iteration < 1) { +// iteration = 1; +// } + + sInc = 0; + + if ( !numJuncs ) { + // first count the number of juncs in the trail + j = trail; + numJuncs = 0; + sInc = 0; + while ( j ) { + numJuncs++; + + // check for a dead next junc + if ( !j->inuse && j->nextJunc && !j->nextJunc->inuse ) { + CG_KillTrail( j ); + } else if ( j->nextJunc && j->nextJunc->freed ) { + // not sure how this can happen, but it does, and causes infinite loops + j->nextJunc = NULL; + } + + if ( j->nextJunc ) { + sInc += VectorDistance( j->nextJunc->pos, j->pos ); + } + + j = j->nextJunc; + } + } + + if ( numJuncs < 2 ) { + return; + } + + s = 0; + if ( trail->sType == STYPE_STRETCH ) { + //sInc = ((1.0 - 0.1) / (float)(numJuncs)); // hack, the end of funnel shows a bit of the start (looping) + s = 0.05; + //s = 0.05; + } else if ( trail->sType == STYPE_REPEAT ) { + s = trail->sTex; + } + + // now traverse the list + j = trail; + jNext = j->nextJunc; + i = 0; + while ( jNext ) { + + // first get the directional vectors to the next junc + VectorSubtract( jNext->pos, j->pos, fwd ); + GetPerpendicularViewVector( cg.refdef_current->vieworg, j->pos, jNext->pos, up ); + + // if it's a crossover, draw it twice + if ( j->flags & TJFL_CROSSOVER ) { + if ( iteration > 0 ) { + ProjectPointOntoVector( cg.refdef_current->vieworg, j->pos, jNext->pos, viewProj ); + VectorSubtract( cg.refdef_current->vieworg, viewProj, v ); + VectorNormalize( v ); + + if ( iteration == 1 ) { + VectorMA( up, 0.3, v, up ); + } else { + VectorMA( up, -0.3, v, up ); + } + VectorNormalize( up ); + } + } + // do fading when moving towards the projection point onto the trail segment vector + else if ( !( j->flags & TJFL_NOCULL ) && ( j->widthEnd > 4 || jNext->widthEnd > 4 ) ) { + ProjectPointOntoVector( cg.refdef_current->vieworg, j->pos, jNext->pos, viewProj ); + viewDist = Distance( viewProj, cg.refdef_current->vieworg ); + if ( viewDist < ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ) ) { + if ( viewDist < TRAIL_FADE_CLOSE_DIST ) { + fadeAlpha = 0.0; + } else { + fadeAlpha = ( viewDist - TRAIL_FADE_CLOSE_DIST ) / ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ); + } + if ( fadeAlpha < j->alpha ) { + j->alpha = fadeAlpha; + } + if ( fadeAlpha < jNext->alpha ) { + jNext->alpha = fadeAlpha; + } + } + } + + // now output the QUAD for this segment + + // 1 ---- + VectorMA( j->pos, 0.5 * j->width, up, p ); + VectorCopy( p, verts[i].xyz ); + verts[i].st[0] = s; + verts[i].st[1] = 1.0; + for ( k = 0; k < 3; k++ ) + verts[i].modulate[k] = ( unsigned char )( j->color[k] * 255.0 ); + verts[i].modulate[3] = ( unsigned char )( j->alpha * 255.0 ); + + // blend this with the previous junc + if ( j != trail ) { + VectorAdd( verts[i].xyz, verts[i - 1].xyz, verts[i].xyz ); + VectorScale( verts[i].xyz, 0.5, verts[i].xyz ); + VectorCopy( verts[i].xyz, verts[i - 1].xyz ); + } else if ( j->flags & TJFL_FADEIN ) { + verts[i].modulate[3] = 0; // fade in + } + + i++; + + // 2 ---- + VectorMA( p, -1 * j->width, up, p ); + VectorCopy( p, verts[i].xyz ); + verts[i].st[0] = s; + verts[i].st[1] = 0.0; + for ( k = 0; k < 3; k++ ) + verts[i].modulate[k] = ( unsigned char )( j->color[k] * 255.0 ); + verts[i].modulate[3] = ( unsigned char )( j->alpha * 255.0 ); + + // blend this with the previous junc + if ( j != trail ) { + VectorAdd( verts[i].xyz, verts[i - 3].xyz, verts[i].xyz ); + VectorScale( verts[i].xyz, 0.5, verts[i].xyz ); + VectorCopy( verts[i].xyz, verts[i - 3].xyz ); + } else if ( j->flags & TJFL_FADEIN ) { + verts[i].modulate[3] = 0; // fade in + } + + i++; + + if ( trail->sType == STYPE_REPEAT ) { + s = jNext->sTex; + } else { + //s += sInc; + s += VectorDistance( j->pos, jNext->pos ) / sInc; + if ( s > 1.0 ) { + s = 1.0; + } + } + + // 3 ---- + VectorMA( jNext->pos, -0.5 * jNext->width, up, p ); + VectorCopy( p, verts[i].xyz ); + verts[i].st[0] = s; + verts[i].st[1] = 0.0; + for ( k = 0; k < 3; k++ ) + verts[i].modulate[k] = ( unsigned char )( jNext->color[k] * 255.0 ); + verts[i].modulate[3] = ( unsigned char )( jNext->alpha * 255.0 ); + i++; + + // 4 ---- + VectorMA( p, jNext->width, up, p ); + VectorCopy( p, verts[i].xyz ); + verts[i].st[0] = s; + verts[i].st[1] = 1.0; + for ( k = 0; k < 3; k++ ) + verts[i].modulate[k] = ( unsigned char )( jNext->color[k] * 255.0 ); + verts[i].modulate[3] = ( unsigned char )( jNext->alpha * 255.0 ); + i++; + + if ( i + 4 > MAX_TRAIL_VERTS ) { + break; + } + + j = jNext; + jNext = j->nextJunc; + } + + if ( trail->flags & TJFL_FIXDISTORT ) { + // build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow + // any shaped (convex) Quad without bilinear distortion + for ( k = 0, numOutVerts = 0; k < i; k += 4 ) { + VectorCopy( verts[k].xyz, mid.xyz ); + mid.st[0] = verts[k].st[0]; + mid.st[1] = verts[k].st[1]; + for ( l = 0; l < 4; l++ ) { + mod[l] = (float)verts[k].modulate[l]; + } + for ( n = 1; n < 4; n++ ) { + VectorAdd( verts[k + n].xyz, mid.xyz, mid.xyz ); + mid.st[0] += verts[k + n].st[0]; + mid.st[1] += verts[k + n].st[1]; + for ( l = 0; l < 4; l++ ) { + mod[l] += (float)verts[k + n].modulate[l]; + } + } + VectorScale( mid.xyz, 0.25, mid.xyz ); + mid.st[0] *= 0.25; + mid.st[1] *= 0.25; + for ( l = 0; l < 4; l++ ) { + mid.modulate[l] = ( unsigned char )( mod[l] / 4.0 ); + } + + // now output the tri's + for ( n = 0; n < 4; n++ ) { + outVerts[numOutVerts++] = verts[k + n]; + outVerts[numOutVerts++] = mid; + if ( n < 3 ) { + outVerts[numOutVerts++] = verts[k + n + 1]; + } else { + outVerts[numOutVerts++] = verts[k]; + } + } + + } + + if ( !( trail->flags & TJFL_NOPOLYMERGE ) ) { + trap_R_AddPolysToScene( trail->shader, 3, &outVerts[0], numOutVerts / 3 ); + } else { + int k; + for ( k = 0; k < numOutVerts / 3; k++ ) { + trap_R_AddPolyToScene( trail->shader, 3, &outVerts[k * 3] ); + } + } + } else + { + // send the polygons + // FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to + if ( !( trail->flags & TJFL_NOPOLYMERGE ) ) { + trap_R_AddPolysToScene( trail->shader, 4, &verts[0], i / 4 ); + } else { + int k; + for ( k = 0; k < i / 4; k++ ) { + trap_R_AddPolyToScene( trail->shader, 4, &verts[k * 4] ); + } + } + } + + // do we need to make another pass? + if ( trail->flags & TJFL_CROSSOVER ) { + if ( iteration < 2 ) { + CG_AddTrailToScene( trail, iteration + 1, numJuncs ); + } + } + +} + +/* +=============== +CG_AddTrails +=============== +*/ +void CG_AddTrails( void ) { + float lifeFrac; + trailJunc_t *j, *jNext; + + if ( !initTrails ) { + CG_ClearTrails(); + } + + //AngleVectors( cg.snap->ps.viewangles, vforward, vright, vup ); + VectorCopy( cg.refdef_current->viewaxis[0], vforward ); + VectorCopy( cg.refdef_current->viewaxis[1], vright ); + VectorCopy( cg.refdef_current->viewaxis[2], vup ); + + // update the settings for each junc + j = activeTrails; + while ( j ) { + lifeFrac = (float)( cg.time - j->spawnTime ) / (float)( j->endTime - j->spawnTime ); + if ( lifeFrac >= 1.0 ) { + j->inuse = qfalse; // flag it as dead + j->width = j->widthEnd; + j->alpha = j->alphaEnd; + if ( j->alpha > 1.0 ) { + j->alpha = 1.0; + } else if ( j->alpha < 0.0 ) { + j->alpha = 0.0; + } + VectorCopy( j->colorEnd, j->color ); + } else { + j->width = j->widthStart + ( j->widthEnd - j->widthStart ) * lifeFrac; + j->alpha = j->alphaStart + ( j->alphaEnd - j->alphaStart ) * lifeFrac; + if ( j->alpha > 1.0 ) { + j->alpha = 1.0; + } else if ( j->alpha < 0.0 ) { + j->alpha = 0.0; + } + VectorSubtract( j->colorEnd, j->colorStart, j->color ); + VectorMA( j->colorStart, lifeFrac, j->color, j->color ); + } + + j = j->nextGlobal; + } + + // draw the trailHeads + j = headTrails; + while ( j ) { + jNext = j->nextHead; // in case it gets removed + if ( !j->inuse ) { + CG_FreeTrailJunc( j ); + } else { + CG_AddTrailToScene( j, 0, 0 ); + } + j = jNext; + } +} diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c new file mode 100644 index 0000000..4841a9e --- /dev/null +++ b/src/cgame/cg_view.c @@ -0,0 +1,1992 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_view.c -- setup all the parameters (position, angle, etc) +// for a 3D rendering +#include "cg_local.h" + +//======================== +extern pmove_t cg_pmove; +//======================== + +/* +============================================================================= + + MODEL TESTING + +The viewthing and gun positioning tools from Q2 have been integrated and +enhanced into a single model testing facility. + +Model viewing can begin with either "testmodel " or "testgun ". + +The names must be the full pathname after the basedir, like +"models/weapons/v_launch/tris.md3" or "players/male/tris.md3" + +Testmodel will create a fake entity 100 units in front of the current view +position, directly facing the viewer. It will remain immobile, so you can +move around it to view it from different angles. + +Testgun will cause the model to follow the player around and supress the real +view weapon model. The default frame 0 of most guns is completely off screen, +so you will probably have to cycle a couple frames to see it. + +"nextframe", "prevframe", "nextskin", and "prevskin" commands will change the +frame or skin of the testmodel. These are bound to F5, F6, F7, and F8 in +q3default.cfg. + +If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let +you adjust the positioning. + +Note that none of the model testing features update while the game is paused, so +it may be convenient to test with deathmatch set to 1 so that bringing down the +console doesn't pause the game. + +============================================================================= +*/ + +/* +================= +CG_TestModel_f + +Creates an entity in front of the current position, which +can then be moved around +================= +*/ +void CG_TestModel_f( void ) { + vec3_t angles; + + memset( &cg.testModelEntity, 0, sizeof( cg.testModelEntity ) ); + if ( trap_Argc() < 2 ) { + return; + } + + Q_strncpyz( cg.testModelName, CG_Argv( 1 ), MAX_QPATH ); + cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); + + if ( trap_Argc() == 3 ) { + cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) ); + cg.testModelEntity.frame = 1; + cg.testModelEntity.oldframe = 0; + } + if ( !cg.testModelEntity.hModel ) { + CG_Printf( "Can't register model\n" ); + return; + } + + VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin ); + + angles[PITCH] = 0; + angles[YAW] = 180 + cg.refdefViewAngles[1]; + angles[ROLL] = 0; + + AnglesToAxis( angles, cg.testModelEntity.axis ); + cg.testGun = qfalse; +} + +/* +================= +CG_TestGun_f + +Replaces the current view weapon with the given model +================= +*/ +void CG_TestGun_f( void ) { + CG_TestModel_f(); + cg.testGun = qtrue; + cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON; +} + + +void CG_TestModelNextFrame_f( void ) { + cg.testModelEntity.frame++; + CG_Printf( "frame %i\n", cg.testModelEntity.frame ); +} + +void CG_TestModelPrevFrame_f( void ) { + cg.testModelEntity.frame--; + if ( cg.testModelEntity.frame < 0 ) { + cg.testModelEntity.frame = 0; + } + CG_Printf( "frame %i\n", cg.testModelEntity.frame ); +} + +void CG_TestModelNextSkin_f( void ) { + cg.testModelEntity.skinNum++; + CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); +} + +void CG_TestModelPrevSkin_f( void ) { + cg.testModelEntity.skinNum--; + if ( cg.testModelEntity.skinNum < 0 ) { + cg.testModelEntity.skinNum = 0; + } + CG_Printf( "skin %i\n", cg.testModelEntity.skinNum ); +} + +static void CG_AddTestModel( void ) { + int i; + + // re-register the model, because the level may have changed + cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName ); + if ( !cg.testModelEntity.hModel ) { + CG_Printf( "Can't register model\n" ); + return; + } + + // if testing a gun, set the origin reletive to the view origin + if ( cg.testGun ) { + VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin ); + VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] ); + VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] ); + VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] ); + + // allow the position to be adjusted + for ( i = 0 ; i < 3 ; i++ ) { + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value; + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value; + cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value; + } + } + + trap_R_AddRefEntityToScene( &cg.testModelEntity ); +} + + + +//============================================================================ + + +/* +================= +CG_CalcVrect + +Sets the coordinates of the rendered window +================= +*/ + +//static float letterbox_frac = 1.0f; // used for transitioning to letterbox for cutscenes // TODO: add to cg. // TTimo: unused +void CG_Letterbox( float xsize, float ysize, qboolean center ) { +// normal aspect is xx:xx +// letterbox is yy:yy (85% of 'normal' height) + if ( cg_letterbox.integer ) { + float lbheight, lbdiff; + + lbheight = ysize * 0.85; + lbdiff = ysize - lbheight; + + if ( !center ) { + int offset = ( cgs.glconfig.vidHeight * ( .5f * lbdiff ) ) / 100; + offset &= ~1; + cg.refdef.y += offset; + } + + ysize = lbheight; +// if(letterbox_frac != 0) { +// letterbox_frac -= 0.01f; // (SA) TODO: make non fps dependant +// if(letterbox_frac < 0) +// letterbox_frac = 0; +// ysize += (lbdiff * letterbox_frac); +// } +// } else { +// if(letterbox_frac != 1) { +// letterbox_frac += 0.01f; // (SA) TODO: make non fps dependant +// if(letterbox_frac > 1) +// letterbox_frac = 1; +// ysize = lbheight + (lbdiff * letterbox_frac); +// } + } + + cg.refdef.width = cgs.glconfig.vidWidth * xsize / 100; + cg.refdef.width &= ~1; + + cg.refdef.height = cgs.glconfig.vidHeight * ysize / 100; + cg.refdef.height &= ~1; + + if ( center ) { + cg.refdef.x = ( cgs.glconfig.vidWidth - cg.refdef.width ) / 2; + cg.refdef.y = ( cgs.glconfig.vidHeight - cg.refdef.height ) / 2; + } +} + +static void CG_CalcVrect( void ) { + if ( cg.showGameView ) { + float x, y, w, h; + x = LIMBO_3D_X; + y = LIMBO_3D_Y; + w = LIMBO_3D_W; + h = LIMBO_3D_H; + + CG_AdjustFrom640( &x, &y, &w, &h ); + + cg.refdef.x = x; + cg.refdef.y = y; + cg.refdef.width = w; + cg.refdef.height = h; + + CG_Letterbox( ( LIMBO_3D_W / 640.f ) * 100, ( LIMBO_3D_H / 480.f ) * 100, qfalse ); + return; + } + + CG_Letterbox( 100, 100, qtrue ); +} + +//============================================================================== + +/* +=============== +CG_OffsetThirdPersonView + +=============== +*/ +#define FOCUS_DISTANCE 400 //800 //512 +void CG_OffsetThirdPersonView( void ) { + vec3_t forward, right, up; + vec3_t view; + vec3_t focusAngles; + trace_t trace; + static vec3_t mins = { -4, -4, -4 }; + static vec3_t maxs = { 4, 4, 4 }; + vec3_t focusPoint; + float focusDist; + float forwardScale, sideScale; + + cg.refdef_current->vieworg[2] += cg.predictedPlayerState.viewheight; + + VectorCopy( cg.refdefViewAngles, focusAngles ); + + // rain - if dead, look at medic or allow freelook if none in range + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { + // rain - #254 - force yaw to 0 if we're tracking a medic + if ( cg.snap->ps.viewlocked != 7 ) { + // rain - do short2angle AFTER the network part + focusAngles[YAW] = SHORT2ANGLE( cg.predictedPlayerState.stats[STAT_DEAD_YAW] ); + cg.refdefViewAngles[YAW] = SHORT2ANGLE( cg.predictedPlayerState.stats[STAT_DEAD_YAW] ); + } + } + + if ( focusAngles[PITCH] > 45 ) { + focusAngles[PITCH] = 45; // don't go too far overhead + } + AngleVectors( focusAngles, forward, NULL, NULL ); + + if ( cg_thirdPerson.integer == 2 ) { + VectorCopy( cg.predictedPlayerState.origin, focusPoint ); + } else { + VectorMA( cg.refdef_current->vieworg, FOCUS_DISTANCE, forward, focusPoint ); + } + + VectorCopy( cg.refdef_current->vieworg, view ); + + view[2] += 8; + cg.refdefViewAngles[PITCH] *= 0.5; + + AngleVectors( cg.refdefViewAngles, forward, right, up ); + + forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); + sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); + VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); + VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); + + // trace a ray from the origin to the viewpoint to make sure the view isn't + // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything + + CG_Trace( &trace, cg.refdef_current->vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); + + if ( trace.fraction != 1.0 ) { + VectorCopy( trace.endpos, view ); + view[2] += ( 1.0 - trace.fraction ) * 32; + // try another trace to this position, because a tunnel may have the ceiling + // close enogh that this is poking out + + CG_Trace( &trace, cg.refdef_current->vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); + VectorCopy( trace.endpos, view ); + } + + + VectorCopy( view, cg.refdef_current->vieworg ); + + // select pitch to look at focus point from vieword + VectorSubtract( focusPoint, cg.refdef_current->vieworg, focusPoint ); + focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); + if ( focusDist < 1 ) { + focusDist = 1; // should never happen + } + cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); + cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; +} + + +// this causes a compiler bug on mac MrC compiler +static void CG_StepOffset( void ) { + int timeDelta; + + // smooth out stair climbing + timeDelta = cg.time - cg.stepTime; + // Ridah + if ( timeDelta < 0 ) { + cg.stepTime = cg.time; + } + if ( timeDelta < STEP_TIME ) { + cg.refdef_current->vieworg[2] -= cg.stepChange + * ( STEP_TIME - timeDelta ) / STEP_TIME; + } +} + +/* +================ +CG_KickAngles +================ +*/ +void CG_KickAngles( void ) { + const vec3_t centerSpeed = {2400, 2400, 2400}; + const float recoilCenterSpeed = 200; + const float recoilIgnoreCutoff = 15; + const float recoilMaxSpeed = 50; + const vec3_t maxKickAngles = {10,10,10}; + float idealCenterSpeed, kickChange; + int i, frametime, t; + float ft; + #define STEP 20 + char buf[32]; // NERVE - SMF + + // this code is frametime-dependant, so split it up into small chunks + //cg.kickAngles[PITCH] = 0; + cg.recoilPitchAngle = 0; + for ( t = cg.frametime; t > 0; t -= STEP ) { + if ( t > STEP ) { + frametime = STEP; + } else { + frametime = t; + } + + ft = ( (float)frametime / 1000 ); + + // kickAngles is spring-centered + for ( i = 0; i < 3; i++ ) { + if ( cg.kickAVel[i] || cg.kickAngles[i] ) { + // apply centering forces to kickAvel + if ( cg.kickAngles[i] && frametime ) { + idealCenterSpeed = -( 2.0 * ( cg.kickAngles[i] > 0 ) - 1.0 ) * centerSpeed[i]; + if ( idealCenterSpeed ) { + cg.kickAVel[i] += idealCenterSpeed * ft; + } + } + // add the kickAVel to the kickAngles + kickChange = cg.kickAVel[i] * ft; + if ( cg.kickAngles[i] && ( cg.kickAngles[i] < 0 ) != ( kickChange < 0 ) ) { // slower when returning to center + kickChange *= 0.06; + } + // check for crossing back over the center point + if ( !cg.kickAngles[i] || ( ( cg.kickAngles[i] + kickChange ) < 0 ) == ( cg.kickAngles[i] < 0 ) ) { + cg.kickAngles[i] += kickChange; + if ( !cg.kickAngles[i] && frametime ) { + cg.kickAVel[i] = 0; + } else if ( fabs( cg.kickAngles[i] ) > maxKickAngles[i] ) { + cg.kickAngles[i] = maxKickAngles[i] * ( ( 2 * ( cg.kickAngles[i] > 0 ) ) - 1 ); + cg.kickAVel[i] = 0; // force Avel to return us to center rather than keep going outside range + } + } else { // about to cross, so just zero it out + cg.kickAngles[i] = 0; + cg.kickAVel[i] = 0; + } + } + } + + // recoil is added to input viewangles per frame + if ( cg.recoilPitch ) { + // apply max recoil + if ( fabs( cg.recoilPitch ) > recoilMaxSpeed ) { + if ( cg.recoilPitch > 0 ) { + cg.recoilPitch = recoilMaxSpeed; + } else { + cg.recoilPitch = -recoilMaxSpeed; + } + } + // apply centering forces to kickAvel + if ( frametime ) { + idealCenterSpeed = -( 2.0 * ( cg.recoilPitch > 0 ) - 1.0 ) * recoilCenterSpeed * ft; + if ( idealCenterSpeed ) { + if ( fabs( idealCenterSpeed ) < fabs( cg.recoilPitch ) ) { + cg.recoilPitch += idealCenterSpeed; + } else { // back zero out + cg.recoilPitch = 0; + } + } + } + } + if ( fabs( cg.recoilPitch ) > recoilIgnoreCutoff ) { + cg.recoilPitchAngle += cg.recoilPitch * ft; + } + } + + // NERVE - SMF - only change cg_recoilPitch cvar when we need to + trap_Cvar_VariableStringBuffer( "cg_recoilPitch", buf, sizeof( buf ) ); + + if ( atof( buf ) != cg.recoilPitchAngle ) { + // encode the kick angles into a 24bit number, for sending to the client exe + trap_Cvar_Set( "cg_recoilPitch", va( "%f", cg.recoilPitchAngle ) ); + } +} + + +/* +CG_Concussive +*/ +void CG_Concussive( centity_t *cent ) { + float length; +// vec3_t dir, forward; + vec3_t vec; +// float dot; + + // + float pitchRecoilAdd, pitchAdd; + float yawRandom; + vec3_t recoil; + // + + if ( !cg.renderingThirdPerson && cent->currentState.density == cg.snap->ps.clientNum ) { + // + pitchRecoilAdd = 0; + pitchAdd = 0; + yawRandom = 0; + // + + VectorSubtract( cg.snap->ps.origin, cent->currentState.origin, vec ); + length = VectorLength( vec ); + + // pitchAdd = 12+rand()%3; + // yawRandom = 6; + + if ( length > 1024 ) { + return; + } + + pitchAdd = ( 32 / length ) * 64; + yawRandom = ( 32 / length ) * 64; + + // recoil[YAW] = crandom()*yawRandom; + if ( rand() % 100 > 50 ) { + recoil[YAW] = -yawRandom; + } else { + recoil[YAW] = yawRandom; + } + + recoil[ROLL] = -recoil[YAW]; // why not + recoil[PITCH] = -pitchAdd; + // scale it up a bit (easier to modify this while tweaking) + VectorScale( recoil, 30, recoil ); + // set the recoil + VectorCopy( recoil, cg.kickAVel ); + // set the recoil + cg.recoilPitch -= pitchRecoilAdd; + + } +} + + +/* +============== +CG_ZoomSway + sway for scoped weapons. + this takes aimspread into account so the view settles after a bit +============== +*/ +static void CG_ZoomSway( void ) { + float spreadfrac; + float phase; + + if ( !cg.zoomval ) { // not zoomed + return; + } + + if ( cg.snap->ps.eFlags & EF_MG42_ACTIVE || cg.snap->ps.eFlags & EF_AAGUN_ACTIVE ) { // don't draw when on mg_42 + return; + } + + spreadfrac = (float)cg.snap->ps.aimSpreadScale / 255.0; + + phase = cg.time / 1000.0 * ZOOM_PITCH_FREQUENCY * M_PI * 2; + cg.refdefViewAngles[PITCH] += ZOOM_PITCH_AMPLITUDE * sin( phase ) * ( spreadfrac + ZOOM_PITCH_MIN_AMPLITUDE ); + + phase = cg.time / 1000.0 * ZOOM_YAW_FREQUENCY * M_PI * 2; + cg.refdefViewAngles[YAW] += ZOOM_YAW_AMPLITUDE * sin( phase ) * ( spreadfrac + ZOOM_YAW_MIN_AMPLITUDE ); + +} + + + +/* +=============== +CG_OffsetFirstPersonView + +=============== +*/ +static void CG_OffsetFirstPersonView( void ) { + float *origin; + float *angles; + float bob; + float ratio; + float delta; + float speed; + float f; + vec3_t predictedVelocity; + int timeDelta; + qboolean useLastValidBob = qfalse; + + if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { + return; + } + + origin = cg.refdef_current->vieworg; + angles = cg.refdefViewAngles; + + if ( cg.snap->ps.weapon == WP_MOBILE_MG42_SET ) { + float yawDiff = cg.refdefViewAngles[YAW] - cg.pmext.mountedWeaponAngles[YAW]; + vec3_t forward, point; + float oldZ = origin[2]; + + AngleVectors( cg.pmext.mountedWeaponAngles, forward, NULL, NULL ); + + if ( yawDiff > 180 ) { + yawDiff -= 360; + } else if ( yawDiff < -180 ) { + yawDiff += 360; + } + + VectorMA( origin, 31, forward, point ); + AngleVectors( cg.refdefViewAngles, forward, NULL, NULL ); + VectorMA( point, -32, forward, origin ); + + origin[2] = oldZ; + } else if ( cg.snap->ps.weapon == WP_MORTAR_SET ) { + float yawDiff = cg.refdefViewAngles[YAW] - cg.pmext.mountedWeaponAngles[YAW]; + vec3_t forward, point; + float oldZ = origin[2]; + + AngleVectors( cg.pmext.mountedWeaponAngles, forward, NULL, NULL ); + + if ( yawDiff > 180 ) { + yawDiff -= 360; + } else if ( yawDiff < -180 ) { + yawDiff += 360; + } + + VectorMA( origin, 31, forward, point ); + AngleVectors( cg.refdefViewAngles, forward, NULL, NULL ); + VectorMA( point, -32, forward, origin ); + + origin[2] = oldZ; + } + + // if dead, fix the angle and don't add any kick + if ( !( cg.snap->ps.pm_flags & PMF_LIMBO ) && cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { + angles[ROLL] = 40; + angles[PITCH] = -15; + + // rain - #254 - force yaw to 0 if we're tracking a medic + // rain - medic tracking doesn't seem to happen in this case? + if ( cg.snap->ps.viewlocked == 7 ) { + angles[YAW] = 0; + } else { + // rain - do short2angle AFTER the network part + angles[YAW] = SHORT2ANGLE( cg.snap->ps.stats[STAT_DEAD_YAW] ); + } + + origin[2] += cg.predictedPlayerState.viewheight; + return; + } + + // add angles based on weapon kick + VectorAdd( angles, cg.kick_angles, angles ); + + // RF, add new weapon kick angles + CG_KickAngles(); + VectorAdd( angles, cg.kickAngles, angles ); + // RF, pitch is already added + //angles[0] -= cg.kickAngles[PITCH]; + + // add angles based on damage kick + if ( cg.damageTime ) { + ratio = cg.time - cg.damageTime; + if ( ratio < DAMAGE_DEFLECT_TIME ) { + ratio /= DAMAGE_DEFLECT_TIME; + angles[PITCH] += ratio * cg.v_dmg_pitch; + angles[ROLL] += ratio * cg.v_dmg_roll; + } else { + ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME; + if ( ratio > 0 ) { + angles[PITCH] += ratio * cg.v_dmg_pitch; + angles[ROLL] += ratio * cg.v_dmg_roll; + } + } + } + + // add pitch based on fall kick +#if 0 + ratio = ( cg.time - cg.landTime ) / FALL_TIME; + if ( ratio < 0 ) { + ratio = 0; + } + angles[PITCH] += ratio * cg.fall_value; +#endif + + // add angles based on velocity + VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity ); + + delta = DotProduct( predictedVelocity, cg.refdef_current->viewaxis[0] ); + angles[PITCH] += delta * cg_runpitch.value; + + delta = DotProduct( predictedVelocity, cg.refdef_current->viewaxis[1] ); + angles[ROLL] -= delta * cg_runroll.value; + + // add angles based on bob + + // make sure the bob is visible even at low speeds + speed = cg.xyspeed > 200 ? cg.xyspeed : 200; + + if ( !cg.bobfracsin && cg.lastvalidBobfracsin > 0 ) { + // 200 msec to get back to center from 1 + // that's 1/200 per msec = 0.005 per msec + cg.lastvalidBobfracsin -= 0.005 * cg.frametime; + useLastValidBob = qtrue; + } + + delta = useLastValidBob ? cg.lastvalidBobfracsin * cg_bobpitch.value * speed : cg.bobfracsin * cg_bobpitch.value * speed; + if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) { + delta *= 3; // crouching + } + angles[PITCH] += delta; + delta = useLastValidBob ? cg.lastvalidBobfracsin * cg_bobroll.value * speed : cg.bobfracsin * cg_bobroll.value * speed; + if ( cg.predictedPlayerState.pm_flags & PMF_DUCKED ) { + delta *= 3; // crouching accentuates roll + } + if ( useLastValidBob ) { + if ( cg.lastvalidBobcycle & 1 ) { + delta = -delta; + } + } else if ( cg.bobcycle & 1 ) { + delta = -delta; + } + angles[ROLL] += delta; + + /*if (cg.predictedPlayerState.eFlags & EF_PRONE) { + delta = useLastValidBob ? cg.lastvalidBobfracsin * cg_bobyaw.value * speed * 15 : cg.bobfracsin * cg_bobyaw.value * speed * 15; + if( useLastValidBob ) { + if( cg.lastvalidBobcycle & 1 ) + delta = -delta; + } else if (cg.bobcycle & 1) + delta = -delta; + + angles[YAW] += delta; + }*/ + +//=================================== + + // add view height + origin[2] += cg.predictedPlayerState.viewheight; + + // smooth out duck height changes + timeDelta = cg.time - cg.duckTime; + if ( cg.predictedPlayerState.eFlags & EF_PRONE ) { + if ( timeDelta < 0 ) { // Ridah + cg.duckTime = cg.time - PRONE_TIME; + } + if ( timeDelta < PRONE_TIME ) { + cg.refdef_current->vieworg[2] -= cg.duckChange + * ( PRONE_TIME - timeDelta ) / PRONE_TIME; + } + } else { + if ( timeDelta < 0 ) { // Ridah + cg.duckTime = cg.time - DUCK_TIME; + } + if ( timeDelta < DUCK_TIME ) { + cg.refdef_current->vieworg[2] -= cg.duckChange + * ( DUCK_TIME - timeDelta ) / DUCK_TIME; + } + } + + // add bob height + bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value; + if ( bob > 6 ) { + bob = 6; + } + + origin[2] += bob; + + + // add fall height + delta = cg.time - cg.landTime; + if ( delta < 0 ) { // Ridah + cg.landTime = cg.time - ( LAND_DEFLECT_TIME + LAND_RETURN_TIME ); + } + if ( delta < LAND_DEFLECT_TIME ) { + f = delta / LAND_DEFLECT_TIME; + cg.refdef_current->vieworg[2] += cg.landChange * f; + } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { + delta -= LAND_DEFLECT_TIME; + f = 1.0 - ( delta / LAND_RETURN_TIME ); + cg.refdef_current->vieworg[2] += cg.landChange * f; + } + + // add step offset + CG_StepOffset(); + + CG_ZoomSway(); + + // adjust for 'lean' + if ( cg.predictedPlayerState.leanf != 0 ) { + //add leaning offset + vec3_t right; + cg.refdefViewAngles[2] += cg.predictedPlayerState.leanf / 2.0f; + AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); + VectorMA( cg.refdef_current->vieworg, cg.predictedPlayerState.leanf, right, cg.refdef_current->vieworg ); + } + + // add kick offset + + VectorAdd( origin, cg.kick_origin, origin ); + + // pivot the eye based on a neck length +#if 0 + { +#define NECK_LENGTH 8 + vec3_t forward, up; + + cg.refdef_current->vieworg[2] -= NECK_LENGTH; + AngleVectors( cg.refdefViewAngles, forward, NULL, up ); + VectorMA( cg.refdef_current->vieworg, 3, forward, cg.refdef_current->vieworg ); + VectorMA( cg.refdef_current->vieworg, NECK_LENGTH, up, cg.refdef_current->vieworg ); + } +#endif +} + +//====================================================================== + +// +// Zoom controls +// + + +// probably move to server variables +float zoomTable[ZOOM_MAX_ZOOMS][2] = { +// max {out,in} + {0, 0}, + + {36, 8}, // binoc + {20, 4}, // sniper + {60, 20}, // snooper + {55, 55}, // fg42 + {55, 55} // mg42 +}; + +void CG_AdjustZoomVal( float val, int type ) { + cg.zoomval += val; + if ( cg.zoomval > zoomTable[type][ZOOM_OUT] ) { + cg.zoomval = zoomTable[type][ZOOM_OUT]; + } + if ( cg.zoomval < zoomTable[type][ZOOM_IN] ) { + cg.zoomval = zoomTable[type][ZOOM_IN]; + } +} + +void CG_ZoomIn_f( void ) { + // Gordon: fixed being able to "latch" your zoom by weaponcheck + quick zoomin + // OSP - change for zoom view in demos + if ( cg_entities[cg.snap->ps.clientNum].currentState.weapon == WP_GARAND_SCOPE ) { + CG_AdjustZoomVal( -( cg_zoomStepSniper.value ), ZOOM_SNIPER ); + } else if ( cg_entities[cg.snap->ps.clientNum].currentState.weapon == WP_K43_SCOPE ) { + CG_AdjustZoomVal( -( cg_zoomStepSniper.value ), ZOOM_SNIPER ); + } else if ( cg.zoomedBinoc ) { + CG_AdjustZoomVal( -( cg_zoomStepSniper.value ), ZOOM_SNIPER ); // JPW NERVE per atvi request all use same vals to match menu (was zoomStepBinoc, ZOOM_BINOC); + } +} + +void CG_ZoomOut_f( void ) { + if ( cg_entities[cg.snap->ps.clientNum].currentState.weapon == WP_GARAND_SCOPE ) { + CG_AdjustZoomVal( cg_zoomStepSniper.value, ZOOM_SNIPER ); + } else if ( cg_entities[cg.snap->ps.clientNum].currentState.weapon == WP_K43_SCOPE ) { + CG_AdjustZoomVal( cg_zoomStepSniper.value, ZOOM_SNIPER ); + } else if ( cg.zoomedBinoc ) { + CG_AdjustZoomVal( cg_zoomStepSniper.value, ZOOM_SNIPER ); // JPW NERVE per atvi request BINOC); + } +} + + +/* +============== +CG_Zoom +============== +*/ +void CG_Zoom( void ) { + // OSP - Fix for demo playback + if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) { + cg.predictedPlayerState.eFlags = cg.snap->ps.eFlags; + cg.predictedPlayerState.weapon = cg.snap->ps.weapon; + + // check for scope wepon in use, and switch to if necessary + // OSP - spec/demo scaling allowances + if ( cg.predictedPlayerState.weapon == WP_FG42SCOPE ) { + cg.zoomval = ( cg.zoomval == 0 ) ? cg_zoomDefaultSniper.value : cg.zoomval; // JPW NERVE was DefaultFG, changed per atvi req + } else if ( cg.predictedPlayerState.weapon == WP_GARAND_SCOPE ) { + cg.zoomval = ( cg.zoomval == 0 ) ? cg_zoomDefaultSniper.value : cg.zoomval; + } else if ( cg.predictedPlayerState.weapon == WP_K43_SCOPE ) { + cg.zoomval = ( cg.zoomval == 0 ) ? cg_zoomDefaultSniper.value : cg.zoomval; + } else if ( !( cg.predictedPlayerState.eFlags & EF_ZOOMING ) ) { + cg.zoomval = 0; + } + } + if ( cg.predictedPlayerState.eFlags & EF_ZOOMING ) { + if ( cg.zoomedBinoc ) { + return; + } + cg.zoomedBinoc = qtrue; + cg.zoomTime = cg.time; + cg.zoomval = cg_zoomDefaultSniper.value; // JPW NERVE was DefaultBinoc, changed per atvi req + } else { + if ( cg.zoomedBinoc ) { + cg.zoomedBinoc = qfalse; + cg.zoomTime = cg.time; + + // check for scope weapon in use, and switch to if necessary + if ( cg.weaponSelect == WP_FG42SCOPE ) { + cg.zoomval = cg_zoomDefaultSniper.value; // JPW NERVE was DefaultFG, changed per atvi req + } else if ( cg.weaponSelect == WP_GARAND_SCOPE ) { + cg.zoomval = cg_zoomDefaultSniper.value; + } else if ( cg.weaponSelect == WP_K43_SCOPE ) { + cg.zoomval = cg_zoomDefaultSniper.value; + } else { + cg.zoomval = 0; + } + } else { +//bani - we now sanity check to make sure we can't zoom non-zoomable weapons +//zinx - fix for #423 - don't sanity check while following + if ( !( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.demoPlayback ) ) { + switch ( cg.weaponSelect ) { + case WP_FG42SCOPE: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + break; + default: + cg.zoomval = 0; + break; + } + } + } + } +} + + +/* +==================== +CG_CalcFov + +Fixed fov at intermissions, otherwise account for fov variable and zooms. +==================== +*/ +#define WAVE_AMPLITUDE 1 +#define WAVE_FREQUENCY 0.4 + +static int CG_CalcFov( void ) { + static float lastfov = 90; // for transitions back from zoomed in modes + float x; + float phase; + float v; + int contents; + float fov_x, fov_y; + float zoomFov; + float f; + int inwater; + + CG_Zoom(); + + if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { + cg.zoomedBinoc = qfalse; + cg.zoomTime = 0; + cg.zoomval = 0; + } + + if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + // if in intermission, use a fixed value + fov_x = 90; + } else { + fov_x = cg_fov.value; + if ( !developer.integer ) { + if ( fov_x < 90 ) { + fov_x = 90; + } else if ( fov_x > 160 ) { + fov_x = 160; + } + } + + if ( !cg.renderingThirdPerson || developer.integer ) { + // account for zooms + if ( cg.zoomval ) { + zoomFov = cg.zoomval; // (SA) use user scrolled amount + + if ( zoomFov < 1 ) { + zoomFov = 1; + } else if ( zoomFov > 160 ) { + zoomFov = 160; + } + } else { + zoomFov = lastfov; + } + + // do smooth transitions for the binocs + if ( cg.zoomedBinoc ) { // binoc zooming in + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = zoomFov; + } else { + fov_x = fov_x + f * ( zoomFov - fov_x ); + } + lastfov = fov_x; + } else if ( cg.zoomval ) { // zoomed by sniper/snooper + fov_x = cg.zoomval; + lastfov = fov_x; + } else { // binoc zooming out + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = fov_x; + } else { + fov_x = zoomFov + f * ( fov_x - zoomFov ); + } + } + } + } + + cg.refdef_current->rdflags &= ~RDF_SNOOPERVIEW; + + // Arnout: mg42 zoom + if ( cg.snap->ps.persistant[PERS_HWEAPON_USE] ) { + fov_x = 55; + } else if ( cg.snap->ps.weapon == WP_MOBILE_MG42_SET ) { + fov_x = 55; + } else if ( cg.snap->ps.eFlags & EF_MOUNTEDTANK ) { + fov_x = 75; + } + + if ( cg.showGameView ) { + fov_x = fov_y = 60.f; + } + + // Arnout: this is weird... (but ensures square pixel ratio!) + x = cg.refdef_current->width / tan( fov_x / 360 * M_PI ); + fov_y = atan2( cg.refdef_current->height, x ); + fov_y = fov_y * 360 / M_PI; + // And this seems better - but isn't really + //fov_y = fov_x / cgs.glconfig.windowAspect; + + // warp if underwater + //if ( cg_pmove.waterlevel == 3 ) { + contents = CG_PointContents( cg.refdef.vieworg, -1 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; + v = WAVE_AMPLITUDE * sin( phase ); + fov_x += v; + fov_y -= v; + inwater = qtrue; + cg.refdef_current->rdflags |= RDF_UNDERWATER; + } else { + cg.refdef_current->rdflags &= ~RDF_UNDERWATER; + inwater = qfalse; + } + + // set it + cg.refdef_current->fov_x = fov_x; + cg.refdef_current->fov_y = fov_y; + +/* + if( cg.predictedPlayerState.eFlags & EF_PRONE ) { + cg.zoomSensitivity = cg.refdef.fov_y / 500.0; + } else +*/ + // rain - allow freelook when dead until we tap out into limbo + if ( cg.snap->ps.pm_type == PM_FREEZE || ( cg.snap->ps.pm_type == PM_DEAD && ( cg.snap->ps.pm_flags & PMF_LIMBO ) ) || cg.snap->ps.pm_flags & PMF_TIME_LOCKPLAYER ) { + // No movement for pauses + cg.zoomSensitivity = 0; + } else if ( !cg.zoomedBinoc ) { + // NERVE - SMF - fix for zoomed in/out movement bug + if ( cg.zoomval ) { + cg.zoomSensitivity = 0.6 * ( cg.zoomval / 90.f ); // NERVE - SMF - changed to get less sensitive as you zoom in +// cg.zoomSensitivity = 0.1; + } else { + cg.zoomSensitivity = 1; + } + // -NERVE - SMF + } else { + cg.zoomSensitivity = cg.refdef_current->fov_y / 75.0; + } + + return inwater; +} + + +/* +============== +CG_UnderwaterSounds +============== +*/ +#define UNDERWATER_BIT 16 +static void CG_UnderwaterSounds( void ) { +// trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.media.underWaterSound, 255, 0 ); + trap_S_AddLoopingSound( cg.snap->ps.origin, vec3_origin, cgs.media.underWaterSound, 255 | ( 1 << UNDERWATER_BIT ), 0 ); +} + + +/* +=============== +CG_DamageBlendBlob + +=============== +*/ +static void CG_DamageBlendBlob( void ) { + int t,i; + int maxTime; + refEntity_t ent; + qboolean pointDamage; + viewDamage_t *vd; + float redFlash; + + // Gordon: no damage blend blobs if in limbo or spectator, and in the limbo menu + if ( ( cg.snap->ps.pm_flags & PMF_LIMBO || cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR ) && cg.showGameView ) { + return; + } + + // ragePro systems can't fade blends, so don't obscure the screen + if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { + return; + } + + redFlash = 0; + + for ( i = 0; i < MAX_VIEWDAMAGE; i++ ) { + vd = &cg.viewDamage[i]; + + if ( !vd->damageValue ) { + continue; + } + + maxTime = vd->damageDuration; + t = cg.time - vd->damageTime; + if ( t <= 0 || t >= maxTime ) { + vd->damageValue = 0; + continue; + } + + pointDamage = !( !vd->damageX && !vd->damageY ); + + // if not point Damage, only do flash blend + if ( !pointDamage ) { + redFlash += 10.0 * ( 1.0 - (float)t / maxTime ); + continue; + } + + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_SPRITE; + ent.renderfx = RF_FIRST_PERSON; + + VectorMA( cg.refdef_current->vieworg, 8, cg.refdef_current->viewaxis[0], ent.origin ); + VectorMA( ent.origin, vd->damageX * -8, cg.refdef_current->viewaxis[1], ent.origin ); + VectorMA( ent.origin, vd->damageY * 8, cg.refdef_current->viewaxis[2], ent.origin ); + + ent.radius = vd->damageValue * 0.4 * ( 0.5 + 0.5 * (float)t / maxTime ) * ( 0.75 + 0.5 * fabs( sin( vd->damageTime ) ) ); + + ent.customShader = cgs.media.viewBloodAni[(int)( floor( ( (float)t / maxTime ) * 4.9 ) )]; //cgs.media.viewBloodShader; + ent.shaderRGBA[0] = 255; + ent.shaderRGBA[1] = 255; + ent.shaderRGBA[2] = 255; + ent.shaderRGBA[3] = 255 * ( ( cg_bloodDamageBlend.value > 1.0f ) ? 1.0f : + ( cg_bloodDamageBlend.value < 0.0f ) ? 0.0f : cg_bloodDamageBlend.value ); + + trap_R_AddRefEntityToScene( &ent ); + + redFlash += ent.radius; + } +} + +/* +=============== +CG_DrawScreenFade +=============== +*/ +static void CG_DrawScreenFade( void ) { +/* moved over to cg_draw.c + static int lastTime; + int elapsed, time; + refEntity_t ent; + + if (cgs.fadeStartTime + cgs.fadeDuration < cg.time) { + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } else if (cgs.fadeAlphaCurrent != cgs.fadeAlpha) { + elapsed = (time = trap_Milliseconds()) - lastTime; // we need to use trap_Milliseconds() here since the cg.time gets modified upon reloading + lastTime = time; + if (elapsed < 500 && elapsed > 0) { + if (cgs.fadeAlphaCurrent > cgs.fadeAlpha) { + cgs.fadeAlphaCurrent -= ((float)elapsed/(float)cgs.fadeDuration); + if (cgs.fadeAlphaCurrent < cgs.fadeAlpha) + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } else { + cgs.fadeAlphaCurrent += ((float)elapsed/(float)cgs.fadeDuration); + if (cgs.fadeAlphaCurrent > cgs.fadeAlpha) + cgs.fadeAlphaCurrent = cgs.fadeAlpha; + } + } + } + // now draw the fade + if (cgs.fadeAlphaCurrent > 0.0) { + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_SPRITE; + ent.renderfx = RF_FIRST_PERSON; + + VectorMA( cg.refdef_current->vieworg, 8, cg.refdef_current->viewaxis[0], ent.origin ); + ent.radius = 80; // occupy entire screen + ent.customShader = cgs.media.viewFadeBlack; + ent.shaderRGBA[3] = (int)(255.0 * cgs.fadeAlphaCurrent); + + trap_R_AddRefEntityToScene( &ent ); + } +*/ +} + +/* +=============== +CG_CalcViewValues + +Sets cg.refdef view values +=============== +*/ +int CG_CalcViewValues( void ) { + playerState_t *ps; + + memset( cg.refdef_current, 0, sizeof( cg.refdef ) ); + + // strings for in game rendering + // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef_current->text[0]) ); + // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef_current->text[1]) ); + + // calculate size of 3D view + CG_CalcVrect(); + + ps = &cg.predictedPlayerState; + + if ( cg.cameraMode ) { + vec3_t origin, angles; + float fov = 90; + float x; + + if ( trap_getCameraInfo( CAM_PRIMARY, cg.time, &origin, &angles, &fov ) ) { + VectorCopy( origin, cg.refdef_current->vieworg ); + angles[ROLL] = 0; + angles[PITCH] = -angles[PITCH]; // (SA) compensate for reversed pitch (this makes the game match the editor, however I'm guessing the real fix is to be done there) + VectorCopy( angles, cg.refdefViewAngles ); + AnglesToAxis( cg.refdefViewAngles, cg.refdef_current->viewaxis ); + + x = cg.refdef.width / tan( fov / 360 * M_PI ); + cg.refdef_current->fov_y = atan2( cg.refdef_current->height, x ); + cg.refdef_current->fov_y = cg.refdef_current->fov_y * 360 / M_PI; + cg.refdef_current->fov_x = fov; + + // FIXME: this is really really bad + trap_SendClientCommand( va( "setCameraOrigin %f %f %f", origin[0], origin[1], origin[2] ) ); + return 0; + + } else { + cg.cameraMode = qfalse; + trap_Cvar_Set( "cg_letterbox", "0" ); + trap_SendClientCommand( "stopCamera" ); + trap_stopCamera( CAM_PRIMARY ); // camera off in client + + CG_Fade( 0, 0, 0, 255, 0, 0 ); // go black + CG_Fade( 0, 0, 0, 0, cg.time + 200, 1500 ); // then fadeup + } + } + + // intermission view + if ( ps->pm_type == PM_INTERMISSION ) { + VectorCopy( ps->origin, cg.refdef_current->vieworg ); + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + AnglesToAxis( cg.refdefViewAngles, cg.refdef_current->viewaxis ); + return CG_CalcFov(); + } + + if ( cg.bobfracsin > 0 && !ps->bobCycle ) { + cg.lastvalidBobcycle = cg.bobcycle; + cg.lastvalidBobfracsin = cg.bobfracsin; + } + + cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; + cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); + cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); + + + if ( cg.showGameView ) { + VectorCopy( cgs.ccPortalPos, cg.refdef_current->vieworg ); + if ( cg.showGameView && cgs.ccPortalEnt != -1 ) { + vec3_t vec; + VectorSubtract( cg_entities[cgs.ccPortalEnt].lerpOrigin, cg.refdef_current->vieworg, vec ); + vectoangles( vec, cg.refdefViewAngles ); + } else { + VectorCopy( cgs.ccPortalAngles, cg.refdefViewAngles ); + } + } else if ( cg.renderingThirdPerson && ( ps->eFlags & EF_MG42_ACTIVE || ps->eFlags & EF_AAGUN_ACTIVE ) ) { // Arnout: see if we're attached to a gun + centity_t *mg42 = &cg_entities[ps->viewlocked_entNum]; + vec3_t forward; + + AngleVectors( ps->viewangles, forward, NULL, NULL ); + VectorMA( mg42->currentState.pos.trBase, -36, forward, cg.refdef_current->vieworg ); + cg.refdef_current->vieworg[2] = ps->origin[2]; + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + } else if ( ps->eFlags & EF_MOUNTEDTANK ) { + centity_t *tank = &cg_entities[cg_entities[cg.snap->ps.clientNum].tagParent]; + + VectorCopy( tank->mountedMG42Player.origin, cg.refdef_current->vieworg ); + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + } else { + VectorCopy( ps->origin, cg.refdef_current->vieworg ); + VectorCopy( ps->viewangles, cg.refdefViewAngles ); + } + + if ( !cg.showGameView ) { + // add error decay + if ( cg_errorDecay.value > 0 ) { + int t; + float f; + + t = cg.time - cg.predictedErrorTime; + f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; + if ( f > 0 && f < 1 ) { + VectorMA( cg.refdef_current->vieworg, f, cg.predictedError, cg.refdef_current->vieworg ); + } else { + cg.predictedErrorTime = 0; + } + } + + // Ridah, lock the viewangles if the game has told us to + if ( ps->viewlocked ) { + + /* + if (ps->viewlocked == 4) + { + centity_t *tent; + tent = &cg_entities[ps->viewlocked_entNum]; + VectorCopy (tent->currentState.apos.trBase, cg.refdefViewAngles); + } + else + */ + // DHM - Nerve :: don't bother evaluating if set to 7 (look at medic) + if ( ps->viewlocked != 7 && ps->viewlocked != 3 && ps->viewlocked != 2 ) { + BG_EvaluateTrajectory( &cg_entities[ps->viewlocked_entNum].currentState.apos, cg.time, cg.refdefViewAngles, qtrue, cg_entities[ps->viewlocked_entNum].currentState.effect2Time ); + } + + if ( ps->viewlocked == 2 ) { + cg.refdefViewAngles[0] += crandom(); + cg.refdefViewAngles[1] += crandom(); + } + } + + if ( cg.renderingThirdPerson ) { + // back away from character + CG_OffsetThirdPersonView(); + } else { + + // offset for local bobbing and kicks + CG_OffsetFirstPersonView(); + + if ( cg.editingSpeakers ) { + CG_SetViewanglesForSpeakerEditor(); + } + } + + // Ridah, lock the viewangles if the game has told us to + if ( ps->viewlocked == 7 ) { + centity_t *tent; + vec3_t vec; + + tent = &cg_entities[ps->viewlocked_entNum]; + VectorCopy( tent->lerpOrigin, vec ); + VectorSubtract( vec, cg.refdef_current->vieworg, vec ); + vectoangles( vec, cg.refdefViewAngles ); + } else if ( ps->viewlocked == 4 ) { + vec3_t fwd; + AngleVectors( cg.refdefViewAngles, fwd, NULL, NULL ); + VectorMA( cg_entities[ps->viewlocked_entNum].lerpOrigin, 16, fwd, cg.refdef_current->vieworg ); + } else if ( ps->viewlocked ) { + vec3_t fwd; + float oldZ; + // set our position to be behind it + oldZ = cg.refdef_current->vieworg[2]; + AngleVectors( cg.refdefViewAngles, fwd, NULL, NULL ); + if ( cg.predictedPlayerState.eFlags & EF_AAGUN_ACTIVE ) { + VectorMA( cg_entities[ps->viewlocked_entNum].lerpOrigin, 0, fwd, cg.refdef_current->vieworg ); + } else { + VectorMA( cg_entities[ps->viewlocked_entNum].lerpOrigin, -34, fwd, cg.refdef_current->vieworg ); + } + cg.refdef_current->vieworg[2] = oldZ; + } + // done. + } + + // position eye reletive to origin + AnglesToAxis( cg.refdefViewAngles, cg.refdef_current->viewaxis ); + + if ( cg.hyperspace ) { + cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; + } + + // field of view + return CG_CalcFov(); +} + + +//========================================================================= + +char* CG_MustParse( char** pString, const char* pErrorMsg ) { + char* token = COM_Parse( pString ); + if ( !*token ) { + CG_Error( pErrorMsg ); + } + return token; +} + +void CG_ParseSkyBox( void ) { + int fogStart, fogEnd; + char *cstr, *token; + vec4_t fogColor; + + cstr = (char*)CG_ConfigString( CS_SKYBOXORG ); + + if ( !*cstr ) { + cg.skyboxEnabled = qfalse; + return; + } + + token = CG_MustParse( &cstr, "CG_ParseSkyBox: error parsing skybox configstring\n" ); + cg.skyboxViewOrg[0] = atof( token ); + + token = CG_MustParse( &cstr, "CG_ParseSkyBox: error parsing skybox configstring\n" ); + cg.skyboxViewOrg[1] = atof( token ); + + token = CG_MustParse( &cstr, "CG_ParseSkyBox: error parsing skybox configstring\n" ); + cg.skyboxViewOrg[2] = atof( token ); + + token = CG_MustParse( &cstr, "CG_ParseSkyBox: error parsing skybox configstring\n" ); + cg.skyboxViewFov = atoi( token ); + + if ( !cg.skyboxViewFov ) { + cg.skyboxViewFov = 90; + } + + // setup fog the first time, ignore this part of the configstring after that + token = CG_MustParse( &cstr, "CG_ParseSkyBox: error parsing skybox configstring. No fog state\n" ); + if ( atoi( token ) ) { // this camera has fog + token = CG_MustParse( &cstr, "CG_DrawSkyBoxPortal: error parsing skybox configstring. No fog[0]\n" ); + fogColor[0] = atof( token ); + + token = CG_MustParse( &cstr, "CG_DrawSkyBoxPortal: error parsing skybox configstring. No fog[1]\n" ); + fogColor[1] = atof( token ); + + token = CG_MustParse( &cstr, "CG_DrawSkyBoxPortal: error parsing skybox configstring. No fog[2]\n" ); + fogColor[2] = atof( token ); + + token = COM_ParseExt( &cstr, qfalse ); + fogStart = atoi( token ); + + token = COM_ParseExt( &cstr, qfalse ); + fogEnd = atoi( token ); + + trap_R_SetFog( FOG_PORTALVIEW, fogStart, fogEnd, fogColor[0], fogColor[1], fogColor[2], 1.1f ); + } else { + trap_R_SetFog( FOG_PORTALVIEW, 0, 0, 0, 0, 0, 0 ); // init to null + } + + cg.skyboxEnabled = qtrue; +} + +/* +============== +CG_ParseTagConnects +============== +*/ + +void CG_ParseTagConnects( void ) { + int i; + + for ( i = CS_TAGCONNECTS; i < CS_TAGCONNECTS + MAX_TAGCONNECTS; i++ ) { + CG_ParseTagConnect( i ); + } +} + +void CG_ParseTagConnect( int tagNum ) { + char *token, *pString = (char*)CG_ConfigString( tagNum ); // Gordon: bleh, i hate that cast away of the const + int entNum; + + if ( !*pString ) { + return; + } + + token = CG_MustParse( &pString, "Invalid TAGCONNECT configstring\n" ); + + entNum = atoi( token ); + if ( entNum < 0 || entNum >= MAX_GENTITIES ) { + CG_Error( "Invalid TAGCONNECT entitynum\n" ); + } + + token = CG_MustParse( &pString, "Invalid TAGCONNECT configstring\n" ); + + cg_entities[entNum].tagParent = atoi( token ); + if ( cg_entities[entNum].tagParent < 0 || cg_entities[entNum].tagParent >= MAX_GENTITIES ) { + CG_Error( "Invalid TAGCONNECT tagparent\n" ); + } + + token = CG_MustParse( &pString, "Invalid TAGCONNECT configstring\n" ); + Q_strncpyz( cg_entities[entNum].tagName, token, MAX_QPATH ); +} + +/* +============== +CG_DrawSkyBoxPortal +============== +*/ +void CG_DrawSkyBoxPortal( qboolean fLocalView ) { + refdef_t rd; + static float lastfov = 90; // for transitions back from zoomed in modes + + + if ( !cg_skybox.integer || !cg.skyboxEnabled ) { + return; + } + + memcpy( &rd, cg.refdef_current, sizeof( refdef_t ) ); + VectorCopy( cg.skyboxViewOrg, rd.vieworg ); + +// Updates for window views... remove me when things have been verified +#if 0 + fov_x = cg.skyboxViewFov; + + if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + // if in intermission, use a fixed value + fov_x = 90; + } else { + // user selectable + fov_x = cg_fov.value; + if ( fov_x < 1 ) { + fov_x = 1; + } else if ( fov_x > 160 ) { + fov_x = 160; + } + + // account for zooms + if ( cg.zoomval ) { + zoomFov = cg.zoomval; // (SA) use user scrolled amount + + if ( zoomFov < 1 ) { + zoomFov = 1; + } else if ( zoomFov > 160 ) { + zoomFov = 160; + } + } else { + zoomFov = lastfov; + } + + // do smooth transitions for the binocs + if ( cg.zoomedBinoc ) { // binoc zooming in + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = zoomFov; + } else { + fov_x = fov_x + f * ( zoomFov - fov_x ); + } + lastfov = fov_x; + } else if ( cg.zoomval ) { // zoomed by sniper/snooper + fov_x = cg.zoomval; + lastfov = fov_x; + } else { // binoc zooming out + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + if ( f > 1.0 ) { + fov_x = fov_x; + } else { + fov_x = zoomFov + f * ( fov_x - zoomFov ); + } + } + } + + cg.refdef_current->rdflags &= ~RDF_SNOOPERVIEW; + + // Arnout: mg42 zoom + if ( cg.snap->ps.persistant[PERS_HWEAPON_USE] || cg.predictedPlayerState.pm_flags & PMF_PRONE_BIPOD ) { + fov_x = 55; + } + + cg.refdef_current->time = cg.time; + + x = cg.refdef_current->width / tan( fov_x / 360 * M_PI ); + fov_y = atan2( cg.refdef_current->height, x ); + fov_y = fov_y * 360 / M_PI; + + cg.refdef_current->fov_x = fov_x; + cg.refdef_current->fov_y = fov_y; + + cg.refdef_current->rdflags |= RDF_SKYBOXPORTAL; + + // draw the skybox + trap_R_RenderScene( cg.refdef_current ); + + cg.refdef = backuprefdef; +#endif + + if ( fLocalView ) { + float fov_x; + float fov_y; + float x; + float zoomFov; + float f; + + if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { + // if in intermission, use a fixed value + fov_x = 90; + } else { + // user selectable + fov_x = cg_fov.value; + if ( fov_x < 1 ) { + fov_x = 1; + } else if ( fov_x > 160 ) { + fov_x = 160; + } + + // account for zooms + if ( cg.zoomval ) { + zoomFov = cg.zoomval; // (SA) use user scrolled amount + if ( zoomFov < 1 ) { + zoomFov = 1; + } else if ( zoomFov > 160 ) { + zoomFov = 160; + } + } else { + zoomFov = lastfov; + } + + // do smooth transitions for the binocs + if ( cg.zoomedBinoc ) { // binoc zooming in + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + fov_x = ( f > 1.0 ) ? zoomFov : fov_x + f * ( zoomFov - fov_x ); + lastfov = fov_x; + } else if ( cg.zoomval ) { // zoomed by sniper/snooper + fov_x = cg.zoomval; + lastfov = fov_x; + } else { // binoc zooming out + f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; + fov_x = ( f > 1.0 ) ? fov_x : zoomFov + f * ( fov_x - zoomFov ); + } + } + + rd.rdflags &= ~RDF_SNOOPERVIEW; + + if ( BG_PlayerMounted( cg.snap->ps.eFlags ) || cg.predictedPlayerState.weapon == WP_MOBILE_MG42_SET ) { + fov_x = 55; + } + + x = rd.width / tan( fov_x / 360 * M_PI ); + fov_y = atan2( rd.height, x ); + fov_y = fov_y * 360 / M_PI; + + rd.fov_x = fov_x; + rd.fov_y = fov_y; + } + + rd.time = cg.time; + rd.rdflags |= RDF_SKYBOXPORTAL; + + // draw the skybox + trap_R_RenderScene( &rd ); +} + +//========================================================================= + +/* +** Frustum code +*/ + +// some culling bits +typedef struct plane_s { + vec3_t normal; + float dist; +} plane_t; + +static plane_t frustum[4]; + +// +// CG_SetupFrustum +// +void CG_SetupFrustum( void ) { + int i; + float xs, xc; + float ang; + + ang = cg.refdef_current->fov_x / 180 * M_PI * 0.5f; + xs = sin( ang ); + xc = cos( ang ); + + VectorScale( cg.refdef_current->viewaxis[0], xs, frustum[0].normal ); + VectorMA( frustum[0].normal, xc, cg.refdef_current->viewaxis[1], frustum[0].normal ); + + VectorScale( cg.refdef_current->viewaxis[0], xs, frustum[1].normal ); + VectorMA( frustum[1].normal, -xc, cg.refdef_current->viewaxis[1], frustum[1].normal ); + + ang = cg.refdef.fov_y / 180 * M_PI * 0.5f; + xs = sin( ang ); + xc = cos( ang ); + + VectorScale( cg.refdef_current->viewaxis[0], xs, frustum[2].normal ); + VectorMA( frustum[2].normal, xc, cg.refdef_current->viewaxis[2], frustum[2].normal ); + + VectorScale( cg.refdef_current->viewaxis[0], xs, frustum[3].normal ); + VectorMA( frustum[3].normal, -xc, cg.refdef_current->viewaxis[2], frustum[3].normal ); + + for ( i = 0 ; i < 4 ; i++ ) { + frustum[i].dist = DotProduct( cg.refdef_current->vieworg, frustum[i].normal ); + } +} + +// +// CG_CullPoint - returns true if culled +// +qboolean CG_CullPoint( vec3_t pt ) { + int i; + plane_t *frust; + + // check against frustum planes + for ( i = 0 ; i < 4 ; i++ ) { + frust = &frustum[i]; + + if ( ( DotProduct( pt, frust->normal ) - frust->dist ) < 0 ) { + return( qtrue ); + } + } + + return( qfalse ); +} + +qboolean CG_CullPointAndRadius( const vec3_t pt, vec_t radius ) { + int i; + plane_t *frust; + + // check against frustum planes + for ( i = 0 ; i < 4 ; i++ ) { + frust = &frustum[i]; + + if ( ( DotProduct( pt, frust->normal ) - frust->dist ) < -radius ) { + return( qtrue ); + } + } + + return( qfalse ); +} + +//========================================================================= + +extern void CG_SetupDlightstyles( void ); + + +//#define DEBUGTIME_ENABLED +#ifdef DEBUGTIME_ENABLED +#define DEBUGTIME elapsed = ( trap_Milliseconds() - dbgTime ); if ( dbgCnt++ == 1 ) {CG_Printf( "t%i:%i ", dbgCnt, elapsed = ( trap_Milliseconds() - dbgTime ) ); } dbgTime += elapsed; +#else +#define DEBUGTIME +#endif + +#ifdef _DEBUG +//#define FAKELAG +#ifdef FAKELAG +extern int snapshotDelayTime; +#endif // FAKELAG +#endif // _DEBUG + +/* +================= +CG_DrawActiveFrame + +Generates and draws a game scene and status information at the given time. +================= +*/ + +//static int lightningtime = 0; +//static int lightningsequencetime = 0; +//static int lightningsequencecounter = 0; + +qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ); + +void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) { + int inwater; + +#ifdef DEBUGTIME_ENABLED + int dbgTime = trap_Milliseconds(),elapsed; + int dbgCnt = 0; +#endif + + cg.time = serverTime; + cgDC.realTime = cg.time; + cg.demoPlayback = demoPlayback; + +#ifdef FAKELAG + cg.time -= snapshotDelayTime; +#endif // _DEBUG + + +#ifdef DEBUGTIME_ENABLED + CG_Printf( "\n" ); +#endif + DEBUGTIME + + // update cvars + CG_UpdateCvars(); + + DEBUGTIME + + // if we are only updating the screen as a loading + // pacifier, don't even try to read snapshots + if ( cg.infoScreenText[0] != 0 ) { + CG_DrawInformation( qfalse ); + return; + } + + CG_PB_ClearPolyBuffers(); + + CG_UpdatePMLists(); + + // any looped sounds will be respecified as entities + // are added to the render list + trap_S_ClearLoopingSounds(); + + CG_UpdateBufferedSoundScripts(); + + DEBUGTIME + + // set up cg.snap and possibly cg.nextSnap + CG_ProcessSnapshots(); + + DEBUGTIME + + // if we haven't received any snapshots yet, all + // we can draw is the information screen + if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) { + CG_DrawInformation( qfalse ); + return; + } + + // check for server set weapons we might not know about + // (FIXME: this is a hack for the time being since a scripted "selectweapon" does + // not hit the first snap, the server weapon set in cg_playerstate.c line 219 doesn't + // do the trick) + if ( !cg.weaponSelect && cg.snap->ps.weapon ) { + cg.weaponSelect = cg.snap->ps.weapon; + cg.weaponSelectTime = cg.time; + } + + if ( cg.weaponSelect == WP_FG42SCOPE ) { + float spd; + spd = VectorLength( cg.snap->ps.velocity ); + if ( spd > 180.0f ) { + CG_FinishWeaponChange( WP_FG42SCOPE, WP_FG42 ); + } + } + + DEBUGTIME + + if ( !cg.lightstylesInited ) { + CG_SetupDlightstyles(); + } + + DEBUGTIME + + // if we have been told not to render, don't + if ( cg_norender.integer ) { + return; + } + + // this counter will be bumped for every valid scene we generate + cg.clientFrame++; + + // update cg.predictedPlayerState + CG_PredictPlayerState(); + + DEBUGTIME + + + // OSP -- MV handling + if ( cg.mvCurrentMainview != NULL && cg.snap->ps.pm_type != PM_INTERMISSION ) { + CG_mvDraw( cg.mvCurrentMainview ); + // FIXME: not valid for demo playback + cg.zoomSensitivity = mv_sensitivity.value / int_sensitivity.value; + } else { + // clear all the render lists + trap_R_ClearScene(); + + DEBUGTIME + + // decide on third person view + cg.renderingThirdPerson = cg_thirdPerson.integer || ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) || cg.showGameView; + + // build cg.refdef + inwater = CG_CalcViewValues(); + CG_SetupFrustum(); + + DEBUGTIME + + // RF, draw the skyboxportal + CG_DrawSkyBoxPortal( qtrue ); + + DEBUGTIME + + if ( inwater ) { + CG_UnderwaterSounds(); + } + + DEBUGTIME + + // first person blend blobs, done after AnglesToAxis + if ( !cg.renderingThirdPerson ) { + CG_DamageBlendBlob(); + } + + DEBUGTIME + + // build the render lists + if ( !cg.hyperspace ) { + CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct + CG_AddMarks(); + + DEBUGTIME + + CG_AddScriptSpeakers(); + + DEBUGTIME + + // Rafael particles + CG_AddParticles(); + // done. + + DEBUGTIME + + CG_AddLocalEntities(); + + DEBUGTIME + + CG_AddSmokeSprites(); + + DEBUGTIME + + CG_AddAtmosphericEffects(); + } + + // Rafael mg42 + if ( !cg.showGameView && !cgs.dbShowing ) { + if ( !cg.snap->ps.persistant[PERS_HWEAPON_USE] ) { + CG_AddViewWeapon( &cg.predictedPlayerState ); + } else { + if ( cg.time - cg.predictedPlayerEntity.overheatTime < 3000 ) { + vec3_t muzzle; + + CG_CalcMuzzlePoint( cg.snap->ps.clientNum, muzzle ); + + muzzle[2] -= 32; + + if ( !( rand() % 3 ) ) { + float alpha; + alpha = 1.0f - ( (float)( cg.time - cg.predictedPlayerEntity.overheatTime ) / 3000.0f ); + alpha *= 0.25f; // .25 max alpha + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, muzzle, 1000, 8, 20, 30, alpha, 8.f ); + } + } + } + } + + // NERVE - SMF - play buffered voice chats + CG_PlayBufferedVoiceChats(); + + DEBUGTIME + // Ridah, trails + if ( !cg.hyperspace ) { + CG_AddFlameChunks(); + CG_AddTrails(); // this must come last, so the trails dropped this frame get drawn + } + // done. + + DEBUGTIME + + // finish up the rest of the refdef + if ( cg.testModelEntity.hModel ) { + CG_AddTestModel(); + } + cg.refdef.time = cg.time; + memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); + + DEBUGTIME + + // warning sounds when powerup is wearing off + //CG_PowerupTimerSounds(); + + // make sure the lagometerSample and frame timing isn't done twice when in stereo + if ( stereoView != STEREO_RIGHT ) { + cg.frametime = cg.time - cg.oldTime; + if ( cg.frametime < 0 ) { + cg.frametime = 0; + } + cg.oldTime = cg.time; + CG_AddLagometerFrameInfo(); + } + + DEBUGTIME + + // Ridah, fade the screen + CG_DrawScreenFade(); + + DEBUGTIME + + // DHM - Nerve :: let client system know our predicted origin + trap_SetClientLerpOrigin( cg.refdef.vieworg[0], cg.refdef.vieworg[1], cg.refdef.vieworg[2] ); + + // actually issue the rendering calls + CG_DrawActive( stereoView ); + + DEBUGTIME + + // update audio positions + trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater ); + } + + if ( cg_stats.integer ) { + CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame ); + } + + DEBUGTIME + + // let the client system know what our weapon, holdable item and zoom settings are + trap_SetUserCmdValue( cg.weaponSelect, cg.showGameView ? 0x01 : 0x00, cg.zoomSensitivity, cg.identifyClientRequest ); +} + diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c new file mode 100644 index 0000000..f08fbee --- /dev/null +++ b/src/cgame/cg_weapons.c @@ -0,0 +1,6164 @@ +/* +=========================================================================== + +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: cg_weapons.c + * + * desc: events and effects dealing with weapons + * +*/ + +#include "cg_local.h" + +vec3_t ejectBrassCasingOrigin; + +//----(SA) +// forward decs +static int getAltWeapon( int weapnum ); +int getEquivWeapon( int weapnum ); +int CG_WeaponIndex( int weapnum, int *bank, int *cycle ); +static qboolean CG_WeaponHasAmmo( int i ); +char cg_fxflags; +extern int weapBanksMultiPlayer[MAX_WEAP_BANKS_MP][MAX_WEAPS_IN_BANK_MP]; // JPW NERVE moved to bg_misc.c so I can get a droplist +// jpw + +//----(SA) end + +/* +============== +CG_StartWeaponAnim +============== +*/ +static void CG_StartWeaponAnim( int anim ) { + if ( cg.predictedPlayerState.pm_type >= PM_DEAD ) { + return; + } + + if ( cg.pmext.weapAnimTimer > 0 ) { + return; + } + + if ( cg.predictedPlayerState.weapon == WP_NONE ) { + return; + } + + cg.predictedPlayerState.weapAnim = ( ( cg.predictedPlayerState.weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; +} + +static void CG_ContinueWeaponAnim( int anim ) { + if ( cg.predictedPlayerState.weapon == WP_NONE ) { + return; + } + + if ( ( cg.predictedPlayerState.weapAnim & ~ANIM_TOGGLEBIT ) == anim ) { + return; + } + if ( cg.pmext.weapAnimTimer > 0 ) { + return; // a high priority animation is running + } + CG_StartWeaponAnim( anim ); +} + +/* +============== +CG_MachineGunEjectBrassNew +============== +*/ +void CG_MachineGunEjectBrassNew( centity_t *cent ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, xvelocity; + float waterScale = 1.0f; + vec3_t v[3]; + + if ( cg_brassTime.integer <= 0 ) { + return; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + velocity[0] = -50 + 25 * crandom(); // JPW NERVE + velocity[1] = -100 + 40 * crandom(); // JPW NERVE + velocity[2] = 200 + 50 * random(); // JPW NERVE + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time - ( rand() & 15 ); + + AnglesToAxis( cent->lerpAngles, v ); + + VectorCopy( ejectBrassCasingOrigin, re->origin ); + + VectorCopy( re->origin, le->pos.trBase ); + + if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) { //----(SA) modified since slime is no longer deadly +// if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { + waterScale = 0.10; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + re->hModel = cgs.media.smallgunBrassModel; + + le->bounceFactor = 0.4 * waterScale; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + le->angles.trBase[0] = ( rand() & 31 ) + 60; // bullets should come out horizontal not vertical JPW NERVE + le->angles.trBase[1] = rand() & 255; // random spin from extractor + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = 2; + le->angles.trDelta[1] = 1; + le->angles.trDelta[2] = 0; + + le->leFlags = LEF_TUMBLE; + + + { + int contents; + vec3_t end; + VectorCopy( cent->lerpOrigin, end ); + end[2] -= 24; + contents = CG_PointContents( end, 0 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + le->leBounceSoundType = LEBS_NONE; + } else { + le->leBounceSoundType = LEBS_BRASS; + } + } + + le->leMarkType = LEMT_NONE; +} + +/* +========================== +CG_MachineGunEjectBrass +========================== +*/ + +void CG_MachineGunEjectBrass( centity_t *cent ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, xvelocity; + vec3_t offset, xoffset; + float waterScale = 1.0f; + vec3_t v[3]; + + if ( cg_brassTime.integer <= 0 ) { + return; + } + + if ( !( cg.snap->ps.persistant[PERS_HWEAPON_USE] ) && ( cent->currentState.clientNum == cg.snap->ps.clientNum ) && ( !( cent->currentState.eFlags & EF_MG42_ACTIVE || cent->currentState.eFlags & EF_AAGUN_ACTIVE ) ) ) { + CG_MachineGunEjectBrassNew( cent ); + return; + } + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; + le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time - ( rand() & 15 ); + + AnglesToAxis( cent->lerpAngles, v ); + +// JPW NERVE new brass handling behavior because the SP stuff just doesn't cut it for MP + if ( cent->currentState.eFlags & EF_MG42_ACTIVE || cent->currentState.eFlags & EF_AAGUN_ACTIVE ) { + offset[0] = 25; + offset[1] = -4; + offset[2] = 28; + velocity[0] = -20 + 40 * crandom(); // JPW NERVE -- more reasonable brass ballistics for a machinegun + velocity[1] = -150 + 40 * crandom(); // JPW NERVE + velocity[2] = 100 + 50 * crandom(); // JPW NERVE + re->hModel = cgs.media.machinegunBrassModel; + le->angles.trBase[0] = 90; //rand()&31; // JPW NERVE belt-fed rounds should come out horizontal + le->angles.trBase[1] = rand() & 255; + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = 2; + le->angles.trDelta[1] = 1; + le->angles.trDelta[2] = 0; + } else { + re->hModel = cgs.media.smallgunBrassModel; + switch ( cent->currentState.weapon ) { + case WP_LUGER: + case WP_COLT: + case WP_SILENCER: + case WP_SILENCED_COLT: + offset[0] = 24; + offset[1] = -4; + offset[2] = 36; + break; + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + offset[0] = 12; + offset[1] = -4; + offset[2] = 24; + re->hModel = cgs.media.machinegunBrassModel; + break; + case WP_KAR98: + case WP_CARBINE: + case WP_K43: + re->hModel = cgs.media.machinegunBrassModel; + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + default: + offset[0] = 16; + offset[1] = -4; + offset[2] = 24; + break; + } + velocity[0] = -50 + 25 * crandom(); + velocity[1] = -100 + 40 * crandom(); + velocity[2] = 200 + 50 * random(); + le->angles.trBase[0] = ( rand() & 15 ) + 82; // bullets should come out horizontal not vertical JPW NERVE + le->angles.trBase[1] = rand() & 255; // random spin from extractor + le->angles.trBase[2] = rand() & 31; + le->angles.trDelta[0] = 2; + le->angles.trDelta[1] = 1; + le->angles.trDelta[2] = 0; + } +// jpw + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, re->origin ); + + VectorCopy( re->origin, le->pos.trBase ); + + if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) { //----(SA) modified since slime is no longer deadly +// if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { + waterScale = 0.10; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + + le->bounceFactor = 0.4 * waterScale; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; + + le->leFlags = LEF_TUMBLE; + + { + int contents; + vec3_t end; + VectorCopy( cent->lerpOrigin, end ); + end[2] -= 24; + contents = CG_PointContents( end, 0 ); + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + le->leBounceSoundType = LEBS_NONE; + } else { + le->leBounceSoundType = LEBS_BRASS; + } + } + + le->leMarkType = LEMT_NONE; +} + + +//----(SA) added +/* +============== +CG_PanzerFaustEjectBrass + toss the 'used' panzerfaust casing (unit is one-shot, disposable) +============== +*/ +static void CG_PanzerFaustEjectBrass( centity_t *cent ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, xvelocity; + vec3_t offset, xoffset; + float waterScale = 1.0f; + vec3_t v[3]; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + +// velocity[0] = 16; +// velocity[1] = -50 + 40 * crandom(); +// velocity[2] = 100 + 50 * crandom(); + + velocity[0] = 16; + velocity[1] = -200; + velocity[2] = 0; + + le->leType = LE_FRAGMENT; + le->startTime = cg.time; +// le->startTime = cg.time + 2000; + le->endTime = le->startTime + ( cg_brassTime.integer * 8 ) + ( cg_brassTime.integer * random() ); + + le->pos.trType = TR_GRAVITY; + le->pos.trTime = cg.time - ( rand() & 15 ); +// le->pos.trTime = cg.time - 2000; + + AnglesToAxis( cent->lerpAngles, v ); + +// offset[0] = 12; +// offset[1] = -4; +// offset[2] = 24; + + offset[0] = -24; // forward + offset[1] = -4; // left + offset[2] = 24; // up + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, re->origin ); + + VectorCopy( re->origin, le->pos.trBase ); + + if ( CG_PointContents( re->origin, -1 ) & ( CONTENTS_WATER | CONTENTS_SLIME ) ) { + waterScale = 0.10; + } + + xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; + xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; + xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; + VectorScale( xvelocity, waterScale, le->pos.trDelta ); + + AxisCopy( axisDefault, re->axis ); + + // (SA) make it bigger + le->sizeScale = 3.0f; + + re->hModel = cgs.media.panzerfaustBrassModel; + + le->bounceFactor = 0.4 * waterScale; + + le->angles.trType = TR_LINEAR; + le->angles.trTime = cg.time; +// le->angles.trBase[0] = rand()&31; +// le->angles.trBase[1] = rand()&31; +// le->angles.trBase[2] = rand()&31; + le->angles.trBase[0] = 0; + le->angles.trBase[1] = cent->currentState.apos.trBase[1]; // rotate to match the player + le->angles.trBase[2] = 0; +// le->angles.trDelta[0] = 2; +// le->angles.trDelta[1] = 1; +// le->angles.trDelta[2] = 0; + le->angles.trDelta[0] = 0; + le->angles.trDelta[1] = 0; + le->angles.trDelta[2] = 0; + + le->leFlags = LEF_TUMBLE | LEF_SMOKING; // (SA) probably doesn't need to be 'tumble' since it doesn't really rotate much when flying + + le->leBounceSoundType = LEBS_NONE; + + le->leMarkType = LEMT_NONE; +} +/* +============== +CG_SpearTrail + simple bubble trail behind a missile +============== +*/ +/*void CG_SpearTrail(centity_t *ent, const weaponInfo_t *wi ) +{ + int contents, lastContents; + vec3_t origin, lastPos; + entityState_t *es; + + es = &ent->currentState; + BG_EvaluateTrajectory( &es->pos, cg.time, origin ); + contents = CG_PointContents( origin, -1 ); + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + if ( contents & lastContents & CONTENTS_WATER ) { + CG_BubbleTrail( lastPos, origin, 1, 8 ); + } + } +}*/ + +// JPW NERVE -- compute random wind vector for smoke puff +/* +========================== +CG_GetWindVector +========================== +*/ +void CG_GetWindVector( vec3_t dir ) { + dir[0] = random() * 0.25; + dir[1] = cgs.smokeWindDir; // simulate a little wind so it looks natural + dir[2] = random(); // one direction (so smoke goes side-like) + VectorNormalize( dir ); +} +// jpw + +// JPW NERVE -- LT pyro for marking air strikes +/* +========================== +CG_PyroSmokeTrail +========================== +*/ +void CG_PyroSmokeTrail( centity_t *ent, const weaponInfo_t *wi ) { + int step; + vec3_t origin, lastPos, dir; + int contents; + int lastContents, startTime; + entityState_t *es; + int t; + float rnd; + localEntity_t *le; + team_t team; + + if ( ent->currentState.weapon == WP_LANDMINE ) { + if ( ent->currentState.teamNum < 8 ) { + ent->miscTime = 0; + return; + } else if ( ent->currentState.teamNum < 12 ) { + if ( !ent->miscTime ) { + ent->trailTime = cg.time; + ent->miscTime = cg.time; + + // Arnout: play the armed sound - weird place to do it but saves us sending an event + trap_S_StartSound( NULL, ent->currentState.number, CHAN_WEAPON, cgs.media.minePrimedSound ); + } + } + + if ( cg.time - ent->miscTime > 1000 ) { + return; + } + + if ( ent->currentState.otherEntityNum2 ) { + team = TEAM_AXIS; + } else { + team = TEAM_ALLIES; + } + } else { + team = ent->currentState.teamNum; + } + + step = 30; + es = &ent->currentState; + startTime = ent->trailTime; + t = step * ( ( startTime + step ) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin, qfalse, es->effect2Time ); + contents = CG_PointContents( origin, -1 ); + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos, qfalse, es->effect2Time ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + +/* smoke pyro works fine in water (well, it's dye in real life, might wanna change this in-game) + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) + return; +*/ + + // drop fire trail sprites + for ( ; t <= ent->trailTime ; t += step ) { + + BG_EvaluateTrajectory( &es->pos, t, lastPos, qfalse, es->effect2Time ); + rnd = random(); + + //VectorCopy (ent->lerpOrigin, lastPos); + + if ( ent->currentState.density ) { // corkscrew effect + vec3_t right; + vec3_t angles; + VectorCopy( ent->currentState.apos.trBase, angles ); + angles[ROLL] += cg.time % 360; + AngleVectors( angles, NULL, right, NULL ); + VectorMA( lastPos, ent->currentState.density, right, lastPos ); + } + + dir[0] = crandom() * 5; // compute offset from flare base + dir[1] = crandom() * 5; + dir[2] = 0; + VectorAdd( lastPos,dir,origin ); // store in origin + + rnd = random(); + + CG_GetWindVector( dir ); + if ( ent->currentState.weapon == WP_LANDMINE ) { + VectorScale( dir,45,dir ); + } else { + VectorScale( dir,65,dir ); + } + + if ( team == TEAM_ALLIES ) { // allied team, generate blue smoke + le = CG_SmokePuff( origin, dir, + 25 + rnd * 110, // width + rnd * 0.5 + 0.5, rnd * 0.5 + 0.5, 1, 0.5, + 4800 + ( rand() % 2800 ), // duration was 2800+ + t, + 0, + 0, + cgs.media.smokePuffShader ); + } else { + le = CG_SmokePuff( origin, dir, + 25 + rnd * 110, // width + 1.0, rnd * 0.5 + 0.5, rnd * 0.5 + 0.5, 0.5, + 4800 + ( rand() % 2800 ), // duration was 2800+ + t, + 0, + 0, + cgs.media.smokePuffShader ); + } +// CG_ParticleExplosion( "expblue", lastPos, vec3_origin, 100 + (int)(rnd*400), 4, 4 ); // fire "flare" + + + // use the optimized local entity add +// le->leType = LE_SCALE_FADE; +/* this one works + if (rand()%4) + CG_ParticleExplosion( "blacksmokeanim", origin, dir, 2800+(int)(random()*1500), 15, 45+(int)(rnd*90) ); // smoke blacksmokeanim + else + CG_ParticleExplosion( "expblue", lastPos, vec3_origin, 100 + (int)(rnd*400), 4, 4 ); // fire "flare" +*/ + } +} +// jpw + + +// Ridah, new trail effects +/* +========================== +CG_RocketTrail +========================== +*/ +void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi ) { + int step; + vec3_t origin, lastPos; + int contents; + int lastContents, startTime; + entityState_t *es; + int t; +// localEntity_t *le; + + if ( ent->currentState.eType == ET_FLAMEBARREL ) { + step = 30; + } else if ( ent->currentState.eType == ET_FP_PARTS ) { + step = 50; + } else if ( ent->currentState.eType == ET_RAMJET ) { + step = 10; + } else { + step = 10; + } + + es = &ent->currentState; + startTime = ent->trailTime; + t = step * ( ( startTime + step ) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin, qfalse, es->effect2Time ); + contents = CG_PointContents( origin, -1 ); + + // if object (e.g. grenade) is stationary, don't toss up smoke + if ( ( ent->currentState.eType != ET_RAMJET ) && es->pos.trType == TR_STATIONARY ) { + ent->trailTime = cg.time; + return; + } + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos, qfalse, es->effect2Time ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + if ( contents & lastContents & CONTENTS_WATER ) { + CG_BubbleTrail( lastPos, origin, 3, 8 ); + } + return; + } + + // drop fire trail sprites + for ( ; t <= ent->trailTime ; t += step ) { + float rnd; + + BG_EvaluateTrajectory( &es->pos, t, lastPos, qfalse, es->effect2Time ); + rnd = random(); + if ( ent->currentState.eType == ET_FLAMEBARREL ) { + if ( ( rand() % 100 ) > 50 ) { + CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, 100 + (int)( rnd * 400 ), 5, 7 + (int)( rnd * 10 ), qfalse ); // fire + + } + CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ), qfalse ); // smoke + } else if ( ent->currentState.eType == ET_FP_PARTS ) { + if ( ( rand() % 100 ) > 50 ) { + CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, 100 + (int)( rnd * 400 ), 5, 7 + (int)( rnd * 10 ), qfalse ); // fire + + } + CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ), qfalse ); // smoke + } else if ( ent->currentState.eType == ET_RAMJET ) { + int duration; + + VectorCopy( ent->lerpOrigin, lastPos ); + duration = 100; + CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, duration + (int)( rnd * 100 ), 5, 5 + (int)( rnd * 10 ), qfalse ); // fire + CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 400 + (int)( rnd * 750 ), 12, 24 + (int)( rnd * 30 ), qfalse ); // smoke + } else if ( ent->currentState.eType == ET_FIRE_COLUMN || ent->currentState.eType == ET_FIRE_COLUMN_SMOKE ) { + int duration; + int sizeStart; + int sizeEnd; + + //VectorCopy (ent->lerpOrigin, lastPos); + + if ( ent->currentState.density ) { // corkscrew effect + vec3_t right; + vec3_t angles; + VectorCopy( ent->currentState.apos.trBase, angles ); + angles[ROLL] += cg.time % 360; + AngleVectors( angles, NULL, right, NULL ); + VectorMA( lastPos, ent->currentState.density, right, lastPos ); + } + + duration = ent->currentState.angles[0]; + sizeStart = ent->currentState.angles[1]; + sizeEnd = ent->currentState.angles[2]; + + if ( !duration ) { + duration = 100; + } + + if ( !sizeStart ) { + sizeStart = 5; + } + + if ( !sizeEnd ) { + sizeEnd = 7; + } + + CG_ParticleExplosion( "twiltb2", lastPos, vec3_origin, duration + (int)( rnd * 400 ), sizeStart, sizeEnd + (int)( rnd * 10 ), qfalse ); // fire + + if ( ent->currentState.eType == ET_FIRE_COLUMN_SMOKE && ( rand() % 100 ) > 50 ) { + CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ), qfalse ); // smoke + } + } else + { + //CG_ParticleExplosion( "twiltb", lastPos, vec3_origin, 300+(int)(rnd*100), 4, 14+(int)(rnd*8) ); // fire + CG_ParticleExplosion( "blacksmokeanim", lastPos, vec3_origin, 800 + (int)( rnd * 1500 ), 5, 12 + (int)( rnd * 30 ), qfalse ); // smoke + } + } +/* + // spawn a smoke junction + if ((cg.time - ent->lastTrailTime) >= 50 + rand()%50) { + ent->headJuncIndex = CG_AddSmokeJunc( ent->headJuncIndex, + ent, // rain - zinx's trail fix + cgs.media.smokeTrailShader, + origin, + 4500, 0.4, 20, 80 ); + ent->lastTrailTime = cg.time; + } +*/ +// done. +} + +// JPW NERVE +/* +========================== +CG_DynamiteTrail +========================== +*/ +static void CG_DynamiteTrail( centity_t *ent, const weaponInfo_t *wi ) { + vec3_t origin; + float mult; + + BG_EvaluateTrajectory( &ent->currentState.pos, cg.time, origin, qfalse, ent->currentState.effect2Time ); + + if ( ent->currentState.teamNum < 4 ) { + mult = 0.004f * ( cg.time - ent->currentState.effect1Time ) / 30000.0f; + trap_R_AddLightToScene( origin, 320, fabs( sin( ( cg.time - ent->currentState.effect1Time ) * mult ) ), 1.0, 0.0, 0.0, 0, REF_FORCE_DLIGHT ); + } else { + mult = 1 - ( ( cg.time - ent->trailTime ) / 15500.0f ); + //% trap_R_AddLightToScene( origin, 10 + 300 * mult, 1.f, 1.f, 0, REF_FORCE_DLIGHT); + trap_R_AddLightToScene( origin, 320, mult, 1.0, 1.0, 0, 0, REF_FORCE_DLIGHT ); + } +} +// jpw + +// Ridah +/* +========================== +CG_GrenadeTrail +========================== +*/ +static void CG_GrenadeTrail( centity_t *ent, const weaponInfo_t *wi ) { + int step; + vec3_t origin, lastPos; + int contents; + int lastContents, startTime; + entityState_t *es; + int t; + + step = 15; // nice and smooth curves + + es = &ent->currentState; + startTime = ent->trailTime; + t = step * ( ( startTime + step ) / step ); + + BG_EvaluateTrajectory( &es->pos, cg.time, origin, qfalse, es->effect2Time ); + contents = CG_PointContents( origin, -1 ); + + // if object (e.g. grenade) is stationary, don't toss up smoke + if ( es->pos.trType == TR_STATIONARY ) { + ent->trailTime = cg.time; + return; + } + + BG_EvaluateTrajectory( &es->pos, ent->trailTime, lastPos, qfalse, es->effect2Time ); + lastContents = CG_PointContents( lastPos, -1 ); + + ent->trailTime = cg.time; + + if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { + if ( contents & lastContents & CONTENTS_WATER ) { + CG_BubbleTrail( lastPos, origin, 2, 8 ); + } + return; + } + +//----(SA) trying this back on for DM + + // spawn smoke junctions + for ( ; t <= ent->trailTime ; t += step ) { + BG_EvaluateTrajectory( &es->pos, t, origin, qfalse, es->effect2Time ); + ent->headJuncIndex = CG_AddSmokeJunc( ent->headJuncIndex, + ent, // rain - zinx's trail fix + cgs.media.smokeTrailShader, + origin, +// 1500, 0.3, 10, 50 ); + 1000, 0.3, 2, 20 ); + ent->lastTrailTime = cg.time; + } +//----(SA) end +} +// done. + + + + +/* +========================== +CG_NailgunEjectBrass +========================== +*/ +/* +// TTimo: defined but not used +static void CG_NailgunEjectBrass( centity_t *cent ) { + localEntity_t *smoke; + vec3_t origin; + vec3_t v[3]; + vec3_t offset; + vec3_t xoffset; + vec3_t up; + + AnglesToAxis( cent->lerpAngles, v ); + + offset[0] = 0; + offset[1] = -12; + offset[2] = 24; + + xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; + xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; + xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; + VectorAdd( cent->lerpOrigin, xoffset, origin ); + + VectorSet( up, 0, 0, 64 ); + + smoke = CG_SmokePuff( origin, up, 32, 1, 1, 1, 0.33f, 700, cg.time, 0, 0, cgs.media.smokePuffShader ); + // use the optimized local entity add + smoke->leType = LE_SCALE_FADE; +} +*/ + +/* +========================== +CG_RailTrail + SA: re-inserted this as a debug mechanism for bullets +========================== +*/ +void CG_RailTrail2( clientInfo_t *ci, vec3_t start, vec3_t end ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + le->leType = LE_FADE_RGB; + le->startTime = cg.time; + le->endTime = cg.time + cg_railTrailTime.value; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + re->shaderTime = cg.time / 1000.0f; + re->reType = RT_RAIL_CORE; + re->customShader = cgs.media.railCoreShader; + + VectorCopy( start, re->origin ); + VectorCopy( end, re->oldorigin ); + +// // still allow different colors so we can tell AI shots from player shots, etc. +/* if(ci) { + le->color[0] = ci->color[0] * 0.75; + le->color[1] = ci->color[1] * 0.75; + le->color[2] = ci->color[2] * 0.75; + } else {*/ + le->color[0] = 1; + le->color[1] = 0; + le->color[2] = 0; +// } + le->color[3] = 1.0f; + + AxisClear( re->axis ); +} + +//void CG_RailTrailBox( clientInfo_t *ci, vec3_t start, vec3_t end) { +/* +============== +CG_RailTrail + modified so we could draw boxes for debugging as well +============== +*/ +void CG_RailTrail( clientInfo_t *ci, vec3_t start, vec3_t end, int type ) { //----(SA) added 'type' + vec3_t diff, v1, v2, v3, v4, v5, v6; + + if ( !type ) { // just a line + CG_RailTrail2( ci, start, end ); + return; + } + + // type '1' (box) + + VectorSubtract( start, end, diff ); + + VectorCopy( start, v1 ); + VectorCopy( start, v2 ); + VectorCopy( start, v3 ); + v1[0] -= diff[0]; + v2[1] -= diff[1]; + v3[2] -= diff[2]; + CG_RailTrail2( ci, start, v1 ); + CG_RailTrail2( ci, start, v2 ); + CG_RailTrail2( ci, start, v3 ); + + VectorCopy( end, v4 ); + VectorCopy( end, v5 ); + VectorCopy( end, v6 ); + v4[0] += diff[0]; + v5[1] += diff[1]; + v6[2] += diff[2]; + CG_RailTrail2( ci, end, v4 ); + CG_RailTrail2( ci, end, v5 ); + CG_RailTrail2( ci, end, v6 ); + + CG_RailTrail2( ci, v2, v6 ); + CG_RailTrail2( ci, v6, v1 ); + CG_RailTrail2( ci, v1, v5 ); + + CG_RailTrail2( ci, v2, v4 ); + CG_RailTrail2( ci, v4, v3 ); + CG_RailTrail2( ci, v3, v5 ); + +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +/* +====================== +CG_ParseWeaponConfig + read information for weapon animations (first/length/fps) +====================== +*/ +static qboolean CG_ParseWeaponConfig( const char *filename, weaponInfo_t *wi ) { + char *text_p, *prev; + int len; + int i; + float fps; + char *token; + qboolean newfmt = qfalse; //----(SA) + char text[20000]; + fileHandle_t f; + + // load the file + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + + if ( len >= sizeof( text ) - 1 ) { + CG_Printf( "File %s too long\n", filename ); + return qfalse; + } + + trap_FS_Read( text, len, f ); + text[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = text; + + // read optional parameters + while ( 1 ) { + prev = text_p; // so we can unget + token = COM_Parse( &text_p ); + if ( !token ) { // get the variable + break; + } +/* if ( !Q_stricmp( token, "whatever_variable" ) ) { + token = COM_Parse( &text_p ); // get the value + if ( !token ) { + break; + } + continue; + }*/ + + if ( !Q_stricmp( token, "newfmt" ) ) { + newfmt = qtrue; + continue; + } + + // if it is a number, start parsing animations + if ( token[0] >= '0' && token[0] <= '9' ) { + text_p = prev; // unget the token + break; + } + Com_Printf( "unknown token in weapon cfg '%s' is %s\n", token, filename ); + } + + + for ( i = 0 ; i < MAX_WP_ANIMATIONS ; i++ ) { + + token = COM_Parse( &text_p ); // first frame + if ( !token ) { + break; + } + wi->weapAnimations[i].firstFrame = atoi( token ); + + token = COM_Parse( &text_p ); // length + if ( !token ) { + break; + } + wi->weapAnimations[i].numFrames = atoi( token ); + + token = COM_Parse( &text_p ); // fps + if ( !token ) { + break; + } + fps = atof( token ); + if ( fps == 0 ) { + fps = 1; + } + + wi->weapAnimations[i].frameLerp = 1000 / fps; + wi->weapAnimations[i].initialLerp = 1000 / fps; + + token = COM_Parse( &text_p ); // looping frames + if ( !token ) { + break; + } + wi->weapAnimations[i].loopFrames = atoi( token ); + if ( wi->weapAnimations[i].loopFrames > wi->weapAnimations[i].numFrames ) { + wi->weapAnimations[i].loopFrames = wi->weapAnimations[i].numFrames; + } else if ( wi->weapAnimations[i].loopFrames < 0 ) { + wi->weapAnimations[i].loopFrames = 0; + } + + + // store animation/draw bits in '.moveSpeed' + + wi->weapAnimations[i].moveSpeed = 0; + + if ( newfmt ) { + token = COM_Parse( &text_p ); // barrel anim bits + if ( !token ) { + break; + } + wi->weapAnimations[i].moveSpeed = atoi( token ); + + token = COM_Parse( &text_p ); // animated weapon + if ( !token ) { + break; + } + if ( atoi( token ) ) { + wi->weapAnimations[i].moveSpeed |= ( 1 << W_MAX_PARTS ); // set the bit one higher than can be set by the barrel bits + + } + token = COM_Parse( &text_p ); // barrel hide bits (so objects can be flagged to not be drawn during all sequences (a reloading hand that comes in from off screen for that one animation for example) + if ( !token ) { + break; + } + wi->weapAnimations[i].moveSpeed |= ( ( atoi( token ) ) << 8 ); // use 2nd byte for draw bits + } + + } + + if ( i != MAX_WP_ANIMATIONS ) { + CG_Printf( "Error parsing weapon animation file: %s", filename ); + return qfalse; + } + + + return qtrue; +} + + +static qboolean CG_RW_ParseError( int handle, char *format, ... ) { + int line; + char filename[128]; + va_list argptr; + static char string[4096]; + + va_start( argptr, format ); + Q_vsnprintf( string, sizeof( string ), format, argptr ); + va_end( argptr ); + + filename[0] = '\0'; + line = 0; + trap_PC_SourceFileAndLine( handle, filename, &line ); + + Com_Printf( S_COLOR_RED "ERROR: %s, line %d: %s\n", filename, line, string ); + + trap_PC_FreeSource( handle ); + + return qfalse; +} + +static qboolean CG_RW_ParseWeaponLinkPart( int handle, weaponInfo_t *weaponInfo, modelViewType_t viewType ) { + pc_token_t token; + char filename[MAX_QPATH]; + int part; + partModel_t *partModel; + + if ( !PC_Int_Parse( handle, &part ) ) { + return CG_RW_ParseError( handle, "expected part index" ); + } + + if ( part < 0 || part >= W_MAX_PARTS ) { + return CG_RW_ParseError( handle, "part index out of bounds" ); + } + + partModel = &weaponInfo->partModels[viewType][part]; + + memset( partModel, 0, sizeof( *partModel ) ); + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "{" ) ) { + return CG_RW_ParseError( handle, "expected '{'" ); + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( token.string[0] == '}' ) { + break; + } + + if ( !Q_stricmp( token.string, "tag" ) ) { + if ( !PC_String_ParseNoAlloc( handle, partModel->tagName, sizeof( partModel->tagName ) ) ) { + return CG_RW_ParseError( handle, "expected tag name" ); + } + } else if ( !Q_stricmp( token.string, "model" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected model filename" ); + } else { + partModel->model = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "skin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + partModel->skin[0] = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "axisSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + partModel->skin[TEAM_AXIS] = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "alliedSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + partModel->skin[TEAM_ALLIES] = trap_R_RegisterSkin( filename ); + } + } else { + return CG_RW_ParseError( handle, "unknown token '%s'", token.string ); + } + } + + return qtrue; +} + +static qboolean CG_RW_ParseWeaponLink( int handle, weaponInfo_t *weaponInfo, modelViewType_t viewType ) { + pc_token_t token; + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "{" ) ) { + return CG_RW_ParseError( handle, "expected '{'" ); + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( token.string[0] == '}' ) { + break; + } + + if ( !Q_stricmp( token.string, "part" ) ) { + if ( !CG_RW_ParseWeaponLinkPart( handle, weaponInfo, viewType ) ) { + return qfalse; + } + } else { + return CG_RW_ParseError( handle, "unknown token '%s'", token.string ); + } + } + + return qtrue; +} + +static qboolean CG_RW_ParseViewType( int handle, weaponInfo_t *weaponInfo, modelViewType_t viewType ) { + pc_token_t token; + char filename[MAX_QPATH]; + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "{" ) ) { + return CG_RW_ParseError( handle, "expected '{'" ); + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( token.string[0] == '}' ) { + break; + } + + if ( !Q_stricmp( token.string, "model" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected model filename" ); + } else { + weaponInfo->weaponModel[viewType].model = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "skin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + weaponInfo->weaponModel[viewType].skin[0] = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "axisSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + weaponInfo->weaponModel[viewType].skin[TEAM_AXIS] = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "alliedSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + weaponInfo->weaponModel[viewType].skin[TEAM_ALLIES] = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "flashModel" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected flashModel filename" ); + } else { + weaponInfo->flashModel[viewType] = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "weaponLink" ) ) { + if ( !CG_RW_ParseWeaponLink( handle, weaponInfo, viewType ) ) { + return qfalse; + } + } else { + return CG_RW_ParseError( handle, "unknown token '%s'", token.string ); + } + } + + return qtrue; +} + +static qboolean CG_RW_ParseModModel( int handle, weaponInfo_t *weaponInfo ) { + char filename[MAX_QPATH]; + int mod; + + if ( !PC_Int_Parse( handle, &mod ) ) { + return CG_RW_ParseError( handle, "expected mod index" ); + } + + if ( mod < 0 || mod >= 6 ) { + return CG_RW_ParseError( handle, "mod index out of bounds" ); + } + + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected model filename" ); + } else { + weaponInfo->modModels[mod] = trap_R_RegisterModel( filename ); + if ( !weaponInfo->modModels[mod] ) { + // maybe it's a shader + weaponInfo->modModels[mod] = trap_R_RegisterShader( filename ); + } + } + + return qtrue; +} + +static qboolean CG_RW_ParseClient( int handle, weaponInfo_t *weaponInfo ) { + pc_token_t token; + char filename[MAX_QPATH]; + int i; + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "{" ) ) { + return CG_RW_ParseError( handle, "expected '{'" ); + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( token.string[0] == '}' ) { + break; + } + + if ( !Q_stricmp( token.string, "standModel" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected standModel filename" ); + } else { + weaponInfo->standModel = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "droppedAnglesHack" ) ) { + weaponInfo->droppedAnglesHack = qtrue; + } else if ( !Q_stricmp( token.string, "pickupModel" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected pickupModel filename" ); + } else { + weaponInfo->weaponModel[W_PU_MODEL].model = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "pickupSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected pickupSound filename" ); + } else { + //weaponInfo->pickupSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "weaponConfig" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected weaponConfig filename" ); + } else { + if ( !CG_ParseWeaponConfig( filename, weaponInfo ) ) { +// CG_Error( "Couldn't register weapon %i (failed to parse %s)", weaponNum, filename ); + } + } + } else if ( !Q_stricmp( token.string, "handsModel" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected handsModel filename" ); + } else { + weaponInfo->handsModel = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "flashDlightColor" ) ) { + if ( !PC_Vec_Parse( handle, &weaponInfo->flashDlightColor ) ) { + return CG_RW_ParseError( handle, "expected flashDlightColor as r g b" ); + } + } else if ( !Q_stricmp( token.string, "flashSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected flashSound filename" ); + } else { + for ( i = 0; i < 4; i++ ) { + if ( !weaponInfo->flashSound[i] ) { + weaponInfo->flashSound[i] = trap_S_RegisterSound( filename, qfalse ); + break; + } + } + if ( i == 4 ) { + CG_Printf( S_COLOR_YELLOW "WARNING: only up to 4 flashSounds supported per weapon\n" ); + } + } + } else if ( !Q_stricmp( token.string, "flashEchoSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected flashEchoSound filename" ); + } else { + for ( i = 0; i < 4; i++ ) { + if ( !weaponInfo->flashEchoSound[i] ) { + weaponInfo->flashEchoSound[i] = trap_S_RegisterSound( filename, qfalse ); + break; + } + } + if ( i == 4 ) { + CG_Printf( S_COLOR_YELLOW "WARNING: only up to 4 flashEchoSounds supported per weapon\n" ); + } + } + } else if ( !Q_stricmp( token.string, "lastShotSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected lastShotSound filename" ); + } else { + for ( i = 0; i < 4; i++ ) { + if ( !weaponInfo->lastShotSound[i] ) { + weaponInfo->lastShotSound[i] = trap_S_RegisterSound( filename, qfalse ); + break; + } + } + if ( i == 4 ) { + CG_Printf( S_COLOR_YELLOW "WARNING: only up to 4 lastShotSound supported per weapon\n" ); + } + } + } else if ( !Q_stricmp( token.string, "readySound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected readySound filename" ); + } else { + weaponInfo->readySound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "firingSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected firingSound filename" ); + } else { + weaponInfo->firingSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "overheatSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected overheatSound filename" ); + } else { + weaponInfo->overheatSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "reloadSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected reloadSound filename" ); + } else { + weaponInfo->reloadSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "reloadFastSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected reloadFastSound filename" ); + } else { + weaponInfo->reloadFastSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "spinupSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected spinupSound filename" ); + } else { + weaponInfo->spinupSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "spindownSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected spindownSound filename" ); + } else { + weaponInfo->spindownSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "switchSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected switchSound filename" ); + } else { + weaponInfo->switchSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "weaponIcon" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected weaponIcon filename" ); + } else { + weaponInfo->weaponIcon[0] = trap_R_RegisterShader( filename ); + } + } else if ( !Q_stricmp( token.string, "weaponSelectedIcon" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected weaponSelectedIcon filename" ); + } else { + weaponInfo->weaponIcon[1] = trap_R_RegisterShader( filename ); + } + /*} else if( !Q_stricmp( token.string, "ammoIcon" ) ) { + if( !PC_String_ParseNoAlloc( handle, filename, sizeof(filename) ) ) { + return CG_RW_ParseError( handle, "expected ammoIcon filename" ); + } else { + weaponInfo->ammoIcon = trap_R_RegisterShader( filename ); + }*/ + } else if ( !Q_stricmp( token.string, "missileModel" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected missileModel filename" ); + } else { + weaponInfo->missileModel = trap_R_RegisterModel( filename ); + } + } else if ( !Q_stricmp( token.string, "missileAlliedSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + weaponInfo->missileAlliedSkin = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "missileAxisSkin" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected skin filename" ); + } else { + weaponInfo->missileAxisSkin = trap_R_RegisterSkin( filename ); + } + } else if ( !Q_stricmp( token.string, "missileSound" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected missileSound filename" ); + } else { + weaponInfo->missileSound = trap_S_RegisterSound( filename, qfalse ); + } + } else if ( !Q_stricmp( token.string, "missileTrailFunc" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected missileTrailFunc" ); + } else { + if ( !Q_stricmp( filename, "GrenadeTrail" ) ) { + weaponInfo->missileTrailFunc = CG_GrenadeTrail; + } else if ( !Q_stricmp( filename, "RocketTrail" ) ) { + weaponInfo->missileTrailFunc = CG_RocketTrail; + } else if ( !Q_stricmp( filename, "PyroSmokeTrail" ) ) { + weaponInfo->missileTrailFunc = CG_PyroSmokeTrail; + } else if ( !Q_stricmp( filename, "DynamiteTrail" ) ) { + weaponInfo->missileTrailFunc = CG_DynamiteTrail; + } + } + } else if ( !Q_stricmp( token.string, "missileDlight" ) ) { + if ( !PC_Float_Parse( handle, &weaponInfo->missileDlight ) ) { + return CG_RW_ParseError( handle, "expected missileDlight value" ); + } + } else if ( !Q_stricmp( token.string, "missileDlightColor" ) ) { + if ( !PC_Vec_Parse( handle, &weaponInfo->missileDlightColor ) ) { + return CG_RW_ParseError( handle, "expected missileDlightColor as r g b" ); + } + } else if ( !Q_stricmp( token.string, "ejectBrassFunc" ) ) { + if ( !PC_String_ParseNoAlloc( handle, filename, sizeof( filename ) ) ) { + return CG_RW_ParseError( handle, "expected ejectBrassFunc" ); + } else { + if ( !Q_stricmp( filename, "MachineGunEjectBrass" ) ) { + weaponInfo->ejectBrassFunc = CG_MachineGunEjectBrass; + } else if ( !Q_stricmp( filename, "PanzerFaustEjectBrass" ) ) { + weaponInfo->ejectBrassFunc = CG_PanzerFaustEjectBrass; + } + } + } else if ( !Q_stricmp( token.string, "modModel" ) ) { + if ( !CG_RW_ParseModModel( handle, weaponInfo ) ) { + return qfalse; + } + } else if ( !Q_stricmp( token.string, "firstPerson" ) ) { + if ( !CG_RW_ParseViewType( handle, weaponInfo, W_FP_MODEL ) ) { + return qfalse; + } + } else if ( !Q_stricmp( token.string, "thirdPerson" ) ) { + if ( !CG_RW_ParseViewType( handle, weaponInfo, W_TP_MODEL ) ) { + return qfalse; + } + } else { + return CG_RW_ParseError( handle, "unknown token '%s'", token.string ); + } + } + + return qtrue; +} + +static qboolean CG_RegisterWeaponFromWeaponFile( const char *filename, weaponInfo_t *weaponInfo ) { + pc_token_t token; + int handle; + + handle = trap_PC_LoadSource( filename ); + + if ( !handle ) { + return qfalse; + } + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "weaponDef" ) ) { + return CG_RW_ParseError( handle, "expected 'weaponDef'" ); + } + + if ( !trap_PC_ReadToken( handle, &token ) || Q_stricmp( token.string, "{" ) ) { + return CG_RW_ParseError( handle, "expected '{'" ); + } + + while ( 1 ) { + if ( !trap_PC_ReadToken( handle, &token ) ) { + break; + } + + if ( token.string[0] == '}' ) { + break; + } + + if ( !Q_stricmp( token.string, "client" ) ) { + if ( !CG_RW_ParseClient( handle, weaponInfo ) ) { + return qfalse; + } + } else { + return CG_RW_ParseError( handle, "unknown token '%s'", token.string ); + } + } + + trap_PC_FreeSource( handle ); + + return qtrue; +} + +/* +================= +CG_RegisterWeapon +================= +*/ +void CG_RegisterWeapon( int weaponNum, qboolean force ) { + weaponInfo_t *weaponInfo; + char *filename; + + if ( weaponNum <= 0 || weaponNum >= WP_NUM_WEAPONS ) { + return; + } + + weaponInfo = &cg_weapons[weaponNum]; + + if ( weaponInfo->registered && !force ) { + return; + } + + memset( weaponInfo, 0, sizeof( *weaponInfo ) ); + weaponInfo->registered = qtrue; + + /*for( item = bg_itemlist + 1 ; item->classname ; item++ ) { + if( item->giType == IT_WEAPON && item->giTag == weaponNum ) { + weaponInfo->item = item; + break; + } + } + + if( !item->classname ) { + CG_Error( "Couldn't find weapon %i", weaponNum ); + }*/ + + switch ( weaponNum ) { + case WP_KNIFE: filename = "knife.weap"; break; + case WP_LUGER: filename = "luger.weap"; break; + case WP_COLT: filename = "colt.weap"; break; + case WP_MP40: filename = "mp40.weap"; break; + case WP_THOMPSON: filename = "thompson.weap"; break; + case WP_STEN: filename = "sten.weap"; break; + case WP_GRENADE_LAUNCHER: filename = "grenade.weap"; break; + case WP_GRENADE_PINEAPPLE: filename = "pineapple.weap"; break; + case WP_PANZERFAUST: filename = "panzerfaust.weap"; break; + case WP_FLAMETHROWER: filename = "flamethrower.weap"; break; + case WP_AMMO: filename = "ammopack.weap"; break; + case WP_SMOKETRAIL: filename = "smoketrail.weap"; break; + case WP_MEDKIT: filename = "medpack.weap"; break; + case WP_PLIERS: filename = "pliers.weap"; break; + case WP_SMOKE_MARKER: filename = "smokemarker.weap"; break; + case WP_DYNAMITE: filename = "dynamite.weap"; break; + case WP_MEDIC_ADRENALINE: filename = "adrenaline.weap"; break; + case WP_MEDIC_SYRINGE: filename = "syringe.weap"; break; + case WP_BINOCULARS: filename = "binocs.weap"; break; + case WP_KAR98: filename = "kar98.weap"; break; + case WP_GPG40: filename = "gpg40.weap"; break; + case WP_CARBINE: filename = "m1_garand.weap"; break; + case WP_M7: filename = "m7.weap"; break; + case WP_GARAND: + case WP_GARAND_SCOPE: filename = "m1_garand_s.weap"; break; + case WP_FG42: + case WP_FG42SCOPE: filename = "fg42.weap"; break; + case WP_LANDMINE: filename = "landmine.weap"; break; + case WP_SATCHEL: filename = "satchel.weap"; break; + case WP_SATCHEL_DET: filename = "satchel_det.weap"; break; + case WP_SMOKE_BOMB: filename = "smokegrenade.weap"; break; + case WP_MOBILE_MG42_SET: // do we need a seperate file for this? + case WP_MOBILE_MG42: filename = "mg42.weap"; break; + case WP_SILENCER: filename = "silenced_luger.weap"; break; + case WP_SILENCED_COLT: filename = "silenced_colt.weap"; break; + case WP_K43: + case WP_K43_SCOPE: filename = "k43.weap"; break; + case WP_MORTAR: filename = "mortar.weap"; break; + case WP_MORTAR_SET: filename = "mortar_set.weap"; break; + case WP_AKIMBO_LUGER: filename = "akimbo_luger.weap"; break; + case WP_AKIMBO_SILENCEDLUGER: filename = "akimbo_silenced_luger.weap"; break; + case WP_AKIMBO_COLT: filename = "akimbo_colt.weap"; break; + case WP_AKIMBO_SILENCEDCOLT: filename = "akimbo_silenced_colt.weap"; break; + case WP_MAPMORTAR: filename = "mapmortar.weap"; break; // do we really need this? + case WP_ARTY: return; // to shut the game up + default: CG_Printf( S_COLOR_RED "WARNING: trying to register weapon %i but there is no weapon file entry for it.\n", weaponNum ); return; + } + + if ( !CG_RegisterWeaponFromWeaponFile( va( "weapons/%s", filename ), weaponInfo ) ) { + CG_Printf( S_COLOR_RED "WARNING: failed to register media for weapon %i from %s\n", weaponNum, filename ); + } +} + +/* +================= +CG_RegisterItemVisuals + +The server says this item is used on this level +================= +*/ +void CG_RegisterItemVisuals( int itemNum ) { + itemInfo_t *itemInfo; + gitem_t *item; + int i; + + itemInfo = &cg_items[ itemNum ]; + if ( itemInfo->registered ) { + return; + } + + item = &bg_itemlist[ itemNum ]; + + memset( itemInfo, 0, sizeof( &itemInfo ) ); + + if ( item->giType == IT_WEAPON ) { + return; + } + + for ( i = 0; i < MAX_ITEM_MODELS; i++ ) { + itemInfo->models[i] = trap_R_RegisterModel( item->world_model[i] ); + } + + if ( item->icon ) { + itemInfo->icons[0] = trap_R_RegisterShader( item->icon ); + if ( item->giType == IT_HOLDABLE ) { + // (SA) register alternate icons (since holdables can have multiple uses, they might have different icons to represent how many uses are left) + for ( i = 1; i < MAX_ITEM_ICONS; i++ ) + itemInfo->icons[i] = trap_R_RegisterShader( va( "%s%i", item->icon, i + 1 ) ); + } + } + + itemInfo->registered = qtrue; //----(SA) moved this down after the registerweapon() +} + + +/* +======================================================================================== + +VIEW WEAPON + +======================================================================================== +*/ + + +// +// weapon animations +// + +/* +============== +CG_GetPartFramesFromWeap + get animation info from the parent if necessary +============== +*/ +qboolean CG_GetPartFramesFromWeap( centity_t *cent, refEntity_t *part, refEntity_t *parent, int partid, weaponInfo_t *wi ) { + int i; + int frameoffset = 0; + animation_t *anim; + + anim = cent->pe.weap.animation; + + if ( partid == W_MAX_PARTS ) { + return qtrue; // primary weap model drawn for all frames right now + } + + // check draw bit + if ( anim->moveSpeed & ( 1 << ( partid + 8 ) ) ) { // hide bits are in high byte + return qfalse; // not drawn for current sequence + } + + // find part's start frame for this animation sequence + // rain - & out ANIM_TOGGLEBIT or we'll go way out of bounds + for ( i = 0; i < ( cent->pe.weap.animationNumber & ~ANIM_TOGGLEBIT ); i++ ) { + if ( wi->weapAnimations[i].moveSpeed & ( 1 << partid ) ) { // this part has animation for this sequence + frameoffset += wi->weapAnimations[i].numFrames; + } + } + + // now set the correct frame into the part + if ( anim->moveSpeed & ( 1 << partid ) ) { + part->backlerp = parent->backlerp; + part->oldframe = frameoffset + ( parent->oldframe - anim->firstFrame ); + part->frame = frameoffset + ( parent->frame - anim->firstFrame ); + } + + return qtrue; +} + +/* +=============== +CG_SetWeapLerpFrameAnimation + +may include ANIM_TOGGLEBIT +=============== +*/ +static void CG_SetWeapLerpFrameAnimation( weaponInfo_t *wi, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= MAX_WP_ANIMATIONS ) { + CG_Error( "Bad animation number (CG_SWLFA): %i", newAnimation ); + } + + anim = &wi->weapAnimations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; + + if ( cg_debugAnim.integer & 2 ) { + CG_Printf( "Weap Anim: %d\n", newAnimation ); + } +} + + +/* +=============== +CG_ClearWeapLerpFrame +=============== +*/ +void CG_ClearWeapLerpFrame( weaponInfo_t *wi, lerpFrame_t *lf, int animationNumber ) { + lf->frameTime = lf->oldFrameTime = cg.time; + CG_SetWeapLerpFrameAnimation( wi, lf, animationNumber ); + lf->oldFrame = lf->frame = lf->animation->firstFrame; + lf->oldFrameModel = lf->frameModel = lf->animation->mdxFile; +} + + +/* +=============== +CG_RunWeapLerpFrame + +Sets cg.snap, cg.oldFrame, and cg.backlerp +cg.time should be between oldFrameTime and frameTime after exit +=============== +*/ +static void CG_RunWeapLerpFrame( clientInfo_t *ci, weaponInfo_t *wi, lerpFrame_t *lf, int newAnimation, float speedScale ) { + int f; + animation_t *anim; + + // debugging tool to get no animations + if ( cg_animSpeed.integer == 0 ) { + lf->oldFrame = lf->frame = lf->backlerp = 0; + return; + } + + // see if the animation sequence is switching + if ( !lf->animation ) { + CG_ClearWeapLerpFrame( wi, lf, newAnimation ); + } else if ( newAnimation != lf->animationNumber ) { + if ( ( newAnimation & ~ANIM_TOGGLEBIT ) == PM_RaiseAnimForWeapon( cg.snap->ps.nextWeapon ) ) { + CG_ClearWeapLerpFrame( wi, lf, newAnimation ); // clear when switching to raise (since it should be out of view anyway) + } else { + CG_SetWeapLerpFrameAnimation( wi, lf, newAnimation ); + } + } + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( cg.time >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + lf->oldFrameModel = lf->frameModel; + + // get the next frame based on the animation + anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } + if ( cg.time < lf->animationTime ) { + lf->frameTime = lf->animationTime; // initial lerp + } else { + lf->frameTime = lf->oldFrameTime + anim->frameLerp; + } + f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + f *= speedScale; // adjust for haste, etc + if ( f >= anim->numFrames ) { + f -= anim->numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = anim->numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = cg.time; + } + } + lf->frame = anim->firstFrame + f; + lf->frameModel = anim->mdxFile; + if ( cg.time > lf->frameTime ) { + lf->frameTime = cg.time; + if ( cg_debugAnim.integer ) { + CG_Printf( "Clamp lf->frameTime\n" ); + } + } + } + + if ( lf->frameTime > cg.time + 200 ) { + lf->frameTime = cg.time; + } + + if ( lf->oldFrameTime > cg.time ) { + lf->oldFrameTime = cg.time; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + + + +/* +============== +CG_WeaponAnimation +============== +*/ + +//----(SA) modified. this is now client-side only (server does not dictate weapon animation info) +static void CG_WeaponAnimation( playerState_t *ps, weaponInfo_t *weapon, int *weapOld, int *weap, float *weapBackLerp ) { + + centity_t *cent = &cg.predictedPlayerEntity; + clientInfo_t *ci = &cgs.clientinfo[ ps->clientNum ]; + + if ( cg_noPlayerAnims.integer ) { + *weapOld = *weap = 0; + return; + } + + CG_RunWeapLerpFrame( ci, weapon, ¢->pe.weap, ps->weapAnim, 1 ); + + *weapOld = cent->pe.weap.oldFrame; + *weap = cent->pe.weap.frame; + *weapBackLerp = cent->pe.weap.backlerp; + + if ( cg_debugAnim.integer == 3 ) { + CG_Printf( "oldframe: %d frame: %d backlerp: %f\n", cent->pe.weap.oldFrame, cent->pe.weap.frame, cent->pe.weap.backlerp ); + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + + +// (SA) it wasn't used anyway + + +/* +============== +CG_CalculateWeaponPosition +============== +*/ +static void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) { + float scale; + int delta; + float fracsin; + + VectorCopy( cg.refdef_current->vieworg, origin ); + VectorCopy( cg.refdefViewAngles, angles ); + + if ( cg.predictedPlayerState.eFlags & EF_MOUNTEDTANK ) { + angles[PITCH] = cg.refdefViewAngles[PITCH] / 1.2; + } + + if ( !cg.renderingThirdPerson && + ( cg.predictedPlayerState.weapon == WP_MORTAR_SET || cg.predictedPlayerState.weapon == WP_MOBILE_MG42_SET ) && + cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + angles[PITCH] = cg.pmext.mountedWeaponAngles[PITCH]; + } + + if ( cg.predictedPlayerState.eFlags & EF_PRONE_MOVING ) { + int pronemovingtime = cg.time - cg.proneMovingTime; + if ( pronemovingtime > 0 ) { // div by 0 + float factor = pronemovingtime > 200 ? 1.f : 1.f / ( 200.f / (float)pronemovingtime ); + VectorMA( origin, factor * -20, cg.refdef_current->viewaxis[0], origin ); + VectorMA( origin, factor * 3, cg.refdef_current->viewaxis[1], origin ); + } + } else { + int pronenomovingtime = cg.time - -cg.proneMovingTime; + + if ( pronenomovingtime < 200 ) { + float factor = pronenomovingtime == 0 ? 1.f : 1.f - ( 1.f / ( 200.f / (float)pronenomovingtime ) ); + VectorMA( origin, factor * -20, cg.refdef_current->viewaxis[0], origin ); + VectorMA( origin, factor * 3, cg.refdef_current->viewaxis[1], origin ); + } + } + + // adjust 'lean' into weapon + if ( cg.predictedPlayerState.leanf != 0 ) { + vec3_t right, up; + float myfrac = 1.0f; + + switch ( cg.predictedPlayerState.weapon ) { + case WP_FLAMETHROWER: + case WP_KAR98: + case WP_CARBINE: + case WP_GPG40: + case WP_M7: + case WP_K43: + myfrac = 2.0f; + break; + case WP_GARAND: + myfrac = 3.0f; + break; + } + + // reverse the roll on the weapon so it stays relatively level + angles[ROLL] -= cg.predictedPlayerState.leanf / ( myfrac * 2.0f ); + AngleVectors( angles, NULL, right, up ); + VectorMA( origin, angles[ROLL], right, origin ); + + // pitch the gun down a bit to show that firing is not allowed when leaning + angles[PITCH] += ( abs( cg.predictedPlayerState.leanf ) / 2.0f ); + + // this gives you some impression that the weapon stays in relatively the same + // position while you lean, so you appear to 'peek' over the weapon + AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); + VectorMA( origin, -cg.predictedPlayerState.leanf / 4.0f, right, origin ); + } + + + // on odd legs, invert some angles + if ( cg.bobcycle & 1 ) { + scale = -cg.xyspeed; + } else { + scale = cg.xyspeed; + } + + // gun angles from bobbing + + angles[ROLL] += scale * cg.bobfracsin * 0.005; + angles[YAW] += scale * cg.bobfracsin * 0.01; + angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005; + + // drop the weapon when landing + delta = cg.time - cg.landTime; + if ( delta < LAND_DEFLECT_TIME ) { + origin[2] += cg.landChange * 0.25 * delta / LAND_DEFLECT_TIME; + } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) { + origin[2] += cg.landChange * 0.25 * + ( LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta ) / LAND_RETURN_TIME; + } + +#if 0 + // drop the weapon when stair climbing + delta = cg.time - cg.stepTime; + if ( delta < STEP_TIME / 2 ) { + origin[2] -= cg.stepChange * 0.25 * delta / ( STEP_TIME / 2 ); + } else if ( delta < STEP_TIME ) { + origin[2] -= cg.stepChange * 0.25 * ( STEP_TIME - delta ) / ( STEP_TIME / 2 ); + } +#endif + + // idle drift + if ( ( !( cg.predictedPlayerState.eFlags & EF_MOUNTEDTANK ) ) && ( cg.predictedPlayerState.weapon != WP_MORTAR_SET ) && ( cg.predictedPlayerState.weapon != WP_MOBILE_MG42_SET ) ) { + //----(SA) adjustment for MAX KAUFMAN + // scale = cg.xyspeed + 40; + scale = 80; + //----(SA) end + fracsin = sin( cg.time * 0.001 ); + angles[ROLL] += scale * fracsin * 0.01; + angles[YAW] += scale * fracsin * 0.01; + angles[PITCH] += scale * fracsin * 0.01; + } + + // RF, subtract the kickAngles + VectorMA( angles, -1.0, cg.kickAngles, angles ); +} + + +// Ridah +/* +=============== +CG_FlamethrowerFlame +=============== +*/ +static void CG_FlamethrowerFlame( centity_t *cent, vec3_t origin ) { + + if ( cent->currentState.weapon != WP_FLAMETHROWER ) { + return; + } + + CG_FireFlameChunks( cent, origin, cent->lerpAngles, 1.0, qtrue ); + return; +} +// done. + +/* +======================== +CG_AddWeaponWithPowerups +======================== +*/ +static void CG_AddWeaponWithPowerups( refEntity_t *gun, int powerups, playerState_t *ps, centity_t *cent ) { + // add powerup effects + // DHM - Nerve :: no powerup effects on weapons + trap_R_AddRefEntityToScene( gun ); +} + +/* +============= +CG_AddPlayerWeapon + +Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL) +The main player will have this called for BOTH cases, so effects like light and +sound should only be done on the world model case. +============= +*/ +static qboolean debuggingweapon = qfalse; + +void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent ) { + + refEntity_t gun; + refEntity_t barrel; + refEntity_t flash; + vec3_t angles; + weapon_t weaponNum; + weaponInfo_t *weapon; + centity_t *nonPredictedCent; + qboolean firing; // Ridah + qboolean akimboFire = qfalse; + +// qboolean playerScaled; + qboolean drawpart; + int i; + qboolean isPlayer; + + bg_playerclass_t* classInfo; + + classInfo = BG_GetPlayerClassInfo( cgs.clientinfo[cent->currentState.clientNum].team, cgs.clientinfo[cent->currentState.clientNum].cls ); + + // (SA) might as well have this check consistant throughout the routine + isPlayer = (qboolean)( cent->currentState.clientNum == cg.snap->ps.clientNum ); + + weaponNum = cent->currentState.weapon; + + if ( ps && cg.cameraMode ) { + return; + } + + // don't draw any weapons when the binocs are up + if ( cent->currentState.eFlags & EF_ZOOMING ) { + return; + } + + // don't draw weapon stuff when looking through a scope + if ( weaponNum == WP_FG42SCOPE || weaponNum == WP_GARAND_SCOPE || weaponNum == WP_K43_SCOPE ) { + if ( isPlayer && !cg.renderingThirdPerson ) { + return; + } + } + + if ( weaponNum == WP_GRENADE_PINEAPPLE || weaponNum == WP_GRENADE_LAUNCHER ) { + if ( ps && !ps->ammoclip[ weaponNum ] ) { + return; + } + } + + // no weapon when on mg_42 + if ( cent->currentState.eFlags & EF_MOUNTEDTANK ) { + if ( isPlayer && !cg.renderingThirdPerson ) { + return; + } + + if ( cg.time - cent->muzzleFlashTime < MUZZLE_FLASH_TIME ) { + memset( &flash, 0, sizeof( flash ) ); + flash.renderfx = RF_LIGHTING_ORIGIN; + flash.hModel = cgs.media.mg42muzzleflash; + + VectorCopy( cg_entities[cg_entities[ cent->currentState.number ].tagParent].mountedMG42Flash.origin, flash.origin ); + AxisCopy( cg_entities[cg_entities[ cent->currentState.number ].tagParent].mountedMG42Flash.axis, flash.axis ); + + trap_R_AddRefEntityToScene( &flash ); + + // ydnar: add dynamic light + trap_R_AddLightToScene( flash.origin, 320, 1.25 + ( rand() & 31 ) / 128, 1.0, 0.6, 0.23, 0, 0 ); + } + return; + } + + if ( cent->currentState.eFlags & EF_MG42_ACTIVE || cent->currentState.eFlags & EF_AAGUN_ACTIVE ) { + // Arnout: MG42 Muzzle Flash + if ( cg.time - cent->muzzleFlashTime < MUZZLE_FLASH_TIME ) { + CG_MG42EFX( cent ); + } + return; + } + + if ( ( !ps || cg.renderingThirdPerson ) && cent->currentState.eFlags & EF_PRONE_MOVING ) { + return; + } + + weapon = &cg_weapons[weaponNum]; + + if ( BG_IsAkimboWeapon( weaponNum ) ) { + if ( isPlayer ) { + akimboFire = BG_AkimboFireSequence( weaponNum, cg.predictedPlayerState.ammoclip[BG_FindClipForWeapon( weaponNum )], cg.predictedPlayerState.ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weaponNum ) )] ); + } else if ( ps ) { + akimboFire = BG_AkimboFireSequence( weaponNum, ps->ammoclip[BG_FindClipForWeapon( weaponNum )], ps->ammoclip[BG_FindClipForWeapon( BG_AkimboSidearm( weaponNum ) )] ); + } + // Gordon: FIXME: alternate for other clients, store flip-flop on cent or smuffin + } + + + // add the weapon + memset( &gun, 0, sizeof( gun ) ); + VectorCopy( parent->lightingOrigin, gun.lightingOrigin ); + gun.shadowPlane = parent->shadowPlane; + gun.renderfx = parent->renderfx; + + if ( ps ) { + team_t team = ps->persistant[PERS_TEAM]; + + if ( ( weaponNum != WP_SATCHEL ) && ( cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) ) { + team = team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + } + + gun.hModel = weapon->weaponModel[W_FP_MODEL].model; + if ( ( team == TEAM_AXIS ) && + weapon->weaponModel[W_FP_MODEL].skin[TEAM_AXIS] ) { + gun.customSkin = weapon->weaponModel[W_FP_MODEL].skin[TEAM_AXIS]; + } else if ( ( team == TEAM_ALLIES ) && + weapon->weaponModel[W_FP_MODEL].skin[TEAM_ALLIES] ) { + gun.customSkin = weapon->weaponModel[W_FP_MODEL].skin[TEAM_ALLIES]; + } else { + gun.customSkin = weapon->weaponModel[W_FP_MODEL].skin[0]; // if not loaded it's 0 so doesn't do any harm + } + } else { + team_t team = cgs.clientinfo[cent->currentState.clientNum].team; + + if ( ( weaponNum != WP_SATCHEL ) && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) { + team = team == TEAM_AXIS ? TEAM_ALLIES : TEAM_AXIS; + } + + gun.hModel = weapon->weaponModel[W_TP_MODEL].model; + if ( ( team == TEAM_AXIS ) && + weapon->weaponModel[W_TP_MODEL].skin[TEAM_AXIS] ) { + gun.customSkin = weapon->weaponModel[W_FP_MODEL].skin[TEAM_AXIS]; + } else if ( ( team == TEAM_ALLIES ) && + weapon->weaponModel[W_TP_MODEL].skin[TEAM_ALLIES] ) { + gun.customSkin = weapon->weaponModel[W_TP_MODEL].skin[TEAM_ALLIES]; + } else { + gun.customSkin = weapon->weaponModel[W_TP_MODEL].skin[0]; // if not loaded it's 0 so doesn't do any harm + } + } + + if ( !gun.hModel ) { + if ( debuggingweapon ) { + CG_Printf( "returning due to: !gun.hModel\n" ); + } + return; + } + + if ( !ps && cg.snap->ps.pm_flags & PMF_LADDER && isPlayer ) { //----(SA) player on ladder + if ( debuggingweapon ) { + CG_Printf( "returning due to: !ps && cg.snap->ps.pm_flags & PMF_LADDER\n" ); + } + return; + } + + if ( !ps ) { + // add weapon ready sound + cent->pe.lightningFiring = qfalse; + if ( ( cent->currentState.eFlags & EF_FIRING ) && weapon->firingSound ) { + // lightning gun and guantlet make a different sound when fire is held down + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, weapon->firingSound, 255, 0 ); + cent->pe.lightningFiring = qtrue; + } else if ( weapon->readySound ) { + trap_S_AddLoopingSound( cent->lerpOrigin, vec3_origin, weapon->readySound, 255, 0 ); + } + } + + // Ridah + firing = ( ( cent->currentState.eFlags & EF_FIRING ) != 0 ); + + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weapon == WP_MORTAR_SET && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + vec3_t angles; + + angles[YAW] = angles[ROLL] = 0.f; + angles[PITCH] = -.4f * AngleNormalize180( cg.pmext.mountedWeaponAngles[PITCH] - ps->viewangles[PITCH] ); + + AnglesToAxis( angles, gun.axis ); + + CG_PositionRotatedEntityOnTag( &gun, parent, "tag_weapon" ); + } else if ( ( !ps || cg.renderingThirdPerson ) && ( weaponNum == WP_MORTAR_SET || weaponNum == WP_MORTAR ) ) { + CG_PositionEntityOnTag( &gun, parent, "tag_weapon2", 0, NULL ); + } else { + CG_PositionEntityOnTag( &gun, parent, "tag_weapon", 0, NULL ); + } + + +/* playerScaled = (qboolean)(cgs.clientinfo[ cent->currentState.clientNum ].playermodelScale[0] != 0); + if(!ps && playerScaled) { // don't "un-scale" weap up in 1st person + for(i=0;i<3;i++) { // scale weapon back up so it doesn't pick up the adjusted scale of the character models. + // this will affect any parts attached to the gun as well (barrel/bolt/flash/brass/etc.) + VectorScale( gun.axis[i], 1.0/(cgs.clientinfo[ cent->currentState.clientNum ].playermodelScale[i]), gun.axis[i]); + } + + }*/ + + if ( ps ) { + drawpart = CG_GetPartFramesFromWeap( cent, &gun, parent, W_MAX_PARTS, weapon ); // W_MAX_PARTS specifies this as the primary view model + } else { + drawpart = qtrue; + } + + if ( drawpart ) { + if ( weaponNum == WP_AMMO ) { + if ( ps ) { + if ( cgs.clientinfo[ ps->clientNum ].skill[ SK_SIGNALS ] >= 1 ) { + gun.customShader = weapon->modModels[0]; + } + } else { + if ( cgs.clientinfo[ cent->currentState.clientNum ].skill[ SK_SIGNALS ] >= 1 ) { + gun.customShader = weapon->modModels[0]; + } + } + } + if ( !ps ) { + if ( weaponNum == WP_MEDIC_SYRINGE ) { + if ( cgs.clientinfo[ cent->currentState.clientNum ].skill[ SK_FIRST_AID ] >= 3 ) { + gun.customShader = weapon->modModels[ 0 ]; + } + } + } + CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups, ps, cent ); + } + + if ( ( !ps || cg.renderingThirdPerson ) && + ( weaponNum == WP_AKIMBO_COLT || weaponNum == WP_AKIMBO_SILENCEDCOLT || weaponNum == WP_AKIMBO_LUGER || weaponNum == WP_AKIMBO_SILENCEDLUGER ) ) { + // add to other hand as well + CG_PositionEntityOnTag( &gun, parent, "tag_weapon2", 0, NULL ); + CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups, ps, cent ); + } + + // ydnar: test hack + //% if( weaponNum == WP_KNIFE ) + //% trap_R_AddLightToScene( gun.origin, 512, 1.5, 1.0, 1.0, 1.0, 0, 0 ); + + if ( isPlayer ) { + refEntity_t brass; + + if ( BG_IsAkimboWeapon( weaponNum ) && akimboFire ) { + CG_PositionRotatedEntityOnTag( &brass, parent, "tag_brass2" ); + } else { + CG_PositionRotatedEntityOnTag( &brass, parent, "tag_brass" ); + } + VectorCopy( brass.origin, ejectBrassCasingOrigin ); + } + + memset( &barrel, 0, sizeof( barrel ) ); + VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); + barrel.shadowPlane = parent->shadowPlane; + barrel.renderfx = parent->renderfx; + + // add barrels + // attach generic weapon parts to the first person weapon. + // if a barrel should be attached for third person, add it in the (!ps) section below + angles[YAW] = angles[PITCH] = 0; + + if ( ps ) { + qboolean spunpart; + + for ( i = W_PART_1; i < W_MAX_PARTS; i++ ) { + if ( weaponNum == WP_MORTAR_SET && ( i == W_PART_4 || i == W_PART_5 ) ) { + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + continue; + } + } + + spunpart = qfalse; + barrel.hModel = weapon->partModels[W_FP_MODEL][i].model; + + if ( weaponNum == WP_MORTAR_SET ) { + if ( i == W_PART_3 ) { + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + angles[PITCH] = angles[YAW] = 0.f; + angles[ROLL] = .8f * AngleNormalize180( cg.pmext.mountedWeaponAngles[YAW] - ps->viewangles[YAW] ); + spunpart = qtrue; + } + } else if ( i == W_PART_1 || i == W_PART_2 ) { + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + angles[YAW] = angles[ROLL] = 0.f; + angles[PITCH] = -.4f * AngleNormalize180( cg.pmext.mountedWeaponAngles[PITCH] - ps->viewangles[PITCH] ); + spunpart = qtrue; + } + } + } else if ( weaponNum == WP_MOBILE_MG42_SET ) { + + } + + if ( spunpart ) { + AnglesToAxis( angles, barrel.axis ); + } + + if ( barrel.hModel ) { + if ( spunpart ) { + CG_PositionRotatedEntityOnTag( &barrel, parent, weapon->partModels[W_FP_MODEL][i].tagName ); + } else { + CG_PositionEntityOnTag( &barrel, parent, weapon->partModels[W_FP_MODEL][i].tagName, 0, NULL ); + } + + drawpart = CG_GetPartFramesFromWeap( cent, &barrel, parent, i, weapon ); + + if ( weaponNum == WP_MORTAR_SET && ( i == W_PART_1 || i == W_PART_2 ) ) { + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + VectorMA( barrel.origin, .5f * angles[PITCH], cg.refdef_current->viewaxis[0], barrel.origin ); + } + } + + if ( drawpart ) { + if ( ( ps->persistant[PERS_TEAM] == TEAM_AXIS || + ( ps->persistant[PERS_TEAM] == TEAM_ALLIES && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) ) && + weapon->partModels[W_FP_MODEL][i].skin[TEAM_AXIS] ) { + barrel.customSkin = weapon->partModels[W_FP_MODEL][i].skin[TEAM_AXIS]; + } else if ( ( ps->persistant[PERS_TEAM] == TEAM_ALLIES || + ( ps->persistant[PERS_TEAM] == TEAM_AXIS && cent->currentState.powerups & ( 1 << PW_OPS_DISGUISED ) ) ) && + weapon->partModels[W_FP_MODEL][i].skin[TEAM_ALLIES] ) { + barrel.customSkin = weapon->partModels[W_FP_MODEL][i].skin[TEAM_ALLIES]; + } else { + barrel.customSkin = weapon->partModels[W_FP_MODEL][i].skin[0]; // if not loaded it's 0 so doesn't do any harm + + } + if ( weaponNum == WP_MEDIC_SYRINGE && i == W_PART_1 ) { + if ( cgs.clientinfo[ ps->clientNum ].skill[ SK_FIRST_AID ] >= 3 ) { + barrel.customShader = weapon->modModels[ 0 ]; + } + } + + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + + if ( weaponNum == WP_SATCHEL_DET && i == W_PART_1 ) { + float rangeSquared; + qboolean inRange; + refEntity_t satchelDetPart; + + if ( cg.satchelCharge ) { + rangeSquared = DistanceSquared( cg.satchelCharge->lerpOrigin, cg.predictedPlayerEntity.lerpOrigin ); + } else { + rangeSquared = Square( 2001.f ); + } + + if ( rangeSquared <= Square( 2000 ) ) { + inRange = qtrue; + } else { + inRange = qfalse; + } + + memset( &satchelDetPart, 0, sizeof( satchelDetPart ) ); + VectorCopy( parent->lightingOrigin, satchelDetPart.lightingOrigin ); + satchelDetPart.shadowPlane = parent->shadowPlane; + satchelDetPart.renderfx = parent->renderfx; + + satchelDetPart.hModel = weapon->modModels[0]; + CG_PositionEntityOnTag( &satchelDetPart, &barrel, "tag_rlight", 0, NULL ); + satchelDetPart.customShader = inRange ? weapon->modModels[2] : weapon->modModels[3]; + CG_AddWeaponWithPowerups( &satchelDetPart, cent->currentState.powerups, ps, cent ); + + CG_PositionEntityOnTag( &satchelDetPart, &barrel, "tag_glight", 0, NULL ); + satchelDetPart.customShader = inRange ? weapon->modModels[5] : weapon->modModels[4]; + CG_AddWeaponWithPowerups( &satchelDetPart, cent->currentState.powerups, ps, cent ); + + satchelDetPart.hModel = weapon->modModels[1]; + angles[PITCH] = angles[ROLL] = 0.f; + if ( inRange ) { + angles[YAW] = -30.f + ( 60.f * ( rangeSquared / Square( 2000 ) ) ); + } else { + angles[YAW] = 30.f; + } + AnglesToAxis( angles, satchelDetPart.axis ); + CG_PositionRotatedEntityOnTag( &satchelDetPart, &barrel, "tag_needle" ); + satchelDetPart.customShader = weapon->modModels[2]; + CG_AddWeaponWithPowerups( &satchelDetPart, cent->currentState.powerups, ps, cent ); + } else if ( weaponNum == WP_MORTAR_SET && i == W_PART_3 ) { + if ( ps && !cg.renderingThirdPerson && cg.predictedPlayerState.weaponstate != WEAPON_RAISING ) { + refEntity_t bipodLeg; + + memset( &bipodLeg, 0, sizeof( bipodLeg ) ); + VectorCopy( parent->lightingOrigin, bipodLeg.lightingOrigin ); + bipodLeg.shadowPlane = parent->shadowPlane; + bipodLeg.renderfx = parent->renderfx; + + bipodLeg.hModel = weapon->partModels[W_FP_MODEL][3].model; + CG_PositionEntityOnTag( &bipodLeg, &barrel, "tag_barrel4", 0, NULL ); + CG_AddWeaponWithPowerups( &bipodLeg, cent->currentState.powerups, ps, cent ); + + bipodLeg.hModel = weapon->partModels[W_FP_MODEL][4].model; + CG_PositionEntityOnTag( &bipodLeg, &barrel, "tag_barrel5", 0, NULL ); + CG_AddWeaponWithPowerups( &bipodLeg, cent->currentState.powerups, ps, cent ); + } + } + } + } + } + } + + // add the scope model to the rifle if you've got it + if ( isPlayer && !cg.renderingThirdPerson ) { // (SA) for now just do it on the first person weapons + if ( weaponNum == WP_CARBINE || weaponNum == WP_KAR98 || weaponNum == WP_GPG40 || weaponNum == WP_M7 ) { + if ( ( cg.snap->ps.ammo[BG_FindAmmoForWeapon( WP_GPG40 )] || cg.snap->ps.ammo[BG_FindAmmoForWeapon( WP_M7 )] || cg.snap->ps.ammoclip[BG_FindAmmoForWeapon( WP_GPG40 )] || cg.snap->ps.ammoclip[BG_FindAmmoForWeapon( WP_M7 )] ) ) { + int anim = cg.snap->ps.weapAnim & ~ANIM_TOGGLEBIT; + if ( anim == PM_AltSwitchFromForWeapon( weaponNum ) || anim == PM_AltSwitchToForWeapon( weaponNum ) || anim == PM_IdleAnimForWeapon( weaponNum ) ) { + barrel.hModel = weapon->modModels[0]; + if ( barrel.hModel ) { + CG_PositionEntityOnTag( &barrel, parent, "tag_scope", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } + } + } + } else if ( weaponNum == WP_GARAND ) { + barrel.hModel = weapon->modModels[0]; + if ( barrel.hModel ) { + CG_PositionEntityOnTag( &barrel, &gun, "tag_scope2", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } + + barrel.hModel = weapon->modModels[1]; +// if(barrel.hModel) { + CG_PositionEntityOnTag( &barrel, &gun, "tag_flash", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); +// } + } else if ( weaponNum == WP_K43 ) { + barrel.hModel = weapon->modModels[0]; + if ( barrel.hModel ) { + CG_PositionEntityOnTag( &barrel, &gun, "tag_scope", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } + + barrel.hModel = weapon->modModels[1]; +// if(barrel.hModel) { + CG_PositionEntityOnTag( &barrel, &gun, "tag_flash", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); +// } + } + } + // 3rd person attachements + else { + if ( weaponNum == WP_M7 || weaponNum == WP_GPG40 /* || weaponNum == WP_CARBINE || weaponNum == WP_KAR98*/ ) { + // the holder + barrel.hModel = weapon->modModels[1]; + CG_PositionEntityOnTag( &barrel, &gun, "tag_flash", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + + // the grenade - have to always enabled it, no means of telling if another person has a grenade loaded or not atm :/ + //if( cg.snap->ps.weaponstate != WEAPON_FIRING && cg.snap->ps.weaponstate != WEAPON_RELOADING ) { + if ( weaponNum == WP_M7 /*|| weaponNum == WP_CARBINE*/ ) { + barrel.hModel = weapon->missileModel; + CG_PositionEntityOnTag( &barrel, &barrel, "tag_prj", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } + } else if ( weaponNum == WP_GARAND || weaponNum == WP_GARAND_SCOPE || weaponNum == WP_K43 || weaponNum == WP_K43_SCOPE ) { + // the holder + barrel.hModel = weapon->modModels[2]; + CG_PositionEntityOnTag( &barrel, &gun, "tag_scope", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } else if ( weaponNum == WP_MOBILE_MG42 ) { + barrel.hModel = weapon->modModels[0]; + barrel.frame = 1; + CG_PositionEntityOnTag( &barrel, &gun, "tag_bipod", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } else if ( weaponNum == WP_MOBILE_MG42_SET ) { + barrel.hModel = weapon->modModels[0]; + barrel.frame = 0; + CG_PositionEntityOnTag( &barrel, &gun, "tag_bipod", 0, NULL ); + CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups, ps, cent ); + } + } + + // make sure we aren't looking at cg.predictedPlayerEntity for LG + nonPredictedCent = &cg_entities[cent->currentState.clientNum]; + + // if the index of the nonPredictedCent is not the same as the clientNum + // then this is a fake player (like on the single player podiums), so + // go ahead and use the cent + if ( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { + nonPredictedCent = cent; + } + + // add the flash + memset( &flash, 0, sizeof( flash ) ); + VectorCopy( parent->lightingOrigin, flash.lightingOrigin ); + flash.shadowPlane = parent->shadowPlane; + flash.renderfx = parent->renderfx; + + if ( ps ) { + flash.hModel = weapon->flashModel[W_FP_MODEL]; + } else { + flash.hModel = weapon->flashModel[W_TP_MODEL]; + } + + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = crandom() * 10; + AnglesToAxis( angles, flash.axis ); + + if ( /*isPlayer &&*/ BG_IsAkimboWeapon( weaponNum ) ) { + if ( !ps || cg.renderingThirdPerson ) { + if ( !cent->akimboFire ) { + CG_PositionRotatedEntityOnTag( &flash, parent, "tag_weapon" ); + VectorMA( flash.origin, 10, flash.axis[0], flash.origin ); + } else { + CG_PositionRotatedEntityOnTag( &flash, &gun, "tag_flash" ); + } + } else { + if ( !cent->akimboFire ) { + CG_PositionRotatedEntityOnTag( &flash, parent, "tag_flash2" ); + + } else { + CG_PositionRotatedEntityOnTag( &flash, parent, "tag_flash" ); + } + } + } else { + CG_PositionRotatedEntityOnTag( &flash, &gun, "tag_flash" ); + } + + // store this position for other cgame elements to access + cent->pe.gunRefEnt = gun; + cent->pe.gunRefEntFrame = cg.clientFrame; + + if ( ( weaponNum == WP_FLAMETHROWER ) && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) { + // continuous flash + + } else { + + // continuous smoke after firing +#define BARREL_SMOKE_TIME 1000 + + if ( ps || cg.renderingThirdPerson || !isPlayer ) { + if ( weaponNum == WP_STEN || weaponNum == WP_MOBILE_MG42 || weaponNum == WP_MOBILE_MG42_SET ) { + // hot smoking gun + if ( cg.time - cent->overheatTime < 3000 ) { + if ( !( rand() % 3 ) ) { + float alpha; + alpha = 1.0f - ( (float)( cg.time - cent->overheatTime ) / 3000.0f ); + alpha *= 0.25f; // .25 max alpha + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, 1000, 8, 20, 30, alpha, 8.f ); + } + } + + } else if ( weaponNum == WP_PANZERFAUST ) { + if ( cg.time - cent->muzzleFlashTime < BARREL_SMOKE_TIME ) { + if ( !( rand() % 5 ) ) { + float alpha; + alpha = 1.0f - ( (float)( cg.time - cent->muzzleFlashTime ) / (float)BARREL_SMOKE_TIME ); // what fraction of BARREL_SMOKE_TIME are we at + alpha *= 0.25f; // .25 max alpha + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, 1000, 8, 20, 30, alpha, 8.f ); + } + } + } + } + + if ( weaponNum == WP_MORTAR_SET ) { + if ( ps && !cg.renderingThirdPerson && cg.time - cent->muzzleFlashTime < 800 ) { + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, 700, 16, 20, 30, .12f, 4.f ); + } + } + + // impulse flash + if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME ) { + // Ridah, blue ignition flame if not firing flamer + if ( weaponNum != WP_FLAMETHROWER ) { + return; + } + } + + } + + // weapons that don't need to go any further as they have no flash or light + if ( weaponNum == WP_GRENADE_LAUNCHER || + weaponNum == WP_GRENADE_PINEAPPLE || + weaponNum == WP_KNIFE || + weaponNum == WP_DYNAMITE || + weaponNum == WP_GPG40 || + weaponNum == WP_M7 || + weaponNum == WP_LANDMINE || + weaponNum == WP_SATCHEL || + weaponNum == WP_SATCHEL_DET || + weaponNum == WP_TRIPMINE || + weaponNum == WP_SMOKE_BOMB || + weaponNum == WP_MEDIC_SYRINGE || + weaponNum == WP_MEDIC_ADRENALINE + ) { + return; + } + + // weaps with barrel smoke + if ( ps || cg.renderingThirdPerson || !isPlayer ) { + if ( weaponNum == WP_STEN ) { + if ( cg.time - cent->muzzleFlashTime < 100 ) { +// CG_ParticleImpactSmokePuff (cgs.media.smokeParticleShader, flash.origin); + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, 500, 8, 20, 30, 0.25f, 8.f ); + } + } + } + + if ( flash.hModel ) { + if ( weaponNum != WP_FLAMETHROWER ) { //Ridah, hide the flash also for now + // RF, changed this so the muzzle flash stays onscreen for long enough to be seen + if ( cg.time - cent->muzzleFlashTime < MUZZLE_FLASH_TIME ) { + //if (firing) { // Ridah + trap_R_AddRefEntityToScene( &flash ); + } + } + } + + // Ridah, zombie fires from his head + //if (CG_MonsterUsingWeapon( cent, AICHAR_ZOMBIE, WP_MONSTER_ATTACK1 )) { + // CG_PositionEntityOnTag( &flash, parent, parent->hModel, "tag_head", NULL); + //} + + if ( ps || cg.renderingThirdPerson || !isPlayer ) { + // ydnar: no flamethrower flame on prone moving + // ydnar: or dead players + if ( firing && !( cent->currentState.eFlags & ( EF_PRONE_MOVING | EF_DEAD ) ) ) { + // Ridah, Flamethrower effect + CG_FlamethrowerFlame( cent, flash.origin ); + + // make a dlight for the flash + if ( weapon->flashDlightColor[0] || weapon->flashDlightColor[1] || weapon->flashDlightColor[2] ) { + //% trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), weapon->flashDlightColor[0], + //% weapon->flashDlightColor[1], weapon->flashDlightColor[2], 0 ); + trap_R_AddLightToScene( flash.origin, 320, 1.25 + ( rand() & 31 ) / 128, weapon->flashDlightColor[0], + weapon->flashDlightColor[1], weapon->flashDlightColor[2], 0, 0 ); + } + } else { + if ( weaponNum == WP_FLAMETHROWER ) { + vec3_t angles; + AxisToAngles( flash.axis, angles ); +// JPW NERVE + weaponNum = BG_FindAmmoForWeapon( WP_FLAMETHROWER ); + if ( ps ) { + if ( ps->ammoclip[weaponNum] ) { + CG_FireFlameChunks( cent, flash.origin, angles, 1.0, qfalse ); + } + } else { + CG_FireFlameChunks( cent, flash.origin, angles, 1.0, qfalse ); + } +// jpw + } + } + } +} + +/* +============== +CG_AddViewWeapon + +Add the weapon, and flash for the player's view +============== +*/ +void CG_AddViewWeapon( playerState_t *ps ) { + refEntity_t hand; + float fovOffset; + vec3_t angles; + vec3_t gunoff; + weaponInfo_t *weapon; + + if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) { + return; + } + + if ( ps->pm_type == PM_INTERMISSION ) { + return; + } + + // no gun if in third person view + if ( cg.renderingThirdPerson ) { + return; + } + + if ( cg.editingSpeakers ) { + return; + } + + // allow the gun to be completely removed + if ( ( !cg_drawGun.integer ) ) { + vec3_t origin; + + //bani - #589 + if ( cg.predictedPlayerState.eFlags & EF_FIRING && !( cg.predictedPlayerState.eFlags & ( EF_MG42_ACTIVE | EF_MOUNTEDTANK ) ) ) { + // special hack for flamethrower... + VectorCopy( cg.refdef_current->vieworg, origin ); + + VectorMA( origin, 18, cg.refdef_current->viewaxis[0], origin ); + VectorMA( origin, -7, cg.refdef_current->viewaxis[1], origin ); + VectorMA( origin, -4, cg.refdef_current->viewaxis[2], origin ); + + // Ridah, Flamethrower effect + CG_FlamethrowerFlame( &cg.predictedPlayerEntity, origin ); + } + + if ( cg.binocZoomTime ) { + if ( cg.binocZoomTime < 0 ) { + if ( -cg.binocZoomTime + 500 + 200 < cg.time ) { + cg.binocZoomTime = 0; + } + } else { + if ( cg.binocZoomTime + 500 < cg.time ) { + trap_SendConsoleCommand( "+zoom\n" ); + cg.binocZoomTime = 0; + } else { + } + } + } + + return; + } + + // don't draw if testing a gun model + if ( cg.testGun ) { + return; + } + + if ( ps->eFlags & EF_MG42_ACTIVE || ps->eFlags & EF_AAGUN_ACTIVE ) { + return; + } + + // drop gun lower at higher fov + if ( cg_fov.integer > 90 ) { + fovOffset = -0.2 * ( cg_fov.integer - 90 ); + } else { + fovOffset = 0; + } + + // Gordon: mounted gun drawing + if ( ps->eFlags & EF_MOUNTEDTANK ) { + // FIXME: Arnout: HACK dummy model to just draw _something_ + refEntity_t flash; + + memset( &hand, 0, sizeof( hand ) ); + CG_CalculateWeaponPosition( hand.origin, angles ); + AnglesToAxis( angles, hand.axis ); + hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; + + if ( cg_entities[cg_entities[cg_entities[ ps->clientNum ].tagParent].tankparent].currentState.density & 8 ) { // should we use a browning? + hand.hModel = cgs.media.hMountedFPBrowning; + } else { + hand.hModel = cgs.media.hMountedFPMG42; + } + + //gunoff[0] = cg_gun_x.value; + //gunoff[1] = cg_gun_y.value; + //gunoff[2] = cg_gun_z.value; + + gunoff[0] = 20; + if ( cg.time - cg.predictedPlayerEntity.muzzleFlashTime < MUZZLE_FLASH_TIME ) { + gunoff[0] += random() * 2.f; + } + VectorMA( hand.origin, gunoff[0], cg.refdef_current->viewaxis[0], hand.origin ); + VectorMA( hand.origin, -10, cg.refdef_current->viewaxis[1], hand.origin ); + VectorMA( hand.origin, ( -8 + fovOffset ), cg.refdef_current->viewaxis[2], hand.origin ); + + CG_AddWeaponWithPowerups( &hand, cg.predictedPlayerEntity.currentState.powerups, ps, &cg.predictedPlayerEntity ); + + if ( cg.time - cg.predictedPlayerEntity.overheatTime < 3000 ) { + if ( !( rand() % 3 ) ) { + float alpha; + alpha = 1.0f - ( (float)( cg.time - cg.predictedPlayerEntity.overheatTime ) / 3000.0f ); + alpha *= 0.25f; // .25 max alpha + CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, cg.tankflashorg, 1000, 8, 20, 30, alpha, 8.f ); + } + } + + { + memset( &flash, 0, sizeof( flash ) ); + flash.renderfx = ( RF_LIGHTING_ORIGIN | RF_DEPTHHACK ); + flash.hModel = cgs.media.mg42muzzleflash; + + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = crandom() * 10; + AnglesToAxis( angles, flash.axis ); + + CG_PositionRotatedEntityOnTag( &flash, &hand, "tag_flash" ); + + VectorMA( flash.origin, 22, flash.axis[0], flash.origin ); + + VectorCopy( flash.origin, cg.tankflashorg ); + + if ( cg.time - cg.predictedPlayerEntity.muzzleFlashTime < MUZZLE_FLASH_TIME ) { + trap_R_AddRefEntityToScene( &flash ); + } + } + return; + } + + if ( ps->weapon > WP_NONE ) { + weapon = &cg_weapons[ ps->weapon ]; + + memset( &hand, 0, sizeof( hand ) ); + + // set up gun position + CG_CalculateWeaponPosition( hand.origin, angles ); + + gunoff[0] = cg_gun_x.value; + gunoff[1] = cg_gun_y.value; + gunoff[2] = cg_gun_z.value; + +//----(SA) removed + + VectorMA( hand.origin, gunoff[0], cg.refdef_current->viewaxis[0], hand.origin ); + VectorMA( hand.origin, gunoff[1], cg.refdef_current->viewaxis[1], hand.origin ); + VectorMA( hand.origin, ( gunoff[2] + fovOffset ), cg.refdef_current->viewaxis[2], hand.origin ); + + AnglesToAxis( angles, hand.axis ); + + if ( cg_gun_frame.integer ) { + hand.frame = hand.oldframe = cg_gun_frame.integer; + hand.backlerp = 0; + } else { // get the animation state + if ( cg.binocZoomTime ) { + if ( cg.binocZoomTime < 0 ) { + if ( -cg.binocZoomTime + 500 + 200 < cg.time ) { + cg.binocZoomTime = 0; + } else { + if ( -cg.binocZoomTime + 200 < cg.time ) { + CG_ContinueWeaponAnim( WEAP_ALTSWITCHFROM ); + } else { + CG_ContinueWeaponAnim( WEAP_IDLE2 ); + } + } + } else { + if ( cg.binocZoomTime + 500 < cg.time ) { + trap_SendConsoleCommand( "+zoom\n" ); + cg.binocZoomTime = 0; + CG_ContinueWeaponAnim( WEAP_IDLE2 ); + } else { + CG_ContinueWeaponAnim( WEAP_ALTSWITCHTO ); + } + } + } + CG_WeaponAnimation( ps, weapon, &hand.oldframe, &hand.frame, &hand.backlerp ); //----(SA) changed + } + + + hand.hModel = weapon->handsModel; + hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; //----(SA) + + // add everything onto the hand + CG_AddPlayerWeapon( &hand, ps, &cg.predictedPlayerEntity ); + // Ridah + + } +} + +/* +============================================================================== + +WEAPON SELECTION + +============================================================================== +*/ + +#define WP_ICON_X 38 // new sizes per MK +#define WP_ICON_X_WIDE 72 // new sizes per MK +#define WP_ICON_Y 38 +#define WP_ICON_SPACE_Y 10 +#define WP_DRAW_X 640 - WP_ICON_X - 4 // 4 is 'selected' border width +#define WP_DRAW_X_WIDE 640 - WP_ICON_X_WIDE - 4 +#define WP_DRAW_Y 4 + +// secondary fire icons +#define WP_ICON_SEC_X 18 // new sizes per MK +#define WP_ICON_SEC_Y 18 + +/* +============== +CG_WeaponHasAmmo + check for ammo +============== +*/ +static qboolean CG_WeaponHasAmmo( int i ) { + // ydnar: certain weapons don't have ammo + if ( i == WP_KNIFE || i == WP_PLIERS ) { + return qtrue; + } + + if ( !( cg.predictedPlayerState.ammo[BG_FindAmmoForWeapon( i )] ) && + !( cg.predictedPlayerState.ammoclip[BG_FindClipForWeapon( i )] ) ) { + return qfalse; + } + + return qtrue; +} + +/* +=============== +CG_WeaponSelectable +=============== +*/ +qboolean CG_WeaponSelectable( int i ) { + + // allow the player to unselect all weapons +// if(i == WP_NONE) +// return qtrue; + + // if holding a melee weapon (chair/shield/etc.) only allow single-handed weapons +/* if(cg.snap->ps.eFlags & EF_MELEE_ACTIVE) { + if(!(WEAPS_ONE_HANDED & (1<= WP_NUM_WEAPONS ) { + if ( bank ) { + *bank = 0; + } + if ( cycle ) { + *cycle = 0; + } + return 0; + } + + for ( bnk = 0; bnk < MAX_WEAP_BANKS_MP; bnk++ ) { + for ( cyc = 0; cyc < MAX_WEAPS_IN_BANK_MP; cyc++ ) { + + if ( !weapBanksMultiPlayer[bnk][cyc] ) { + break; + } + + // found the current weapon + if ( weapBanksMultiPlayer[bnk][cyc] == weapnum ) { + if ( bank ) { + *bank = bnk; + } + if ( cycle ) { + *cycle = cyc; + } + return 1; + } +// jpw + } + } + + // failed to find the weapon in the table + // probably an alternate + + return 0; +} + +/* +============== +getNextWeapInBank + Pass in a bank and cycle and this will return the next valid weapon higher in the cycle. + if the weap passed in is above highest in a cycle (MAX_WEAPS_IN_BANK), this will safely loop around +============== +*/ +static int getNextWeapInBank( int bank, int cycle ) { + + cycle++; + + cycle = cycle % MAX_WEAPS_IN_BANK_MP; + + if ( weapBanksMultiPlayer[bank][cycle] ) { // return next weapon in bank if there is one + return weapBanksMultiPlayer[bank][cycle]; + } else { // return first in bank + return weapBanksMultiPlayer[bank][0]; + } +} + +static int getNextWeapInBankBynum( int weapnum ) { + int bank, cycle; + + if ( !CG_WeaponIndex( weapnum, &bank, &cycle ) ) { + return weapnum; + } + + return getNextWeapInBank( bank, cycle ); +} + +/* +============== +getPrevWeapInBank + Pass in a bank and cycle and this will return the next valid weapon lower in the cycle. + if the weap passed in is the lowest in a cycle (0), this will loop around to the + top (MAX_WEAPS_IN_BANK-1) and start down from there looking for a valid weapon position +============== +*/ +static int getPrevWeapInBank( int bank, int cycle ) { + cycle--; + if ( cycle < 0 ) { + cycle = MAX_WEAPS_IN_BANK_MP - 1; + } + + + while ( !weapBanksMultiPlayer[bank][cycle] ) { + cycle--; + + if ( cycle < 0 ) { + cycle = MAX_WEAPS_IN_BANK_MP - 1; + } + } + return weapBanksMultiPlayer[bank][cycle]; +} + + +static int getPrevWeapInBankBynum( int weapnum ) { + int bank, cycle; + + if ( !CG_WeaponIndex( weapnum, &bank, &cycle ) ) { + return weapnum; + } + + return getPrevWeapInBank( bank, cycle ); +} + + + +/* +============== +getNextBankWeap + Pass in a bank and cycle and this will return the next valid weapon in a higher bank. + sameBankPosition: if there's a weapon in the next bank at the same cycle, + return that (colt returns thompson for example) rather than the lowest weapon +============== +*/ +static int getNextBankWeap( int bank, int cycle, qboolean sameBankPosition ) { + bank++; + + bank = bank % MAX_WEAP_BANKS_MP; + + if ( sameBankPosition && weapBanksMultiPlayer[bank][cycle] ) { + return weapBanksMultiPlayer[bank][cycle]; + } else { + return weapBanksMultiPlayer[bank][0]; + } +} + +/* +============== +getPrevBankWeap + Pass in a bank and cycle and this will return the next valid weapon in a lower bank. + sameBankPosition: if there's a weapon in the prev bank at the same cycle, + return that (thompson returns colt for example) rather than the highest weapon +============== +*/ +static int getPrevBankWeap( int bank, int cycle, qboolean sameBankPosition ) { + int i; + + bank--; + + if ( bank < 0 ) { // don't go below 0, cycle up to top + bank += MAX_WEAP_BANKS_MP; // JPW NERVE + + } + bank = bank % MAX_WEAP_BANKS_MP; + + if ( sameBankPosition && weapBanksMultiPlayer[bank][cycle] ) { + return weapBanksMultiPlayer[bank][cycle]; + } else + { // find highest weap in bank + for ( i = MAX_WEAPS_IN_BANK_MP - 1; i >= 0; i-- ) { + if ( weapBanksMultiPlayer[bank][i] ) { + return weapBanksMultiPlayer[bank][i]; + } + } + + // if it gets to here, no valid weaps in this bank, go down another bank + return getPrevBankWeap( bank, cycle, sameBankPosition ); + } +} + +/* +============== +getAltWeapon +============== +*/ +static int getAltWeapon( int weapnum ) { +/* if(weapnum > MAX_WEAP_ALTS) Gordon: seems unneeded + return weapnum;*/ + + if ( weapAlts[weapnum] ) { + return weapAlts[weapnum]; + } + + return weapnum; +} + +/* +============== +getEquivWeapon + return the id of the opposite team's weapon. + Passing the weapnum of the mp40 returns the id of the thompson, and likewise + passing the weapnum of the thompson returns the id of the mp40. + No equivalent available will return the weapnum passed in. +============== +*/ +int getEquivWeapon( int weapnum ) { + int num = weapnum; + + switch ( weapnum ) { + // going from german to american + case WP_LUGER: num = WP_COLT; break; + case WP_MP40: num = WP_THOMPSON; break; + case WP_GRENADE_LAUNCHER: num = WP_GRENADE_PINEAPPLE; break; + case WP_KAR98: num = WP_CARBINE; break; + case WP_SILENCER: num = WP_SILENCED_COLT; break; + + // going from american to german + case WP_COLT: num = WP_LUGER; break; + case WP_THOMPSON: num = WP_MP40; break; + case WP_GRENADE_PINEAPPLE: num = WP_GRENADE_LAUNCHER; break; + case WP_CARBINE: num = WP_KAR98; break; + case WP_SILENCED_COLT: num = WP_SILENCER; break; + } + return num; +} + + + + +/* +============== +CG_SetSniperZoom +============== +*/ + +void CG_SetSniperZoom( int lastweap, int newweap ) { + int zoomindex; + + if ( lastweap == newweap ) { + return; + } + + // Keep binocs swaying + if ( !( cg.predictedPlayerState.eFlags & EF_ZOOMING ) ) { + cg.zoomval = 0; + } + cg.zoomedScope = 0; + + // check for fade-outs + switch ( lastweap ) { + case WP_FG42SCOPE: +// cg.zoomedScope = 1; // TODO: add to zoomTable +// cg.zoomTime = cg.time; + break; + case WP_GARAND_SCOPE: +// cg.zoomedScope = 500; // TODO: add to zoomTable +// cg.zoomTime = cg.time; + break; + case WP_K43_SCOPE: +// cg.zoomedScope = 500; // TODO: add to zoomTable +// cg.zoomTime = cg.time; + break; + } + + switch ( newweap ) { + + default: + return; // no sniper zoom, get out. + + case WP_FG42SCOPE: + cg.zoomval = cg_zoomDefaultSniper.value; // JPW NERVE changed from defaultFG per atvi req + cg.zoomedScope = 1; // TODO: add to zoomTable + zoomindex = ZOOM_SNIPER; // JPW NERVE was FG42SCOPE + break; + case WP_GARAND_SCOPE: + cg.zoomval = cg_zoomDefaultSniper.value; + cg.zoomedScope = 900; // TODO: add to zoomTable + zoomindex = ZOOM_SNIPER; + break; + case WP_K43_SCOPE: + cg.zoomval = cg_zoomDefaultSniper.value; + cg.zoomedScope = 900; // TODO: add to zoomTable + zoomindex = ZOOM_SNIPER; + break; + } + + // constrain user preferred fov to weapon limitations + if ( cg.zoomval > zoomTable[zoomindex][ZOOM_OUT] ) { + cg.zoomval = zoomTable[zoomindex][ZOOM_OUT]; + } + if ( cg.zoomval < zoomTable[zoomindex][ZOOM_IN] ) { + cg.zoomval = zoomTable[zoomindex][ZOOM_IN]; + } + + cg.zoomTime = cg.time; +} + +/* +============== +CG_PlaySwitchSound + Get special switching sounds if they're there +============== +*/ +void CG_PlaySwitchSound( int lastweap, int newweap ) { +// weaponInfo_t *weap; +// weap = &cg_weapons[ ent->weapon ]; + sfxHandle_t switchsound; + + switchsound = cgs.media.selectSound; + + if ( getAltWeapon( lastweap ) == newweap ) { // alt switch + switch ( newweap ) { + case WP_SILENCER: + case WP_LUGER: + case WP_SILENCED_COLT: + case WP_COLT: + case WP_GPG40: + case WP_M7: + case WP_MORTAR: + case WP_MORTAR_SET: + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + switchsound = cg_weapons[newweap].switchSound; + break; + case WP_CARBINE: + case WP_KAR98: + if ( cg.predictedPlayerState.ammoclip[lastweap] ) { + switchsound = cg_weapons[newweap].switchSound; + } + break; + default: + return; + } + } else { + return; + } + + trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_WEAPON, switchsound ); +} + +/* +============== +CG_FinishWeaponChange +============== +*/ +void CG_FinishWeaponChange( int lastweap, int newweap ) { + int newbank; + + if ( cg.binocZoomTime ) { + return; + } + + cg.mortarImpactTime = -2; + + switch ( newweap ) { + case WP_LUGER: + if ( ( cg.pmext.silencedSideArm & 1 ) && lastweap != WP_SILENCER ) { + newweap = WP_SILENCER; + cg.weaponSelect = newweap; + } + break; + case WP_SILENCER: + if ( !( cg.pmext.silencedSideArm & 1 ) && lastweap != WP_LUGER ) { + newweap = WP_LUGER; + cg.weaponSelect = newweap; + } + break; + case WP_COLT: + if ( ( cg.pmext.silencedSideArm & 1 ) && lastweap != WP_SILENCED_COLT ) { + newweap = WP_SILENCED_COLT; + cg.weaponSelect = newweap; + } + break; + case WP_SILENCED_COLT: + if ( !( cg.pmext.silencedSideArm & 1 ) && lastweap != WP_COLT ) { + newweap = WP_COLT; + cg.weaponSelect = newweap; + } + break; + case WP_CARBINE: + if ( ( cg.pmext.silencedSideArm & 2 ) && lastweap != WP_M7 ) { + newweap = WP_M7; + cg.weaponSelect = newweap; + } + break; + case WP_M7: + if ( !( cg.pmext.silencedSideArm & 2 ) && lastweap != WP_CARBINE ) { + newweap = WP_CARBINE; + cg.weaponSelect = newweap; + } + break; + case WP_KAR98: + if ( ( cg.pmext.silencedSideArm & 2 ) && lastweap != WP_GPG40 ) { + newweap = WP_GPG40; + cg.weaponSelect = newweap; + } + break; + case WP_GPG40: + if ( !( cg.pmext.silencedSideArm & 2 ) && lastweap != WP_KAR98 ) { + newweap = WP_KAR98; + cg.weaponSelect = newweap; + } + break; +// case WP_MEDIC_SYRINGE: +// if((cg.pmext.silencedSideArm & 4) && lastweap != WP_MEDIC_ADRENALINE) { +// newweap = WP_MEDIC_ADRENALINE; +// cg.weaponSelect = newweap; +// } +// break; +// case WP_MEDIC_ADRENALINE: +// if(!(cg.pmext.silencedSideArm & 4) && lastweap != WP_MEDIC_SYRINGE) { +// newweap = WP_MEDIC_SYRINGE; +// cg.weaponSelect = newweap; +// } +// break; + } + + if ( lastweap == WP_BINOCULARS && cg.snap->ps.eFlags & EF_ZOOMING ) { + trap_SendConsoleCommand( "-zoom\n" ); + } + + cg.weaponSelectTime = cg.time; // flash the weapon icon + + // NERVE - SMF + if ( cg.newCrosshairIndex ) { + trap_Cvar_Set( "cg_drawCrossHair", va( "%d", cg.newCrosshairIndex - 1 ) ); + } + cg.newCrosshairIndex = 0; + // -NERVE - SMF + + // remember which weapon in this bank was last selected so when cycling back + // to this bank, that weap will be highlighted first + if ( CG_WeaponIndex( newweap, &newbank, NULL ) ) { + cg.lastWeapSelInBank[newbank] = newweap; + } + + if ( lastweap == newweap ) { // no need to do any more than flash the icon + return; + } + + CG_PlaySwitchSound( lastweap, newweap ); // Gordon: grabbed from SP + + CG_SetSniperZoom( lastweap, newweap ); + + // setup for a user call to CG_LastWeaponUsed_f() + if ( lastweap == cg.lastFiredWeapon ) { + // don't set switchback for some weaps... + switch ( lastweap ) { + case WP_FG42SCOPE: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + break; + default: + cg.switchbackWeapon = lastweap; + break; + } + } else { + // if this ended up having the switchback be the same + // as the new weapon, set the switchback to the prev + // selected weapon will become the switchback + if ( cg.switchbackWeapon == newweap ) { + cg.switchbackWeapon = lastweap; + } + } + + cg.weaponSelect = newweap; +} + +extern pmove_t cg_pmove; + +/* +============== +CG_AltfireWeapon_f + for example, switching between WP_MAUSER and WP_SNIPERRIFLE +============== +*/ +void CG_AltWeapon_f( void ) { + int original, num; + + if ( !cg.snap ) { + return; + } + + // Overload for spec mode when following + if ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) || cg.mvTotalClients > 0 ) { + return; + } + + // Need ground for this + if ( cg.weaponSelect == WP_MORTAR ) { + int contents; + vec3_t point; + + if ( cg.predictedPlayerState.groundEntityNum == ENTITYNUM_NONE ) { + return; + } + if ( !cg.predictedPlayerState.ammoclip[WP_MORTAR] ) { + return; + } + + if ( cg.predictedPlayerState.eFlags & EF_PRONE ) { + return; + } + + if ( cg_pmove.waterlevel == 3 ) { + return; + } + + // ydnar: don't allow set if moving + if ( VectorLengthSquared( cg.snap->ps.velocity ) ) { + return; + } + + // eurgh, need it here too else we play sounds :/ + point[0] = cg.snap->ps.origin[0]; + point[1] = cg.snap->ps.origin[1]; + point[2] = cg.snap->ps.origin[2] + cg.snap->ps.crouchViewHeight; + contents = CG_PointContents( point, cg.snap->ps.clientNum ); + if ( contents & MASK_WATER ) { + return; + } + } else if ( cg.weaponSelect == WP_MOBILE_MG42 ) { + if ( !( cg.predictedPlayerState.eFlags & EF_PRONE ) ) { + return; + } + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + // Don't try to switch when in the middle of reloading. + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) { + return; + } + + original = cg.weaponSelect; + + num = getAltWeapon( original ); + + if ( original == WP_BINOCULARS ) { + /*if(cg.snap->ps.eFlags & EF_ZOOMING) { + trap_SendConsoleCommand( "-zoom\n" ); + } else { + trap_SendConsoleCommand( "+zoom\n" ); + }*/ + if ( cg.snap->ps.eFlags & EF_ZOOMING ) { + trap_SendConsoleCommand( "-zoom\n" ); + cg.binocZoomTime = -cg.time; + } else { + if ( !cg.binocZoomTime ) { + cg.binocZoomTime = cg.time; + } + } + } + + // Arnout: don't allow another weapon switch when we're still swapping the gpg40, to prevent animation breaking + if ( ( cg.snap->ps.weaponstate == WEAPON_RAISING || cg.snap->ps.weaponstate == WEAPON_DROPPING ) && + ( ( original == WP_GPG40 || num == WP_GPG40 || original == WP_M7 || num == WP_M7 ) || + ( original == WP_SILENCER || num == WP_SILENCER || original == WP_SILENCED_COLT || num == WP_SILENCED_COLT ) || + ( original == WP_AKIMBO_SILENCEDCOLT || num == WP_AKIMBO_SILENCEDCOLT || original == WP_AKIMBO_SILENCEDLUGER || num == WP_AKIMBO_SILENCEDLUGER ) || + ( original == WP_MORTAR_SET || num == WP_MORTAR_SET ) || + ( original == WP_MOBILE_MG42_SET || num == WP_MOBILE_MG42_SET ) ) ) { + return; + } + + if ( CG_WeaponSelectable( num ) ) { // new weapon is valid + CG_FinishWeaponChange( original, num ); + } +} + + +/* +============== +CG_NextWeap + + switchBanks - curweap is the last in a bank, 'qtrue' means go to the next available bank, 'qfalse' means loop to the head of the bank +============== +*/ +void CG_NextWeap( qboolean switchBanks ) { + int bank = 0, cycle = 0, newbank = 0, newcycle = 0; + int num, curweap; + qboolean nextbank = qfalse; // need to switch to the next bank of weapons? + int i, j; + + num = curweap = cg.weaponSelect; + + if ( curweap == WP_MORTAR_SET || curweap == WP_MOBILE_MG42_SET ) { + return; + } + + switch ( num ) { + case WP_SILENCER: + curweap = num = WP_LUGER; + break; + case WP_SILENCED_COLT: + curweap = num = WP_COLT; + break; + case WP_GPG40: + curweap = num = WP_KAR98; + break; + case WP_M7: + curweap = num = WP_CARBINE; + break; + case WP_MORTAR_SET: + curweap = num = WP_MORTAR; + break; + } + + CG_WeaponIndex( curweap, &bank, &cycle ); // get bank/cycle of current weapon + + // if you're using an alt mode weapon, try switching back to the parent first + if ( curweap >= WP_BEGINSECONDARY && curweap <= WP_LASTSECONDARY ) { + num = getAltWeapon( curweap ); // base any further changes on the parent + if ( CG_WeaponSelectable( num ) ) { // the parent was selectable, drop back to that + CG_FinishWeaponChange( curweap, num ); + return; + } + } + + if ( cg_cycleAllWeaps.integer || !switchBanks ) { + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + num = getNextWeapInBankBynum( num ); + + CG_WeaponIndex( num, NULL, &newcycle ); // get cycle of new weapon. if it's lower than the original, then it cycled around + + if ( switchBanks ) { + if ( newcycle <= cycle ) { + nextbank = qtrue; + break; + } + } else { // don't switch banks if you get to the end + + if ( num == curweap ) { // back to start, just leave it where it is + return; + } + } + + if ( CG_WeaponSelectable( num ) ) { + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + } + } else { + nextbank = qtrue; + } + + if ( nextbank ) { + for ( i = 0; i < MAX_WEAP_BANKS_MP; i++ ) { + if ( cg_cycleAllWeaps.integer ) { + num = getNextBankWeap( bank + i, cycle, qfalse ); // cycling all weaps always starts the next bank at the bottom + } else { + if ( cg.lastWeapSelInBank[bank + i + 1] ) { + num = cg.lastWeapSelInBank[bank + i + 1]; + } else { + num = getNextBankWeap( bank + i, cycle, qtrue ); + } + } + + if ( num == 0 ) { + continue; + } + +// if(num == WP_BINOCULARS) { +// continue; +// } + + if ( CG_WeaponSelectable( num ) ) { // first entry in bank was selectable, no need to scan the bank + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + + CG_WeaponIndex( num, &newbank, &newcycle ); // get the bank of the new weap + + for ( j = newcycle; j < MAX_WEAPS_IN_BANK_MP; j++ ) { + num = getNextWeapInBank( newbank, j ); + +/* if(num == WP_BINOCULARS) { + continue; + }*/ + + if ( CG_WeaponSelectable( num ) ) { // found selectable weapon + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + + num = 0; + } + + if ( num ) { // a selectable weapon was found in the current bank + break; + } + } + } + + CG_FinishWeaponChange( curweap, num ); //----(SA) +} + +/* +============== +CG_PrevWeap + + switchBanks - curweap is the last in a bank + 'qtrue' - go to the next available bank + 'qfalse' - loop to the head of the bank +============== +*/ +void CG_PrevWeap( qboolean switchBanks ) { + int bank = 0, cycle = 0, newbank = 0, newcycle = 0; + int num, curweap; + qboolean prevbank = qfalse; // need to switch to the next bank of weapons? + int i, j; + + num = curweap = cg.weaponSelect; + + if ( curweap == WP_MORTAR_SET || curweap == WP_MOBILE_MG42_SET ) { + return; + } + + switch ( num ) { + case WP_SILENCER: + curweap = num = WP_LUGER; + break; + case WP_SILENCED_COLT: + curweap = num = WP_COLT; + break; + case WP_GPG40: + curweap = num = WP_KAR98; + break; + case WP_M7: + curweap = num = WP_CARBINE; + break; + case WP_MORTAR_SET: + curweap = num = WP_MORTAR; + break; + } + + CG_WeaponIndex( curweap, &bank, &cycle ); // get bank/cycle of current weapon + + // if you're using an alt mode weapon, try switching back to the parent first + if ( curweap >= WP_BEGINSECONDARY && curweap <= WP_LASTSECONDARY ) { + num = getAltWeapon( curweap ); // base any further changes on the parent + if ( CG_WeaponSelectable( num ) ) { // the parent was selectable, drop back to that + CG_FinishWeaponChange( curweap, num ); + return; + } + } + + // initially, just try to find a lower weapon in the current bank + if ( cg_cycleAllWeaps.integer || !switchBanks ) { + for ( i = cycle; i >= 0; i-- ) { + num = getPrevWeapInBankBynum( num ); + + CG_WeaponIndex( num, NULL, &newcycle ); // get cycle of new weapon. if it's greater than the original, then it cycled around + + if ( switchBanks ) { + if ( newcycle > ( cycle - 1 ) ) { + prevbank = qtrue; + break; + } + } else { // don't switch banks if you get to the end + if ( num == curweap ) { // back to start, just leave it where it is + return; + } + } + +// if(num == WP_BINOCULARS) { +// continue; +// } + + if ( CG_WeaponSelectable( num ) ) { + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + } + } else { + prevbank = qtrue; + } + + // cycle to previous bank. + // if cycleAllWeaps: find highest weapon in bank + // else: try to find weap in bank that matches cycle position + // else: use base weap in bank + + if ( prevbank ) { + for ( i = 0; i < MAX_WEAP_BANKS_MP; i++ ) { + if ( cg_cycleAllWeaps.integer ) { + num = getPrevBankWeap( bank - i, cycle, qfalse ); // cycling all weaps always starts the next bank at the bottom + } else { + num = getPrevBankWeap( bank - i, cycle, qtrue ); + } + + if ( num == 0 ) { + continue; + } + + if ( CG_WeaponSelectable( num ) ) { // first entry in bank was selectable, no need to scan the bank + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + + CG_WeaponIndex( num, &newbank, &newcycle ); // get the bank of the new weap + + for ( j = MAX_WEAPS_IN_BANK_MP; j > 0; j-- ) { + num = getPrevWeapInBank( newbank, j ); + + if ( CG_WeaponSelectable( num ) ) { // found selectable weapon + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + + num = 0; + } + + if ( num ) { // a selectable weapon was found in the current bank + break; + } + } + } + + CG_FinishWeaponChange( curweap, num ); //----(SA) +} + + +/* +============== +CG_LastWeaponUsed_f +============== +*/ +void CG_LastWeaponUsed_f( void ) { + int lastweap; + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + if ( cg.weaponSelect == WP_MORTAR_SET || cg.weaponSelect == WP_MOBILE_MG42_SET ) { + return; + } + + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + // don't switchback if reloading (it nullifies the reload) + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) { + return; + } + + if ( !cg.switchbackWeapon ) { + cg.switchbackWeapon = cg.weaponSelect; + return; + } + + if ( CG_WeaponSelectable( cg.switchbackWeapon ) ) { + lastweap = cg.weaponSelect; + CG_FinishWeaponChange( cg.weaponSelect, cg.switchbackWeapon ); + } else { // switchback no longer selectable, reset cycle + cg.switchbackWeapon = 0; + } + +} + +/* +============== +CG_NextWeaponInBank_f +============== +*/ +void CG_NextWeaponInBank_f( void ) { + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + // this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel) + // for zooming (binocs/snooper/sniper/etc.) + if ( cg.zoomval ) { + if ( cg_useWeapsForZoom.integer == 1 ) { + CG_ZoomIn_f(); + return; + } else if ( cg_useWeapsForZoom.integer == 2 ) { + CG_ZoomOut_f(); + return; + } + } + + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + CG_NextWeap( qfalse ); +} + +/* +============== +CG_PrevWeaponInBank_f +============== +*/ +void CG_PrevWeaponInBank_f( void ) { + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + // this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel) + // for zooming (binocs/snooper/sniper/etc.) + if ( cg.zoomval ) { + if ( cg_useWeapsForZoom.integer == 2 ) { + CG_ZoomIn_f(); + return; + } else if ( cg_useWeapsForZoom.integer == 1 ) { + CG_ZoomOut_f(); + return; + } + } + + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + CG_PrevWeap( qfalse ); +} + + +/* +============== +CG_NextWeapon_f +============== +*/ +void CG_NextWeapon_f( void ) { + + if ( !cg.snap ) { + return; + } + + // Overload for MV clients + if ( cg.mvTotalClients > 0 ) { + CG_mvToggleView_f(); + return; + } + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + // this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel) + // for zooming (binocs/snooper/sniper/etc.) + if ( cg.zoomval ) { + if ( cg_useWeapsForZoom.integer == 1 ) { + CG_ZoomIn_f(); + return; + } else if ( cg_useWeapsForZoom.integer == 2 ) { + CG_ZoomOut_f(); + return; + } + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + // Don't try to switch when in the middle of reloading. + // cheatinfo: The server actually would let you switch if this check were not + // present, but would discard the reload. So the when you switched + // back you'd have to start the reload over. This seems bad, however + // the delay for the current reload is already in effect, so you'd lose + // the reload time twice. (the first pause for the current weapon reload, + // and the pause when you have to reload again 'cause you canceled this one) + + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) { + return; + } + + CG_NextWeap( qtrue ); +} + + +/* +============== +CG_PrevWeapon_f +============== +*/ +void CG_PrevWeapon_f( void ) { + if ( !cg.snap ) { + return; + } + + // Overload for MV clients + if ( cg.mvTotalClients > 0 ) { + CG_mvSwapViews_f(); + return; + } + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + // this cvar is an option that lets the player use his weapon switching keys (probably the mousewheel) + // for zooming (binocs/snooper/sniper/etc.) + if ( cg.zoomval ) { + if ( cg_useWeapsForZoom.integer == 1 ) { + CG_ZoomOut_f(); + return; + } else if ( cg_useWeapsForZoom.integer == 2 ) { + CG_ZoomIn_f(); + return; + } + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + // Don't try to switch when in the middle of reloading. + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) { + return; + } + + CG_PrevWeap( qtrue ); +} + + +/* +============== +CG_WeaponBank_f + weapon keys are not generally bound directly('bind 1 weapon 1'), + rather the key is bound to a given bank ('bind 1 weaponbank 1') +============== +*/ +void CG_WeaponBank_f( void ) { + int num, i, curweap; + int curbank = 0, curcycle = 0, bank = 0, cycle = 0; + + if ( !cg.snap ) { + return; + } + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + if ( cg.time - cg.weaponSelectTime < cg_weaponCycleDelay.integer ) { + return; // force pause so holding it down won't go too fast + + } + if ( cg.weaponSelect == WP_MORTAR_SET || cg.weaponSelect == WP_MOBILE_MG42_SET ) { + return; + } + + cg.weaponSelectTime = cg.time; // flash the current weapon icon + + // Don't try to switch when in the middle of reloading. + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) { + return; + } + + bank = atoi( CG_Argv( 1 ) ); + + if ( bank <= 0 || bank > MAX_WEAP_BANKS_MP ) { + return; + } + + curweap = cg.weaponSelect; + CG_WeaponIndex( curweap, &curbank, &curcycle ); // get bank/cycle of current weapon + + if ( !cg.lastWeapSelInBank[bank] ) { + num = weapBanksMultiPlayer[bank][0]; + cycle -= 1; // cycle up to first weap + } else { + num = cg.lastWeapSelInBank[bank]; + CG_WeaponIndex( num, &bank, &cycle ); + if ( bank != curbank ) { + cycle -= 1; + } + } + + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + num = getNextWeapInBank( bank, cycle + i ); + + if ( CG_WeaponSelectable( num ) ) { + break; + } else { + qboolean found = qfalse; + switch ( num ) { + case WP_CARBINE: + if ( ( found = CG_WeaponSelectable( WP_M7 ) ) ) { + num = WP_M7; + } + break; + case WP_KAR98: + if ( ( found = CG_WeaponSelectable( WP_GPG40 ) ) ) { + num = WP_GPG40; + } + break; + } + + if ( found ) { + break; + } + } + } + + if ( i == MAX_WEAPS_IN_BANK_MP ) { + return; + } + + // Arnout: don't allow another weapon switch when we're still swapping the gpg40, to prevent animation breaking + if ( ( cg.snap->ps.weaponstate == WEAPON_RAISING || cg.snap->ps.weaponstate == WEAPON_DROPPING ) && + ( ( curweap == WP_GPG40 || num == WP_GPG40 || curweap == WP_M7 || num == WP_M7 ) || + ( curweap == WP_SILENCER || num == WP_SILENCER || curweap == WP_SILENCED_COLT || num == WP_SILENCED_COLT ) || + ( curweap == WP_MORTAR_SET || num == WP_MORTAR_SET ) ) ) { + return; + } + + CG_FinishWeaponChange( curweap, num ); + +} + +/* +=============== +CG_Weapon_f +=============== +*/ +void CG_Weapon_f( void ) { + int num; +// int bank = 0, cycle = 0, newbank = 0, newcycle = 0; +// qboolean banked = qfalse; + + if ( !cg.snap ) { + return; + } + + //fretn - #447 + //osp-rtcw & et pause bug + if ( cg.snap->ps.pm_type == PM_FREEZE ) { + return; + } + + if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { + return; + } + + if ( cg.weaponSelect == WP_MORTAR_SET || cg.weaponSelect == WP_MOBILE_MG42_SET ) { + return; + } + + num = atoi( CG_Argv( 1 ) ); + +// JPW NERVE +// weapon bind should execute weaponbank instead -- for splitting out class weapons, per Id request + if ( num < MAX_WEAP_BANKS_MP ) { + CG_WeaponBank_f(); + } + return; +// jpw + +/* cg.weaponSelectTime = cg.time; // flash the current weapon icon + + // Don't try to switch when in the middle of reloading. + if ( cg.snap->ps.weaponstate == WEAPON_RELOADING ) + return; + + + if ( num <= WP_NONE || num > WP_NUM_WEAPONS ) { + return; + } + + curweap = cg.weaponSelect; + + CG_WeaponIndex(curweap, &bank, &cycle); // get bank/cycle of current weapon + banked = CG_WeaponIndex(num, &newbank, &newcycle); // get bank/cycle of requested weapon + + // the new weapon was not found in the reglar banks + // assume the player want's to go directly to it if possible + if(!banked) { + if(CG_WeaponSelectable(num)) { + CG_FinishWeaponChange(curweap, num); + return; + } + } + + if(bank != newbank) + cycle = newcycle - 1; // drop down one from the requested weap's cycle so it will + // try to initially cycle up to the requested weapon + + for(i = 0; i < MAX_WEAPS_IN_BANK; i++) { + num = getNextWeapInBank(newbank, cycle+i); + + if(num == curweap) // no other weapons in bank + return; + + if(CG_WeaponSelectable(num)) { + break; + } + } + + if(i == MAX_WEAPS_IN_BANK) + return; + + CG_FinishWeaponChange(curweap, num);*/ +} + +/* +=================== +CG_OutOfAmmoChange + +The current weapon has just run out of ammo +=================== +*/ +void CG_OutOfAmmoChange( qboolean allowforceswitch ) { + int i; + int bank, cycle; + int equiv = WP_NONE; + + // + // trivial switching + // + + if ( cg.weaponSelect == WP_PLIERS || ( cg.weaponSelect == WP_SATCHEL_DET && cg.predictedPlayerState.ammo[WP_SATCHEL_DET] ) ) { + return; + } + + if ( allowforceswitch ) { + if ( cg.weaponSelect == WP_SMOKE_BOMB ) { + if ( CG_WeaponSelectable( WP_LUGER ) ) { + cg.weaponSelect = WP_LUGER; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, WP_LUGER ); + return; + } else if ( CG_WeaponSelectable( WP_COLT ) ) { + cg.weaponSelect = WP_COLT; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, WP_COLT ); + return; + } + } else if ( cg.weaponSelect == WP_LANDMINE ) { + if ( CG_WeaponSelectable( WP_PLIERS ) ) { + cg.weaponSelect = WP_PLIERS; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, WP_PLIERS ); + return; + } + } else if ( cg.weaponSelect == WP_SATCHEL ) { + if ( CG_WeaponSelectable( WP_SATCHEL_DET ) ) { + cg.weaponSelect = WP_SATCHEL_DET; + return; + } + } else if ( cg.weaponSelect == WP_MORTAR_SET ) { + cg.weaponSelect = WP_MORTAR; + return; + } else if ( cg.weaponSelect == WP_MOBILE_MG42_SET ) { + cg.weaponSelect = WP_MOBILE_MG42; + return; + } + + // JPW NERVE -- early out if we just dropped dynamite, go to pliers + if ( cg.weaponSelect == WP_DYNAMITE ) { + if ( CG_WeaponSelectable( WP_PLIERS ) ) { + cg.weaponSelect = WP_PLIERS; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, WP_PLIERS ); + return; + } + } + + // JPW NERVE -- early out if we just fired Panzerfaust, go to pistola, then grenades + if ( cg.weaponSelect == WP_PANZERFAUST ) { + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + if ( CG_WeaponSelectable( weapBanksMultiPlayer[2][i] ) ) { // find a pistol + cg.weaponSelect = weapBanksMultiPlayer[2][i]; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); + return; + } + } + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + if ( CG_WeaponSelectable( weapBanksMultiPlayer[4][i] ) ) { // find a grenade + cg.weaponSelect = weapBanksMultiPlayer[4][i]; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); + return; + } + } + } + + // if you're using an alt mode weapon, try switching back to the parent + // otherwise, switch to the equivalent if you've got it + if ( cg.weaponSelect >= WP_BEGINSECONDARY && cg.weaponSelect <= WP_LASTSECONDARY ) { + cg.weaponSelect = equiv = getAltWeapon( cg.weaponSelect ); // base any further changes on the parent + if ( CG_WeaponSelectable( equiv ) ) { // the parent was selectable, drop back to that + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA) + return; + } + } + + + // now try the opposite team's equivalent weap + equiv = getEquivWeapon( cg.weaponSelect ); + + if ( equiv != cg.weaponSelect && CG_WeaponSelectable( equiv ) ) { + cg.weaponSelect = equiv; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA) + return; + } + } + + // + // more complicated selection + // + + // didn't have available alternative or equivalent, try another weap in the bank + CG_WeaponIndex( cg.weaponSelect, &bank, &cycle ); // get bank/cycle of current weapon + + // JPW NERVE -- more useful weapon changes -- check if rifle or pistol is still working, and use that if available + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + if ( CG_WeaponSelectable( weapBanksMultiPlayer[3][i] ) ) { // find a rifle + cg.weaponSelect = weapBanksMultiPlayer[3][i]; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); + return; + } + } + for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + if ( CG_WeaponSelectable( weapBanksMultiPlayer[2][i] ) ) { // find a pistol + cg.weaponSelect = weapBanksMultiPlayer[2][i]; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); + return; + } + } + + // otherwise just do something + for ( i = cycle; i < MAX_WEAPS_IN_BANK_MP; i++ ) { + equiv = getNextWeapInBank( bank, i ); + if ( CG_WeaponSelectable( equiv ) ) { // found a reasonable replacement + cg.weaponSelect = equiv; + CG_FinishWeaponChange( cg.predictedPlayerState.weapon, cg.weaponSelect ); //----(SA) + return; + } + } + + + // still nothing available, just go to the next + // available weap using the regular selection scheme + CG_NextWeap( qtrue ); + +} + +/* +=================================================================================================== + +WEAPON EVENTS + +=================================================================================================== +*/ + +void CG_MG42EFX( centity_t *cent ) { + // Arnout: complete overhaul of this one + centity_t *mg42; + int num; + vec3_t forward, point; + refEntity_t flash; + + // find the mg42 we're attached to + for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { + mg42 = &cg_entities[ cg.snap->entities[ num ].number ]; + if ( mg42->currentState.eType == ET_MG42_BARREL && + mg42->currentState.otherEntityNum == cent->currentState.number ) { + // found it, clamp behind gun + + VectorCopy( mg42->currentState.pos.trBase, point ); + //AngleVectors (mg42->s.apos.trBase, forward, NULL, NULL); + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + VectorMA( point, 40, forward, point ); + + memset( &flash, 0, sizeof( flash ) ); + flash.renderfx = RF_LIGHTING_ORIGIN; + flash.hModel = cgs.media.mg42muzzleflash; + + VectorCopy( point, flash.origin ); + AnglesToAxis( cent->lerpAngles, flash.axis ); + + trap_R_AddRefEntityToScene( &flash ); + + // ydnar: add dynamic light + trap_R_AddLightToScene( flash.origin, 320, 1.25 + ( rand() & 31 ) / 128, + 1.0, 0.6, 0.23, 0, 0 ); + + return; + } + } + +} + +// Note to self this is dead code +/*void CG_FLAKEFX (centity_t *cent, int whichgun) +{ + entityState_t *ent; + vec3_t forward, right, up; + vec3_t point; + refEntity_t flash; + + ent = ¢->currentState; + + VectorCopy (cent->currentState.pos.trBase, point); + AngleVectors (cent->currentState.apos.trBase, forward, right, up); + + // gun 1 and 2 were switched + if (whichgun == 2) + { + VectorMA (point, 136, forward, point); + VectorMA (point, 31, up, point); + VectorMA (point, 22, right, point); + } + else if (whichgun == 1) + { + VectorMA (point, 136, forward, point); + VectorMA (point, 31, up, point); + VectorMA (point, -22, right, point); + } + else if (whichgun == 3) + { + VectorMA (point, 136, forward, point); + VectorMA (point, 10, up, point); + VectorMA (point, 22, right, point); + } + else if (whichgun == 4) + { + VectorMA (point, 136, forward, point); + VectorMA (point, 10, up, point); + VectorMA (point, -22, right, point); + } + + trap_R_AddLightToScene( point, 200 + (rand()&31),1.0, 0.6, 0.23, 0 ); + + memset (&flash, 0, sizeof (flash)); + flash.renderfx = RF_LIGHTING_ORIGIN; + flash.hModel = cgs.media.mg42muzzleflash; + + VectorCopy( point, flash.origin ); + AnglesToAxis (cg.refdefViewAngles, flash.axis); + + trap_R_AddRefEntityToScene( &flash ); + + trap_S_StartSound( NULL, ent->number , CHAN_WEAPON, hflakWeaponSnd ); +}*/ + + +//----(SA) +/* +============== +CG_MortarEFX + Right now mostly copied directly from Raf's MG42 FX, but with the optional addtion of smoke +============== +*/ +void CG_MortarEFX( centity_t *cent ) { + refEntity_t flash; + + if ( cent->currentState.density & 1 ) { + // smoke + CG_ParticleImpactSmokePuff( cgs.media.smokePuffShader, cent->currentState.origin ); + } + + if ( cent->currentState.density & 2 ) { + // light + //% trap_R_AddLightToScene( cent->currentState.origin, 200 + (rand()&31), 1.0, 1.0, 1.0, 0 ); + trap_R_AddLightToScene( cent->currentState.origin, 256, 0.75 + 8.0 / ( rand() & 31 ), 1.0, 1.0, 1.0, 0, 0 ); + + // muzzle flash + memset( &flash, 0, sizeof( flash ) ); + flash.renderfx = RF_LIGHTING_ORIGIN; + flash.hModel = cgs.media.mg42muzzleflash; + VectorCopy( cent->currentState.origin, flash.origin ); + AnglesToAxis( cg.refdefViewAngles, flash.axis ); + trap_R_AddRefEntityToScene( &flash ); + } +} + +//----(SA) end + + +// RF +/* +============== +CG_WeaponFireRecoil +============== +*/ +void CG_WeaponFireRecoil( int weapon ) { +// const vec3_t maxKickAngles = {25, 30, 25}; + float pitchRecoilAdd, pitchAdd; + float yawRandom; + vec3_t recoil; + // + pitchRecoilAdd = 0; + pitchAdd = 0; + yawRandom = 0; + // + switch ( weapon ) { + case WP_LUGER: + case WP_SILENCER: + case WP_AKIMBO_LUGER: + case WP_AKIMBO_SILENCEDLUGER: + case WP_COLT: + case WP_SILENCED_COLT: + case WP_AKIMBO_COLT: + case WP_AKIMBO_SILENCEDCOLT: + //pitchAdd = 2+rand()%3; + //yawRandom = 2; + break; + case WP_GARAND: + case WP_KAR98: + case WP_CARBINE: + case WP_K43: + //pitchAdd = 4+rand()%3; + //yawRandom = 4; + pitchAdd = 2; //----(SA) for DM + yawRandom = 1; //----(SA) for DM + break; + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + pitchAdd = 0.3; + break; + case WP_FG42SCOPE: + case WP_FG42: + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + //pitchRecoilAdd = 1; + pitchAdd = 1 + rand() % 3; + yawRandom = 2; + + + pitchAdd *= 0.3; + yawRandom *= 0.3; + break; + case WP_PANZERFAUST: + //pitchAdd = 12+rand()%3; + //yawRandom = 6; + + // push the player back instead + break; + default: + return; + } + // calc the recoil + recoil[YAW] = crandom() * yawRandom; + recoil[ROLL] = -recoil[YAW]; // why not + recoil[PITCH] = -pitchAdd; + // scale it up a bit (easier to modify this while tweaking) + VectorScale( recoil, 30, recoil ); + + // set the recoil + VectorCopy( recoil, cg.kickAVel ); + // set the recoil + cg.recoilPitch -= pitchRecoilAdd; +} + + +/* +================ +CG_FireWeapon + +Caused by an EV_FIRE_WEAPON event + +================ +*/ +void CG_FireWeapon( centity_t *cent ) { + entityState_t *ent; + int c; + weaponInfo_t *weap; + sfxHandle_t *firesound; + sfxHandle_t *fireEchosound; + + ent = ¢->currentState; + + // Arnout: quick hack for EF_MOUNTEDTANK, need to change this - likely it needs to use viewlocked as well + if ( cent->currentState.eFlags & EF_MOUNTEDTANK ) { + if ( cg_entities[cg_entities[cg_entities[ cent->currentState.number ].tagParent].tankparent].currentState.density & 8 ) { // should we use a browning? + trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cgs.media.hWeaponSnd_2 ); + } else { + trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cgs.media.hWeaponSnd ); + } + cent->muzzleFlashTime = cg.time; + return; + } + + // Rafael - mg42 + if ( BG_PlayerMounted( cent->currentState.eFlags ) ) { + if ( cent->currentState.eFlags & EF_AAGUN_ACTIVE ) { +// trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cgs.media.hflakWeaponSnd ); + } else { + trap_S_StartSound( NULL, cent->currentState.number, CHAN_WEAPON, cgs.media.hWeaponSnd ); + } + + if ( cg_brassTime.integer > 0 ) { + CG_MachineGunEjectBrass( cent ); + } + + cent->muzzleFlashTime = cg.time; + + return; + } + + if ( ent->weapon == WP_NONE ) { + return; + } + if ( ent->weapon >= WP_NUM_WEAPONS ) { + CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" ); + return; + } + weap = &cg_weapons[ ent->weapon ]; + + if ( cent->currentState.clientNum == cg.snap->ps.clientNum ) { + cg.lastFiredWeapon = ent->weapon; //----(SA) added + } + + // mark the entity as muzzle flashing, so when it is added it will + // append the flash to the weapon model + cent->muzzleFlashTime = cg.time; + + // RF, kick angles + if ( ent->number == cg.snap->ps.clientNum ) { + CG_WeaponFireRecoil( ent->weapon ); + } + + if ( ent->weapon == WP_MORTAR_SET ) { + if ( ent->clientNum == cg.snap->ps.clientNum ) { + cg.mortarImpactTime = -1; + cg.mortarFireAngles[PITCH] = cg.predictedPlayerState.viewangles[PITCH]; + cg.mortarFireAngles[YAW] = cg.predictedPlayerState.viewangles[YAW]; + } + } + + // lightning gun only does this this on initial press + if ( ent->weapon == WP_FLAMETHROWER ) { + if ( cent->pe.lightningFiring ) { + return; + } + } else if ( ent->weapon == WP_GRENADE_LAUNCHER || + ent->weapon == WP_GRENADE_PINEAPPLE || + ent->weapon == WP_DYNAMITE || + ent->weapon == WP_SMOKE_MARKER + || ent->weapon == WP_LANDMINE + || ent->weapon == WP_SATCHEL + || ent->weapon == WP_TRIPMINE + || ent->weapon == WP_SMOKE_BOMB + ) { // JPW NERVE + if ( ent->apos.trBase[0] > 0 ) { // underhand + return; + } + } + + if ( ent->weapon == WP_GPG40 ) { + if ( ent->clientNum == cg.snap->ps.clientNum ) { + cg.weaponSelect = WP_KAR98; + } + } else if ( ent->weapon == WP_M7 ) { + if ( ent->clientNum == cg.snap->ps.clientNum ) { + cg.weaponSelect = WP_CARBINE; + } + } + + if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == EV_FIRE_WEAPON_LASTSHOT ) { + firesound = &weap->lastShotSound[0]; + fireEchosound = &weap->flashEchoSound[0]; + + // try to use the lastShotSound, but don't assume it's there. + // if a weapon without the sound calls it, drop back to regular fire sound + + for ( c = 0; c < 4; c++ ) { + if ( !firesound[c] ) { + break; + } + } + if ( !c ) { + firesound = &weap->flashSound[0]; + fireEchosound = &weap->flashEchoSound[0]; + } + } else { + firesound = &weap->flashSound[0]; + fireEchosound = &weap->flashEchoSound[0]; + } + +/* // JPW NERVE -- special case medic tool + if( ent->weapon == WP_MEDKIT ) { + firesound = &cg_weapons[ WP_MEDKIT ].flashSound[0]; + }*/ + + if ( !( cent->currentState.eFlags & EF_ZOOMING ) ) { // JPW NERVE -- don't play sounds or eject brass if zoomed in + // play a sound + for ( c = 0 ; c < 4 ; c++ ) { + if ( !firesound[c] ) { + break; + } + } + if ( c > 0 ) { + c = rand() % c; + if ( firesound[c] ) { + trap_S_StartSound( NULL, ent->number, CHAN_WEAPON, firesound[c] ); + + if ( fireEchosound && fireEchosound[c] ) { // check for echo + centity_t *cent; + vec3_t porg, gorg, norm; // player/gun origin + float gdist; + + cent = &cg_entities[ent->number]; + VectorCopy( cent->currentState.pos.trBase, gorg ); + VectorCopy( cg.refdef_current->vieworg, porg ); + VectorSubtract( gorg, porg, norm ); + gdist = VectorNormalize( norm ); + if ( gdist > 512 && gdist < 4096 ) { // temp dist. TODO: use numbers that are weapon specific + // use gorg as the new sound origin + VectorMA( cg.refdef_current->vieworg, 64, norm, gorg ); // sound-on-a-stick + trap_S_StartSoundEx( gorg, ent->number, CHAN_WEAPON, fireEchosound[c], SND_NOCUT ); + } + } + } + } + + // do brass ejection + if ( weap->ejectBrassFunc && cg_brassTime.integer > 0 ) { + weap->ejectBrassFunc( cent ); + } + } // jpw +} + + +// Ridah +/* +================= +CG_AddSparks +================= +*/ +void CG_AddSparks( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity; + int i; + + for ( i = 0; i < count; i++ ) { + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + VectorSet( velocity, dir[0] + crandom() * randScale, dir[1] + crandom() * randScale, dir[2] + crandom() * randScale ); + VectorScale( velocity, (float)speed, velocity ); + + le->leType = LE_SPARK; + le->startTime = cg.time; + le->endTime = le->startTime + duration - (int)( 0.5 * random() * duration ); + le->lastTrailTime = cg.time; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->pos.trType = TR_GRAVITY_LOW; + VectorCopy( origin, le->pos.trBase ); + VectorMA( le->pos.trBase, 2 + random() * 4, dir, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + le->refEntity.customShader = cgs.media.sparkParticleShader; + + le->bounceFactor = 0.9; + +// le->leBounceSoundType = LEBS_BLOOD; +// le->leMarkType = LEMT_BLOOD; + } +} +/* +================= +CG_AddBulletParticles +================= +*/ +void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale ) { +// localEntity_t *le; +// refEntity_t *re; + vec3_t velocity, pos; + int i; +/* + // add the falling streaks + for (i=0; irefEntity; + + VectorSet( velocity, dir[0] + crandom()*randScale, dir[1] + crandom()*randScale, dir[2] + crandom()*randScale ); + VectorScale( velocity, (float)speed*3, velocity ); + + le->leType = LE_SPARK; + le->startTime = cg.time; + le->endTime = le->startTime + duration - (int)(0.5 * random() * duration); + le->lastTrailTime = cg.time; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->pos.trType = TR_GRAVITY; + VectorCopy( origin, le->pos.trBase ); + VectorMA( le->pos.trBase, 2 + random()*4, dir, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + le->refEntity.customShader = cgs.media.bulletParticleTrailShader; +// le->refEntity.customShader = cgs.media.sparkParticleShader; + + le->bounceFactor = 0.9; + +// le->leBounceSoundType = LEBS_BLOOD; +// le->leMarkType = LEMT_BLOOD; + } +*/ + // add the falling particles + for ( i = 0; i < count; i++ ) { + + VectorSet( velocity, dir[0] + crandom() * randScale, dir[1] + crandom() * randScale, dir[2] + crandom() * randScale ); + VectorScale( velocity, (float)speed, velocity ); + + VectorCopy( origin, pos ); + VectorMA( pos, 2 + random() * 4, dir, pos ); + + CG_ParticleBulletDebris( pos, velocity, 300 + rand() % 300 ); + + } +} + +/* +================= +CG_AddDirtBulletParticles +================= +*/ +void CG_AddDirtBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale, float width, float height, float alpha, qhandle_t shader ) { + vec3_t velocity, pos; + int i; + + // add the big falling particle + VectorSet( velocity, 0, 0, (float)speed ); + VectorCopy( origin, pos ); + + CG_ParticleDirtBulletDebris_Core( pos, velocity, duration, width,height, alpha, shader ); //600 + rand()%300 ); // keep central one + for ( i = 0; i < count; i++ ) { + VectorSet( velocity, dir[0] * crandom() * speed * randScale, dir[1] * crandom() * speed * randScale, dir[2] * random() * speed ); + CG_ParticleDirtBulletDebris_Core( pos, velocity, duration + ( rand() % ( duration >> 1 ) ), width,height, alpha, shader ); + } +} + +/* +================= +CG_AddDebris +================= +*/ +void CG_AddDebris( vec3_t origin, vec3_t dir, int speed, int duration, int count ) { + localEntity_t *le; + refEntity_t *re; + vec3_t velocity, unitvel; + float timeAdd; + int i; + + for ( i = 0; i < count; i++ ) { + le = CG_AllocLocalEntity(); + re = &le->refEntity; + + VectorSet( unitvel, dir[0] + crandom() * 0.9, dir[1] + crandom() * 0.9, fabs( dir[2] ) > 0.5 ? dir[2] * ( 0.2 + 0.8 * random() ) : random() * 0.6 ); + VectorScale( unitvel, (float)speed + (float)speed * 0.5 * crandom(), velocity ); + + le->leType = LE_DEBRIS; + le->startTime = cg.time; + le->endTime = le->startTime + duration + (int)( (float)duration * 0.8 * crandom() ); + le->lastTrailTime = cg.time; + + VectorCopy( origin, re->origin ); + AxisCopy( axisDefault, re->axis ); + + le->pos.trType = TR_GRAVITY_LOW; + VectorCopy( origin, le->pos.trBase ); + VectorCopy( velocity, le->pos.trDelta ); + le->pos.trTime = cg.time; + + timeAdd = 10.0 + random() * 40.0; + BG_EvaluateTrajectory( &le->pos, cg.time + (int)timeAdd, le->pos.trBase, qfalse, -1 ); + + le->bounceFactor = 0.5; + +// if (!rand()%2) +// le->effectWidth = 0; // no flame +// else + le->effectWidth = 5 + random() * 5; + +// if (rand()%3) + le->effectFlags |= 1; // smoke trail + + +// le->leBounceSoundType = LEBS_BLOOD; +// le->leMarkType = LEMT_BLOOD; + } +} +// done. + + +/* +============== +CG_WaterRipple +============== +*/ +void CG_WaterRipple( qhandle_t shader, vec3_t loc, vec3_t dir, int size, int lifetime ) { + localEntity_t *le; + refEntity_t *re; + + le = CG_AllocLocalEntity(); + le->leType = LE_SCALE_FADE; + le->leFlags = LEF_PUFF_DONT_SCALE; + + le->startTime = cg.time; + le->endTime = cg.time + lifetime; + le->lifeRate = 1.0 / ( le->endTime - le->startTime ); + + re = &le->refEntity; + VectorCopy( loc, re->origin ); + re->shaderTime = cg.time / 1000.0f; + re->reType = RT_SPLASH; + re->radius = size; + re->customShader = shader; + re->shaderRGBA[0] = 0xff; + re->shaderRGBA[1] = 0xff; + re->shaderRGBA[2] = 0xff; + re->shaderRGBA[3] = 0xff; + le->color[3] = 1.0; +} + +/* +================= +CG_MissileHitWall + +Caused by an EV_MISSILE_MISS event, or directly by local bullet tracing + +ClientNum is a dummy field used to define what sort of effect to spawn +================= +*/ +void CG_MissileHitWall( int weapon, int clientNum, vec3_t origin, vec3_t dir, int surfFlags ) { // (SA) modified to send missilehitwall surface parameters + qhandle_t mod, mark, shader; + sfxHandle_t sfx, sfx2; + localEntity_t *le; + qboolean isSprite, alphaFade = qfalse; + int r, duration, lightOverdraw, i, j, markDuration, volume; + trace_t trace; + vec3_t lightColor, tmpv, tmpv2, sprOrg, sprVel; + float radius, light, sfx2range = 0; + vec4_t projection, color; + vec3_t markOrigin; + + mark = 0; + radius = 32; + sfx = 0; + sfx2 = 0; + mod = 0; + shader = 0; + light = 0; + VectorSet( lightColor, 1, 1, 0 ); + // Ridah + lightOverdraw = 0; + volume = 127; + + // set defaults + isSprite = qfalse; + duration = 600; + markDuration = -1; + + if ( surfFlags & SURF_SKY ) { + return; + } + + switch ( weapon ) { + case WP_KNIFE: + i = rand() % 4; + if ( !surfFlags ) { + sfx = cgs.media.sfx_knifehit[4]; // different values for different types (stone/metal/wood/etc.) + mark = cgs.media.bulletMarkShader; + radius = 1 + rand() % 2; + + CG_AddBulletParticles( origin, dir, 20, 800, 3 + rand() % 6, 1.0 ); + } else { + sfx = cgs.media.sfx_knifehit[i]; + } + + // ydnar: set mark duration + markDuration = cg_markTime.integer; + + break; + + case WP_LUGER: + case WP_SILENCER: + case WP_AKIMBO_LUGER: + case WP_AKIMBO_SILENCEDLUGER: + case WP_COLT: + case WP_SILENCED_COLT: + case WP_AKIMBO_COLT: + case WP_AKIMBO_SILENCEDCOLT: + case WP_MP40: + case WP_THOMPSON: + case WP_STEN: + case WP_GARAND: + case WP_FG42: + case WP_FG42SCOPE: + case WP_KAR98: + case WP_CARBINE: + case WP_MOBILE_MG42: + case WP_MOBILE_MG42_SET: + case WP_K43: + case WP_GARAND_SCOPE: + case WP_K43_SCOPE: + // actually yeah. meant that. very rare. + r = ( rand() & 3 ) + 1; // JPW NERVE increased spark frequency so players can tell where rounds are coming from in MP + + volume = 64; + +/* if ( r == 3 ) { + sfx = cgs.media.sfx_ric1; + } else if ( r == 2 ) { + sfx = cgs.media.sfx_ric2; + } else if ( r == 1 ) { + sfx = cgs.media.sfx_ric3; + }*/ + + // clientNum is a dummy field used to define what sort of effect to spawn + + if ( !clientNum ) { + // RF, why is this here? we need sparks if clientNum = 0, used for warzombie + CG_AddSparks( origin, dir, 350, 200, 15 + rand() % 7, 0.2 ); + } else if ( clientNum == 1 ) { // just do a little smoke puff + vec3_t d, o; + VectorMA( origin, 12, dir, o ); + VectorScale( dir, 7, d ); + d[2] += 16; + + // DHM - Nerve :: use dirt images + if ( ( surfFlags & SURF_GRASS || surfFlags & SURF_GRAVEL || surfFlags & SURF_SNOW ) ) { // JPW NERVE added SURF_SNOW + + // some debris particles + // JPW NERVE added surf_snow + if ( surfFlags & SURF_SNOW ) { + CG_AddDirtBulletParticles( origin, dir, 190, 900, 5, 0.25, 80, 32, 0.5, cgs.media.dirtParticle2Shader ); + } else { + CG_AddDirtBulletParticles( origin, dir, 190, 900, 5, 0.5, 80, 16, 0.5, cgs.media.dirtParticle1Shader ); + } + } else { + CG_ParticleImpactSmokePuff( cgs.media.smokeParticleShader, o ); + + // some debris particles + CG_AddBulletParticles( origin, dir, 20, 800, 3 + rand() % 6, 1.0 ); // rand scale + + // just do a little one + if ( sfx && ( rand() % 3 == 0 ) ) { + CG_AddSparks( origin, dir, 450, 300, 3 + rand() % 3, 0.5 ); // rand scale + } + } + } else if ( clientNum == 2 ) { + sfx = 0; + mark = 0; + + // (SA) needed to do the CG_WaterRipple using a localent since I needed the timer reset on the shader for each shot + CG_WaterRipple( cgs.media.wakeMarkShaderAnim, origin, tv( 0, 0, 1 ), 32, 1000 ); + CG_AddDirtBulletParticles( origin, dir, 190, 900, 5, 0.5, 80, 16, 0.125, cgs.media.dirtParticle2Shader ); + break; + + // play a water splash + mod = cgs.media.waterSplashModel; + shader = cgs.media.waterSplashShader; + duration = 250; + } + + // Ridah, optimization, only spawn the bullet hole if we are close + // enough to see it, this way we can leave other marks around a lot + // longer, since most of the time we can't actually see the bullet holes +// (SA) small modification. only do this for non-rifles (so you can see your shots hitting when you're zooming with a rifle scope) + if ( weapon == WP_FG42SCOPE || weapon == WP_GARAND_SCOPE || weapon == WP_K43_SCOPE || ( Distance( cg.refdef_current->vieworg, origin ) < 384 ) ) { + if ( clientNum ) { + // mark and sound can potentially use the surface for override values + + mark = cgs.media.bulletMarkShader; // default + alphaFade = qtrue; // max made the bullet mark alpha (he'll make everything in the game out of 1024 textures, all with alpha blend funcs yet...) + //% radius = 1.5f + rand()%2; // slightly larger for DM + radius = 1.0f + 0.5f * ( rand() % 2 ); + +#define MAX_IMPACT_SOUNDS 5 + if ( surfFlags & SURF_METAL || surfFlags & SURF_ROOF ) { + sfx = cgs.media.sfx_bullet_metalhit[rand() % MAX_IMPACT_SOUNDS]; + mark = cgs.media.bulletMarkShaderMetal; + alphaFade = qtrue; + } else if ( surfFlags & SURF_WOOD ) { + sfx = cgs.media.sfx_bullet_woodhit[rand() % MAX_IMPACT_SOUNDS]; + mark = cgs.media.bulletMarkShaderWood; + alphaFade = qtrue; + radius += 0.4f; // experimenting with different mark sizes per surface + } else if ( surfFlags & SURF_GLASS ) { + sfx = cgs.media.sfx_bullet_glasshit[rand() % MAX_IMPACT_SOUNDS]; + mark = cgs.media.bulletMarkShaderGlass; + alphaFade = qtrue; + } else { + sfx = cgs.media.sfx_bullet_stonehit[rand() % MAX_IMPACT_SOUNDS]; + mark = cgs.media.bulletMarkShader; + alphaFade = qtrue; + } + + // ydnar: set mark duration + markDuration = cg_markTime.integer; + } + } + break; + + case WP_MAPMORTAR: + sfx = cgs.media.sfx_rockexp; + sfx2 = cgs.media.sfx_rockexpDist; + sfx2range = 1200; + mark = cgs.media.burnMarkShader; + markDuration = cg_markTime.integer * 3; + radius = 96; // ydnar: bigger mark radius + light = 300; + isSprite = qtrue; + duration = 1000; + lightColor[0] = 0.75; + lightColor[1] = 0.5; + lightColor[2] = 0.1; + + VectorScale( dir, 16, sprVel ); + if ( CG_PointContents( origin, 0 ) & CONTENTS_WATER ) { + VectorCopy( origin,tmpv ); + tmpv[2] += 10000; + + trap_CM_BoxTrace( &trace, tmpv,origin, NULL, NULL, 0, MASK_WATER ); + CG_WaterRipple( cgs.media.wakeMarkShaderAnim, trace.endpos, dir, 150, 1000 ); + CG_AddDirtBulletParticles( trace.endpos, dir, 900, 1800, 15, 0.5, 350, 128, 0.125, cgs.media.dirtParticle2Shader ); + } else { + VectorCopy( origin,tmpv ); + tmpv[2] += 20; + VectorCopy( origin,tmpv2 ); + tmpv2[2] -= 20; + trap_CM_BoxTrace( &trace,tmpv,tmpv2,NULL,NULL,0,MASK_SHOT ); + if ( trace.surfaceFlags & SURF_GRASS || trace.surfaceFlags & SURF_GRAVEL ) { + CG_AddDirtBulletParticles( origin, dir, 600, 2000, 10, 0.5, 275,125, 0.25, cgs.media.dirtParticle1Shader ); + } + + for ( i = 0; i < 5; i++ ) { + for ( j = 0; j < 3; j++ ) { + sprOrg[j] = origin[j] + 64 * dir[j] + 24 * crandom(); + } + sprVel[2] += rand() % 50; + CG_ParticleExplosion( "blacksmokeanim", sprOrg, sprVel, 3500 + rand() % 250, 10, 250 + rand() % 60, qfalse ); + } + VectorMA( origin, 24, dir, sprOrg ); + VectorScale( dir, 64, sprVel ); + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1000, 20, 300, qtrue ); + } + break; + + case WP_DYNAMITE: + case WP_TRIPMINE: + shader = cgs.media.rocketExplosionShader; + sfx = cgs.media.sfx_dynamiteexp; + sfx2 = cgs.media.sfx_dynamiteexpDist; + sfx2range = 400; + mark = cgs.media.burnMarkShader; + markDuration = cg_markTime.integer * 3; + radius = 128; // ydnar: bigger mark radius + light = 300; + isSprite = qtrue; + duration = 1000; + lightColor[0] = 0.75; + lightColor[1] = 0.5; + lightColor[2] = 0.1; + +// JPW NERVE +// biggie dynamite explosions that mean it -- dynamite is biggest explode, so it gets extra crap thrown on +// check for water/dirt spurt + if ( CG_PointContents( origin, 0 ) & CONTENTS_WATER ) { + VectorCopy( origin,tmpv ); + tmpv[2] += 10000; + + trap_CM_BoxTrace( &trace, tmpv,origin, NULL, NULL, 0, MASK_WATER ); + CG_WaterRipple( cgs.media.wakeMarkShaderAnim, trace.endpos, dir, 300, 2000 ); + + CG_AddDirtBulletParticles( trace.endpos, dir, 400 + random() * 200, 900, 15, 0.5, 512, 128, 0.125, cgs.media.dirtParticle2Shader ); + CG_AddDirtBulletParticles( trace.endpos, dir, 400 + random() * 600, 1400, 15, 0.5, 128, 512, 0.125, cgs.media.dirtParticle2Shader ); + } else { + VectorSet( tmpv, origin[0], origin[1], origin[2] + 20 ); + VectorSet( tmpv2, origin[0], origin[1], origin[2] - 20 ); + trap_CM_BoxTrace( &trace, tmpv, tmpv2, NULL, NULL, 0, MASK_SHOT ); + if ( trace.surfaceFlags & SURF_GRASS || trace.surfaceFlags & SURF_GRAVEL ) { + CG_AddDirtBulletParticles( origin, dir, 400 + random() * 200, 3000, 10, 0.5, 400,256, 0.25, cgs.media.dirtParticle1Shader ); + } + for ( i = 0; i < 3; i++ ) { + for ( j = 0; j < 3; j++ ) { + sprOrg[j] = origin[j] + 150 * crandom(); + sprVel[j] = 0.35 * crandom(); + } + VectorAdd( sprVel, trace.plane.normal, sprVel ); + VectorScale( sprVel,130,sprVel ); + CG_ParticleExplosion( "blacksmokeanim", sprOrg, sprVel, 6000 + random() * 2000, 40, 400 + random() * 200, qfalse ); // JPW NERVE was blacksmokeanimb + } + for ( i = 0; i < 4; i++ ) { // JPW random vector based on plane normal so explosions move away from walls/dirt/etc + for ( j = 0; j < 3; j++ ) { + sprOrg[j] = origin[j] + 100 * crandom(); + sprVel[j] = 0.65 * crandom(); // wider fireball spread + } + VectorAdd( sprVel, trace.plane.normal, sprVel ); + VectorScale( sprVel,random() * 100 + 300,sprVel ); + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1000 + rand() % 1450, 40, 400 + random() * 200, ( i == 0 ? qtrue : qfalse ) ); + } + CG_AddDebris( origin, dir, 400 + random() * 200, rand() % 2000 + 1400, 12 + rand() % 12 ); + } + break; + case WP_GRENADE_LAUNCHER: + case WP_GRENADE_PINEAPPLE: + case WP_GPG40: + case WP_M7: + case WP_SATCHEL: + case WP_LANDMINE: + case WP_MORTAR_SET: + sfx2range = 1200; + if ( weapon == WP_GPG40 || weapon == WP_M7 ) { + sfx2range = 800; + } + if ( weapon == WP_SATCHEL ) { + sfx = cgs.media.sfx_satchelexp; + sfx2 = cgs.media.sfx_satchelexpDist; + } else if ( weapon == WP_LANDMINE ) { + sfx = cgs.media.sfx_landmineexp; + sfx2 = cgs.media.sfx_landmineexpDist; + } else if ( weapon == WP_MORTAR_SET ) { + sfx = sfx2 = 0; + } else if ( weapon == WP_GRENADE_LAUNCHER || weapon == WP_GRENADE_PINEAPPLE || weapon == WP_GPG40 || weapon == WP_M7 ) { + sfx = cgs.media.sfx_grenexp; + sfx2 = cgs.media.sfx_grenexpDist; + } else { + sfx = cgs.media.sfx_rockexp; + sfx2 = cgs.media.sfx_rockexpDist; + } + shader = cgs.media.rocketExplosionShader; // copied from RL + sfx2range = 400; + mark = cgs.media.burnMarkShader; + markDuration = cg_markTime.integer * 3; + radius = 64; + light = 300; + isSprite = qtrue; + duration = 1000; + lightColor[0] = 0.75; + lightColor[1] = 0.5; + lightColor[2] = 0.1; + + // Ridah, explosion sprite animation + VectorMA( origin, 16, dir, sprOrg ); + VectorScale( dir, 100, sprVel ); + + if ( CG_PointContents( origin, 0 ) & CONTENTS_WATER ) { + sfx = cgs.media.sfx_rockexpWater; + + VectorCopy( origin,tmpv ); + tmpv[2] += 10000; + + trap_CM_BoxTrace( &trace, tmpv,origin, NULL, NULL, 0, MASK_WATER ); + CG_WaterRipple( cgs.media.wakeMarkShaderAnim, trace.endpos, dir, 150, 1000 ); + + CG_AddDirtBulletParticles( trace.endpos, dir, 400, 900, 15, 0.5, 256,128, 0.125, cgs.media.dirtParticle2Shader ); + } else { + VectorCopy( origin,tmpv ); + tmpv[2] += 20; + VectorCopy( origin,tmpv2 ); + tmpv2[2] -= 20; + trap_CM_BoxTrace( &trace,tmpv,tmpv2,NULL,NULL,0,MASK_SHOT ); + if ( trace.surfaceFlags & SURF_GRASS || trace.surfaceFlags & SURF_GRAVEL ) { + CG_AddDirtBulletParticles( origin, dir, 400, 2000, 10, 0.5, 200,75, 0.25, cgs.media.dirtParticle1Shader ); + } + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 700, 60, 240, qtrue ); + CG_AddDebris( origin, dir, 280, 1400, 7 + rand() % 2 ); + } + break; + case WP_PANZERFAUST: + case VERYBIGEXPLOSION: + case WP_ARTY: + case WP_SMOKE_MARKER: + sfx = cgs.media.sfx_rockexp; + sfx2 = cgs.media.sfx_rockexpDist; + if ( weapon == VERYBIGEXPLOSION || weapon == WP_ARTY ) { + sfx = cgs.media.sfx_artilleryExp[rand() % 3]; + sfx2 = cgs.media.sfx_artilleryDist; + } else if ( weapon == WP_SMOKE_MARKER ) { + sfx = cgs.media.sfx_airstrikeExp[rand() % 3]; + sfx2 = cgs.media.sfx_airstrikeDist; + } + sfx2range = 800; + mark = cgs.media.burnMarkShader; + markDuration = cg_markTime.integer * 3; + radius = 128; // ydnar: bigger mark radius + light = 600; + isSprite = qtrue; + duration = 1000; + // Ridah, changed to flamethrower colors + lightColor[0] = 0.75; + lightColor[1] = 0.5; + lightColor[2] = 0.1; + + // explosion sprite animation + VectorMA( origin, 24, dir, sprOrg ); + VectorScale( dir, 64, sprVel ); + + if ( CG_PointContents( origin, 0 ) & CONTENTS_WATER ) { + VectorCopy( origin,tmpv ); + tmpv[2] += 10000; + + trap_CM_BoxTrace( &trace, tmpv,origin, NULL, NULL, 0, MASK_WATER ); + CG_WaterRipple( cgs.media.wakeMarkShaderAnim, trace.endpos, dir, 300, 2000 ); + + CG_AddDirtBulletParticles( trace.endpos, dir, 400 + random() * 200, 900, 15, 0.5, 512, 128, 0.125, cgs.media.dirtParticle2Shader ); + CG_AddDirtBulletParticles( trace.endpos, dir, 400 + random() * 600, 1400, 15, 0.5, 128, 512, 0.125, cgs.media.dirtParticle2Shader ); + } else { + VectorCopy( origin,tmpv ); + tmpv[2] += 20; + VectorCopy( origin,tmpv2 ); + tmpv2[2] -= 20; + trap_CM_BoxTrace( &trace,tmpv,tmpv2,NULL,NULL,0,MASK_SHOT ); + + if ( trace.surfaceFlags & SURF_GRASS || trace.surfaceFlags & SURF_GRAVEL ) { + CG_AddDirtBulletParticles( origin, dir, 400 + random() * 200, 3000, 10, 0.5, 400,256, 0.25, cgs.media.dirtParticle1Shader ); + } + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1600, 20, 200 + random() * 400, qtrue ); + + for ( i = 0; i < 4; i++ ) { // JPW random vector based on plane normal so explosions move away from walls/dirt/etc + for ( j = 0; j < 3; j++ ) { + sprOrg[j] = origin[j] + 50 * crandom(); + sprVel[j] = 0.35 * crandom(); + } + VectorAdd( sprVel, trace.plane.normal, sprVel ); + VectorScale( sprVel,300,sprVel ); + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 1600, 40, 260 + rand() % 120, qfalse ); + } + CG_AddDebris( origin, dir, 400 + random() * 200, rand() % 2000 + 1000, 5 + rand() % 5 ); + } + break; + + default: + case WP_FLAMETHROWER: + return; + break; + } + // done. + + if ( sfx ) { + trap_S_StartSoundVControl( origin, -1, CHAN_AUTO, sfx, volume ); + } + + if ( sfx2 ) { // distant sounds for weapons with a broadcast fire sound (so you /always/ hear dynamite explosions) + vec3_t porg, gorg, norm; // player/gun origin + float gdist; + + VectorCopy( origin, gorg ); + VectorCopy( cg.refdef_current->vieworg, porg ); + VectorSubtract( gorg, porg, norm ); + gdist = VectorNormalize( norm ); + if ( gdist > 1200 && gdist < 8000 ) { // 1200 is max cam shakey dist (2*600) use gorg as the new sound origin + VectorMA( cg.refdef_current->vieworg, sfx2range, norm, gorg ); // JPW NERVE non-distance falloff makes more sense; sfx2range was gdist*0.2 + // sfx2range is variable to give us minimum volume control different explosion sizes (see mortar, panzerfaust, and grenade) + trap_S_StartSoundEx( gorg, -1, CHAN_WEAPON, sfx2, SND_NOCUT ); + } + } + + if ( mod ) { + le = CG_MakeExplosion( origin, dir, mod, shader, duration, isSprite ); + le->light = light; + le->lightOverdraw = lightOverdraw; + VectorCopy( lightColor, le->lightColor ); + } + + // ydnar: omnidirectional explosion marks + if ( mark == cgs.media.burnMarkShader ) { + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = radius; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( mark, 1, (vec3_t*) origin, projection, color, markDuration, ( markDuration >> 4 ) ); + } else if ( mark ) { + VectorSubtract( vec3_origin, dir, projection ); + projection[ 3 ] = radius * 32; + VectorMA( origin, -16.0f, projection, markOrigin ); + // jitter markorigin a bit so they don't end up on an ordered grid + markOrigin[ 0 ] += ( random() - 0.5f ); + markOrigin[ 1 ] += ( random() - 0.5f ); + markOrigin[ 2 ] += ( random() - 0.5f ); + CG_ImpactMark( mark, markOrigin, projection, radius, random() * 360.0f, 1.0f, 1.0f, 1.0f, 1.0f, markDuration ); + } +} + + +/* +============== +CG_MissileHitWallSmall +============== +*/ +void CG_MissileHitWallSmall( int weapon, int clientNum, vec3_t origin, vec3_t dir ) { + qhandle_t mod; + qhandle_t mark; + qhandle_t shader; + sfxHandle_t sfx; + float radius; + float light; + vec3_t lightColor; + localEntity_t *le; + qboolean isSprite; + int duration; + int lightOverdraw; + vec3_t sprOrg, sprVel; + vec4_t projection, color; + + + mark = 0; + radius = 32; + sfx = 0; + mod = 0; + shader = 0; + light = 0; + lightColor[0] = 1; + lightColor[1] = 1; + lightColor[2] = 0; + // Ridah + lightOverdraw = 0; + + // set defaults + isSprite = qfalse; + duration = 600; + + shader = cgs.media.rocketExplosionShader; // copied from RL + sfx = cgs.media.sfx_rockexp; + mark = cgs.media.burnMarkShader; + radius = 80; + light = 300; + isSprite = qtrue; + duration = 1000; + lightColor[0] = 0.75; + lightColor[1] = 0.5; + lightColor[2] = 0.1; + + // Ridah, explosion sprite animation + VectorMA( origin, 16, dir, sprOrg ); + VectorScale( dir, 64, sprVel ); + + CG_ParticleExplosion( "explode1", sprOrg, sprVel, 600, 6, 50, qtrue ); + + // Ridah, throw some debris + CG_AddDebris( origin, dir, + 280, // speed + 1400, // duration + // 15 + rand()%5 ); // count + 7 + rand() % 2 ); // count + + if ( sfx ) { + trap_S_StartSound( origin, -1, CHAN_AUTO, sfx ); + } + + // + // create the explosion + // + if ( mod ) { + le = CG_MakeExplosion( origin, dir, mod, shader, duration, isSprite ); + le->light = light; + // Ridah + le->lightOverdraw = lightOverdraw; + VectorCopy( lightColor, le->lightColor ); + } + + // + // impact mark + // + + // ydnar: testing omnidirectional marks + #if 0 + VectorSubtract( vec3_origin, dir, projection ); + projection[ 3 ] = radius * 3; + VectorMA( origin, -4.0f, projection, markOrigin ); + + CG_ImpactMark( mark, markOrigin, projection, radius, random() * 360.0f, 1.0f, 1.0f, 1.0f, 1.0f, cg_markTime.integer ); + #else + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = radius; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( mark, 1, (vec3_t*) origin, projection, color, cg_markTime.integer, ( cg_markTime.integer >> 4 ) ); + #endif +} + +/* +================= +CG_MissileHitPlayer +================= +*/ +void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, int entityNum ) { + + CG_Bleed( origin, entityNum ); + + // some weapons will make an explosion with the blood, while + // others will just make the blood + switch ( weapon ) { + case WP_GRENADE_LAUNCHER: + case WP_PANZERFAUST: + CG_MissileHitWall( weapon, 0, origin, dir, 0 ); // JPW NERVE like the old one + break; + case WP_KNIFE: + CG_MissileHitWall( weapon, 0, origin, dir, 1 ); // JPW NERVE this one makes the hitting fleshy sound. whee + break; + default: + break; + } +} + + + +/* +============================================================================ + +VENOM GUN TRACING + +============================================================================ +*/ + +//----(SA) all changes to venom below should be mine +#define DEFAULT_VENOM_COUNT 10 +//#define DEFAULT_VENOM_SPREAD 20 +//#define DEFAULT_VENOM_SPREAD 400 +#define DEFAULT_VENOM_SPREAD 700 + +/* +============================================================================ + +BULLETS + +============================================================================ +*/ + +/* +=============== +CG_SpawnTracer +=============== +*/ +void CG_SpawnTracer( int sourceEnt, vec3_t pstart, vec3_t pend ) { + localEntity_t *le; + float dist; + vec3_t dir, ofs; + orientation_t or; + vec3_t start, end; + + VectorCopy( pstart, start ); + VectorCopy( pend, end ); + + // DHM - make MG42 tracers line up + if ( cg_entities[sourceEnt].currentState.eFlags & EF_MG42_ACTIVE ) { + start[2] -= 42; + } + + VectorSubtract( end, start, dir ); + dist = VectorNormalize( dir ); + + if ( dist < 2.0 * cg_tracerLength.value ) { + return; // segment isnt long enough, dont bother + + } + if ( sourceEnt < cgs.maxclients ) { + // for visual purposes, find the actual tag_weapon for this client + // and offset the start and end accordingly + if ( !( cg_entities[sourceEnt].currentState.eFlags & EF_MG42_ACTIVE || cg_entities[sourceEnt].currentState.eFlags & EF_AAGUN_ACTIVE ) ) { // not MG42 + if ( CG_GetWeaponTag( sourceEnt, "tag_flash", &or ) ) { + VectorSubtract( or.origin, start, ofs ); + if ( VectorLength( ofs ) < 64 ) { + VectorAdd( start, ofs, start ); + } + } + } + } + + // subtract the length of the tracer from the end point, so we dont go through the end point + VectorMA( end, -cg_tracerLength.value, dir, end ); + dist = VectorDistance( start, end ); + + le = CG_AllocLocalEntity(); + le->leType = LE_MOVING_TRACER; + le->startTime = cg.time - ( cg.frametime ? ( rand() % cg.frametime ) / 2 : 0 ); + le->endTime = le->startTime + 1000.0 * dist / cg_tracerSpeed.value; + + le->pos.trType = TR_LINEAR; + le->pos.trTime = le->startTime; + VectorCopy( start, le->pos.trBase ); + VectorScale( dir, cg_tracerSpeed.value, le->pos.trDelta ); +} + +/* +=============== +CG_DrawTracer +=============== +*/ +void CG_DrawTracer( vec3_t start, vec3_t finish ) { + vec3_t forward, right; + polyVert_t verts[4]; + vec3_t line; + + VectorSubtract( finish, start, forward ); + + line[0] = DotProduct( forward, cg.refdef_current->viewaxis[1] ); + line[1] = DotProduct( forward, cg.refdef_current->viewaxis[2] ); + + VectorScale( cg.refdef_current->viewaxis[1], line[1], right ); + VectorMA( right, -line[0], cg.refdef_current->viewaxis[2], right ); + VectorNormalize( right ); + + VectorMA( finish, cg_tracerWidth.value, right, verts[0].xyz ); + verts[0].st[0] = 1; + verts[0].st[1] = 1; + verts[0].modulate[0] = 255; + verts[0].modulate[1] = 255; + verts[0].modulate[2] = 255; + verts[0].modulate[3] = 255; + + VectorMA( finish, -cg_tracerWidth.value, right, verts[1].xyz ); + verts[1].st[0] = 1; + verts[1].st[1] = 0; + verts[1].modulate[0] = 255; + verts[1].modulate[1] = 255; + verts[1].modulate[2] = 255; + verts[1].modulate[3] = 255; + + VectorMA( start, -cg_tracerWidth.value, right, verts[2].xyz ); + verts[2].st[0] = 0; + verts[2].st[1] = 0; + verts[2].modulate[0] = 255; + verts[2].modulate[1] = 255; + verts[2].modulate[2] = 255; + verts[2].modulate[3] = 255; + + VectorMA( start, cg_tracerWidth.value, right, verts[3].xyz ); + verts[3].st[0] = 0; + verts[3].st[1] = 1; + verts[3].modulate[0] = 255; + verts[3].modulate[1] = 255; + verts[3].modulate[2] = 255; + verts[3].modulate[3] = 255; + + trap_R_AddPolyToScene( cgs.media.tracerShader, 4, verts ); +} + +/* +=============== +CG_Tracer +=============== +*/ +void CG_Tracer( vec3_t source, vec3_t dest, int sparks ) { + float len, begin, end; + vec3_t start, finish; + vec3_t midpoint; + vec3_t forward; + + // tracer + VectorSubtract( dest, source, forward ); + len = VectorNormalize( forward ); + + // start at least a little ways from the muzzle + if ( len < 100 && !sparks ) { + return; + } + begin = 50 + random() * ( len - 60 ); + end = begin + cg_tracerLength.value; + if ( end > len ) { + end = len; + } + VectorMA( source, begin, forward, start ); + VectorMA( source, end, forward, finish ); + + CG_DrawTracer( start, finish ); + + midpoint[0] = ( start[0] + finish[0] ) * 0.5; + midpoint[1] = ( start[1] + finish[1] ) * 0.5; + midpoint[2] = ( start[2] + finish[2] ) * 0.5; +} + + +/* +====================== +CG_CalcMuzzlePoint +====================== +*/ +qboolean CG_CalcMuzzlePoint( int entityNum, vec3_t muzzle ) { + vec3_t forward, right, up; + centity_t *cent; +// int anim; + + if ( entityNum == cg.snap->ps.clientNum ) { + // Arnout: see if we're attached to a gun + if ( cg.snap->ps.eFlags & EF_MG42_ACTIVE ) { + centity_t *mg42 = &cg_entities[cg.snap->ps.viewlocked_entNum]; + vec3_t forward; + + AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); + VectorMA( mg42->currentState.pos.trBase, 40, forward, muzzle ); // wsa -36, made 40 to be in sync with the actual muzzleflash drawing + muzzle[2] += cg.snap->ps.viewheight; + } else if ( cg.snap->ps.eFlags & EF_AAGUN_ACTIVE ) { + centity_t *aagun = &cg_entities[cg.snap->ps.viewlocked_entNum]; + vec3_t forward, right, up; + + AngleVectors( cg.snap->ps.viewangles, forward, right, up ); + VectorCopy( aagun->lerpOrigin, muzzle ); // Gordon: modelindex2 will already have been incremented on the server, so work out what it WAS then + BG_AdjustAAGunMuzzleForBarrel( muzzle, forward, right, up, ( aagun->currentState.modelindex2 + 3 ) % 4 ); + } else if ( cg.snap->ps.eFlags & EF_MOUNTEDTANK ) { + if ( cg.renderingThirdPerson ) { + centity_t* tank = &cg_entities[cg_entities[cg.snap->ps.clientNum].tagParent]; + + VectorCopy( tank->mountedMG42Flash.origin, muzzle ); + AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); + VectorMA( muzzle, 14, forward, muzzle ); + } else { + //bani - fix firstperson tank muzzle origin if drawgun is off + if ( !cg_drawGun.integer ) { + VectorCopy( cg.snap->ps.origin, muzzle ); + AngleVectors( cg.snap->ps.viewangles, forward, right, up ); + VectorMA( muzzle, 48, forward, muzzle ); + muzzle[2] += cg.snap->ps.viewheight; + VectorMA( muzzle, 8, right, muzzle ); + } else { + VectorCopy( cg.tankflashorg, muzzle ); + } + } + } else { + VectorCopy( cg.snap->ps.origin, muzzle ); + muzzle[2] += cg.snap->ps.viewheight; + AngleVectors( cg.snap->ps.viewangles, forward, NULL, NULL ); + if ( cg.snap->ps.weapon == WP_MOBILE_MG42_SET ) { + VectorMA( muzzle, 36, forward, muzzle ); + } else { + VectorMA( muzzle, 14, forward, muzzle ); + } + } + return qtrue; + } + + cent = &cg_entities[entityNum]; +//----(SA) removed check. is this still necessary? (this way works for ai's firing mg42) should I check for mg42? +// if ( !cent->currentValid ) { +// return qfalse; +// } +//----(SA) end + + if ( cent->currentState.eFlags & EF_MG42_ACTIVE ) { +// centity_t *mg42; +// int num; + vec3_t forward; + + // ydnar: this is silly--the entity is a mg42 barrel, so just use itself + #if 0 + + // find the mg42 we're attached to + for ( num = 0 ; num < cg.snap->numEntities ; num++ ) + { + mg42 = &cg_entities[ cg.snap->entities[ num ].number ]; + if ( mg42->currentState.eType == ET_MG42_BARREL ) { + if ( mg42->currentState.number == cent->currentState.number ) { + // found it + + VectorCopy( mg42->currentState.pos.trBase, muzzle ); + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + //VectorMA( muzzle, -36, forward, muzzle ); + VectorMA( muzzle, 40, forward, muzzle ); + muzzle[2] += DEFAULT_VIEWHEIGHT; + + break; + } + } + } + + #else + + if ( cent->currentState.eType == ET_MG42_BARREL ) { + VectorCopy( cent->currentState.pos.trBase, muzzle ); + AngleVectors( cent->lerpAngles, forward, NULL, NULL ); + VectorMA( muzzle, 40, forward, muzzle ); + muzzle[ 2 ] += DEFAULT_VIEWHEIGHT; + } + + #endif + + } else if ( cent->currentState.eFlags & EF_MOUNTEDTANK ) { + centity_t* tank = &cg_entities[cent->tagParent]; + + VectorCopy( tank->mountedMG42Flash.origin, muzzle ); + } else if ( cent->currentState.eFlags & EF_AAGUN_ACTIVE ) { + centity_t *aagun; + int num; + + // find the mg42 we're attached to + for ( num = 0; num < cg.snap->numEntities; num++ ) { + aagun = &cg_entities[ cg.snap->entities[ num ].number ]; + if ( aagun->currentState.eType == ET_AAGUN && aagun->currentState.otherEntityNum == cent->currentState.number ) { + // found it + vec3_t forward, right, up; + + AngleVectors( cg.snap->ps.viewangles, forward, right, up ); + VectorCopy( aagun->lerpOrigin, muzzle ); // Gordon: modelindex2 will already have been incremented on the server, so work out what it WAS then + BG_AdjustAAGunMuzzleForBarrel( muzzle, forward, right, up, ( aagun->currentState.modelindex2 + 3 ) % 4 ); + } + } + } else { + VectorCopy( cent->currentState.pos.trBase, muzzle ); + + AngleVectors( cent->currentState.apos.trBase, forward, right, up ); + if ( cent->currentState.eFlags & EF_PRONE ) { + muzzle[2] += PRONE_VIEWHEIGHT; + if ( cent->currentState.weapon == WP_MOBILE_MG42_SET ) { + VectorMA( muzzle, 36, forward, muzzle ); + } else { + VectorMA( muzzle, 14, forward, muzzle ); + } + } else { + muzzle[2] += DEFAULT_VIEWHEIGHT; + VectorMA( muzzle, 14, forward, muzzle ); + } + } + + return qtrue; + +} + +void SnapVectorTowards( vec3_t v, vec3_t to ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) { + if ( to[i] <= v[i] ) { +// v[i] = (int)v[i]; + v[i] = floor( v[i] ); + } else { +// v[i] = (int)v[i] + 1; + v[i] = ceil( v[i] ); + } + } +} + +/* +====================== +CG_Bullet + +Renders bullet effects. +====================== +*/ +void CG_Bullet( vec3_t end, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum, int otherEntNum2, float waterfraction, int seed ) { + trace_t trace,trace2; + int sourceContentType, destContentType; + vec3_t dir; + vec3_t start, trend; // JPW + vec4_t projection; + static int lastBloodSpat; + centity_t *cent; + + cent = &cg_entities[fleshEntityNum]; + + // JPW NERVE -- don't ever shoot if we're binoced in + if ( cg_entities[sourceEntityNum].currentState.eFlags & EF_ZOOMING ) { + return; + } + + // Arnout: snap tracers for MG42 to viewangle of client when antilag is enabled + if ( cgs.antilag && otherEntNum2 == cg.snap->ps.clientNum && cg_entities[otherEntNum2].currentState.eFlags & EF_MG42_ACTIVE ) { + vec3_t muzzle, forward, right, up; + float r, u; + trace_t tr; + + AngleVectors( cg.predictedPlayerState.viewangles, forward, right, up ); + VectorCopy( cg_entities[cg.snap->ps.viewlocked_entNum].currentState.pos.trBase, muzzle ); + if ( cg_entities[cg.snap->ps.viewlocked_entNum].currentState.onFireStart ) { + VectorMA( muzzle, 16, up, muzzle ); + } + + r = Q_crandom( &seed ) * MG42_SPREAD_MP; + u = Q_crandom( &seed ) * MG42_SPREAD_MP; + + VectorMA( muzzle, 8192, forward, end ); + VectorMA( end, r, right, end ); + VectorMA( end, u, up, end ); + + CG_Trace( &tr, muzzle, NULL, NULL, end, otherEntNum2, MASK_SHOT ); + + SnapVectorTowards( tr.endpos, muzzle ); + VectorCopy( tr.endpos, end ); + } + + // if the shooter is currently valid, calc a source point and possibly + // do trail effects + if ( sourceEntityNum >= 0 && cg_tracerChance.value > 0 ) { + if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) { + sourceContentType = CG_PointContents( start, 0 ); + destContentType = CG_PointContents( end, 0 ); + + // do a complete bubble trail if necessary + if ( ( sourceContentType == destContentType ) && ( sourceContentType & CONTENTS_WATER ) ) { + CG_BubbleTrail( start, end, .5, 8 ); + } else if ( ( sourceContentType & CONTENTS_WATER ) ) { // bubble trail from water into air + trap_CM_BoxTrace( &trace, end, start, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( start, trace.endpos, .5, 8 ); + } else if ( ( destContentType & CONTENTS_WATER ) ) { // bubble trail from air into water + // only add bubbles if effect is close to viewer + if ( Distance( cg.snap->ps.origin, end ) < 1024 ) { + trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, CONTENTS_WATER ); + CG_BubbleTrail( end, trace.endpos, .5, 8 ); + } + } + + // if not flesh, then do a moving tracer + if ( flesh ) { + // draw a tracer + if ( random() < cg_tracerChance.value ) { + CG_Tracer( start, end, 0 ); + } + } else { // (not flesh) + if ( otherEntNum2 >= 0 && otherEntNum2 != ENTITYNUM_NONE ) { + CG_SpawnTracer( otherEntNum2, start, end ); + } else { + CG_SpawnTracer( sourceEntityNum, start, end ); + } + } + } + } + + // impact splash and mark + if ( flesh ) { + vec3_t origin; + localEntity_t *le; // JPW NERVE + float rnd, tmpf; // JPW NERVE + vec3_t smokedir, tmpv, tmpv2; // JPW NERVE + int i,headshot; // JPW NERVE + + if ( fleshEntityNum < MAX_CLIENTS ) { + CG_Bleed( end, fleshEntityNum ); + } + + // JPW NERVE smoke puffs (sometimes with some blood) + VectorSubtract( end,start,smokedir ); // get a nice "through the body" vector + VectorNormalize( smokedir ); + // all this to come up with a decent center-body displacement of bullet impact point + VectorSubtract( cent->currentState.pos.trBase,end,tmpv ); + tmpv[2] = 0; + tmpf = VectorLength( tmpv ); + VectorScale( smokedir,tmpf,tmpv ); + VectorAdd( end,tmpv,origin ); + // whee, got a bullet impact point projected to center body + CG_GetOriginForTag( cent,¢->pe.headRefEnt, "tag_mouth", 0, tmpv, NULL ); + tmpv[2] += 5; + VectorSubtract( tmpv, origin, tmpv2 ); + headshot = ( VectorLength( tmpv2 ) < 10 ); + + if ( headshot && cg_blood.integer ) { + for ( i = 0; i < 5; i++ ) { + rnd = random(); + VectorScale( smokedir,25.0 + random() * 25,tmpv ); + tmpv[0] += crandom() * 25.0f; + tmpv[1] += crandom() * 25.0f; + tmpv[2] += crandom() * 25.0f; + CG_GetWindVector( tmpv2 ); + VectorScale( tmpv2,35,tmpv2 ); // was 75, before that 55 + tmpv2[2] = 0; + VectorAdd( tmpv,tmpv2,tmpv ); + le = CG_SmokePuff( origin, tmpv, 5 + rnd * 10, 1, rnd * 0.8, rnd * 0.8, 0.5, 500 + ( rand() % 800 ), cg.time, 0, 0, cgs.media.fleshSmokePuffShader ); + } + } else { + // puff out the front (more dust no blood) + for ( i = 0; i < 10; i++ ) { + rnd = random(); + VectorScale( smokedir,-35.0 + random() * 25,tmpv ); + tmpv[0] += crandom() * 25.0f; + tmpv[1] += crandom() * 25.0f; + tmpv[2] += crandom() * 25.0f; + CG_GetWindVector( tmpv2 ); + VectorScale( tmpv2,35,tmpv2 ); // was 75, before that 55 + tmpv2[2] = 0; + VectorAdd( tmpv,tmpv2,tmpv ); + le = CG_SmokePuff( origin, tmpv, 5 + rnd * 10, rnd * 0.3f + 0.5f, rnd * 0.3f + 0.5f, rnd * 0.3f + 0.5f, 0.125f, 500 + ( rand() % 300 ), cg.time, 0, 0, cgs.media.smokePuffShader ); + } + } +// jpw + + // play the bullet hit flesh sound + // HACK, if this is not us getting hit, make it quieter // JPW NERVE pulled hack, we like loud impact sounds for MP + if ( fleshEntityNum == cg.snap->ps.clientNum ) { + //CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshScript, NULL, fleshEntityNum ); + trap_S_StartSound( NULL, fleshEntityNum, CHAN_BODY, cgs.media.sfx_bullet_stonehit[rand() % MAX_IMPACT_SOUNDS] ); + } else { + //CG_SoundPlayIndexedScript( cgs.media.bulletHitFleshScript, cg_entities[fleshEntityNum].currentState.origin, ENTITYNUM_WORLD ); // JPW NERVE changed from ,origin, to this + trap_S_StartSound( cg_entities[fleshEntityNum].currentState.origin, ENTITYNUM_WORLD, CHAN_BODY, cgs.media.sfx_bullet_stonehit[rand() % MAX_IMPACT_SOUNDS] ); + } + + // if we haven't dropped a blood spat in a while, check if this is a good scenario + if ( cg_blood.integer && ( lastBloodSpat > cg.time || lastBloodSpat < cg.time - 500 ) ) { + vec4_t color; + + + if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) ) { + VectorSubtract( end, start, dir ); + VectorNormalize( dir ); + VectorMA( end, 128, dir, trend ); + trap_CM_BoxTrace( &trace, end, trend, NULL, NULL, 0, MASK_SHOT & ~CONTENTS_BODY ); + + if ( trace.fraction < 1 ) { + //% CG_ImpactMark( cgs.media.bloodDotShaders[rand()%5], trace.endpos, trace.plane.normal, random()*360, + //% 1,1,1,1, qtrue, 15+random()*20, qfalse, cg_bloodTime.integer * 1000 ); + #if 0 + VectorSubtract( vec3_origin, dir, projection ); + projection[ 3 ] = 64; + VectorMA( trace.endpos, -8.0f, projection, markOrigin ); + CG_ImpactMark( cgs.media.bloodDotShaders[ rand() % 5 ], markOrigin, projection, 15.0f + random() * 20.0f, 360.0f * random(), + 1.0f, 1.0f, 1.0f, 1.0f, cg_bloodTime.integer * 1000 ); + #else + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = 15.0f + random() * 20.0f; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( cgs.media.bloodDotShaders[ rand() % 5 ], 1, (vec3_t*) origin, projection, color, + cg_bloodTime.integer * 1000, ( cg_bloodTime.integer * 1000 ) >> 4 ); + #endif + lastBloodSpat = cg.time; + } else if ( lastBloodSpat < cg.time - 1000 ) { + // drop one on the ground? + VectorCopy( end, trend ); + trend[ 2 ] -= 64; + trap_CM_BoxTrace( &trace, end, trend, NULL, NULL, 0, MASK_SHOT & ~CONTENTS_BODY ); + + if ( trace.fraction < 1 ) { + //% CG_ImpactMark( cgs.media.bloodDotShaders[rand()%5], trace.endpos, trace.plane.normal, random()*360, + //% 1,1,1,1, qtrue, 15+random()*10, qfalse, cg_bloodTime.integer * 1000 ); + #if 0 + VectorSubtract( vec3_origin, dir, projection ); + projection[ 3 ] = 64; + VectorMA( trace.endpos, -8.0f, projection, markOrigin ); + CG_ImpactMark( cgs.media.bloodDotShaders[ rand() % 5 ], markOrigin, projection, 15.0f + random() * 10.0f, 360.0f * random(), + 1.0f, 1.0f, 1.0f, 1.0f, cg_bloodTime.integer * 1000 ); + #else + VectorSet( projection, 0, 0, -1 ); + projection[ 3 ] = 15.0f + random() * 20.0f; + Vector4Set( color, 1.0f, 1.0f, 1.0f, 1.0f ); + trap_R_ProjectDecal( cgs.media.bloodDotShaders[ rand() % 5 ], 1, (vec3_t*) origin, projection, color, + cg_bloodTime.integer * 1000, ( cg_bloodTime.integer * 1000 ) >> 4 ); + #endif + lastBloodSpat = cg.time; + } + } + } + } + + } else { // (not flesh) + // Gordon: all bullet weapons have the same fx, and this stops pvs issues causing grenade explosions + int fromweap = WP_MP40; // cg_entities[sourceEntityNum].currentState.weapon; + + if ( !fromweap || + cg_entities[sourceEntityNum].currentState.eFlags & EF_MG42_ACTIVE || + cg_entities[sourceEntityNum].currentState.eFlags & EF_MOUNTEDTANK ) { // mounted + fromweap = WP_MP40; + } + + if ( CG_CalcMuzzlePoint( sourceEntityNum, start ) || cg.snap->ps.persistant[PERS_HWEAPON_USE] ) { + if ( waterfraction ) { + vec3_t dist; + vec3_t end2; + vec3_t dir = {0, 0, 1}; + + VectorSubtract( end, start, dist ); + VectorMA( start, waterfraction, dist, end2 ); + + trap_S_StartSound( end, -1, CHAN_AUTO, cgs.media.sfx_bullet_waterhit[rand() % 5] ); + + CG_MissileHitWall( fromweap, 2, end2, dir, 0 ); + //CG_MissileHitWall( fromweap, 1, end, normal, trace.surfaceFlags); + CG_MissileHitWall( fromweap, 1, end, trace.plane.normal, 0 ); + } else { + + // Gordon: um.... WTF? this doenst even make sense... + /* vec3_t start2; + VectorSubtract( end, start, dir ); + VectorNormalize( dir );*/ + /* VectorMA( end, -4, dir, start2 ); // back off a little so it doesn't start in solid + VectorMA( end, 64, dir, dir );*/ + + // Arnout: but this does! + VectorSubtract( end, start, dir ); + VectorNormalizeFast( dir ); + VectorMA( end, 4, dir, end ); + + //CG_RailTrail2( NULL, start, end ); + + CG_Trace( &trace, start, NULL, NULL, end, 0, MASK_SHOT ); + // JPW NERVE -- water check + CG_Trace( &trace2, start, NULL, NULL, end, 0, MASK_WATER | MASK_SHOT ); + if ( trace.fraction != trace2.fraction ) { + //trap_CM_BoxTrace( &trace2, start, end, NULL, NULL, -1, MASK_WATER ); + + trap_S_StartSound( end, -1, CHAN_AUTO, cgs.media.sfx_bullet_waterhit[rand() % 5] ); + + CG_Trace( &trace2, start, NULL, NULL, end, -1, MASK_WATER ); + CG_MissileHitWall( fromweap, 2, trace2.endpos, trace2.plane.normal, trace2.surfaceFlags ); + return; + } + + //CG_MissileHitWall( fromweap, 1, end, normal, trace.surfaceFlags); // smoke puff // (SA) modified to send missilehitwall surface parameters + + + //% CG_MissileHitWall( fromweap, 1, trace.endpos, trace.plane.normal, trace.surfaceFlags); // smoke puff // (SA) modified to send missilehitwall surface parameters + + // ydnar: better bullet marks + VectorSubtract( vec3_origin, dir, dir ); + CG_MissileHitWall( fromweap, 1, trace.endpos, dir, trace.surfaceFlags ); + + //CG_RailTrail2( NULL, start, trace.endpos ); + } + } + } +} diff --git a/src/cgame/cg_window.c b/src/cgame/cg_window.c new file mode 100644 index 0000000..64a3d86 --- /dev/null +++ b/src/cgame/cg_window.c @@ -0,0 +1,967 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cg_window.c: cgame window handling +// ---------------------------------- +// 24 Jul 02 +// rhea@OrangeSmoothie.org +// +#include "cg_local.h" +#include "../ui/ui_shared.h" + +extern pmove_t cg_pmove; // cg_predict.c + + +vec4_t colorClear = { 0.0f, 0.0f, 0.0f, 0.0f }; // Transparent +vec4_t colorBrown1 = { 0.3f, 0.2f, 0.1f, 0.9f }; // Brown +vec4_t colorGreen1 = { 0.21f, 0.3f, 0.0f, 0.9f }; // Greenish (darker) +vec4_t colorGreen2 = { 0.305f, 0.475f, 0.305f, 0.48f }; // Slightly off from default fill + + + +void CG_createStatsWindow( void ) { + cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FADEIN | /*WFX_SCROLLUP|*/ WFX_TRUETYPE, 110 ); + + cg.statsWindow = sw; + if ( sw == NULL ) { + return; + } + + // Window specific + sw->id = WID_STATS; + sw->fontScaleX = cf_wstats.value * 0.2f; + sw->fontScaleY = cf_wstats.value * 0.2f; +// sw->x = (cg.snap->ps.pm_type == PM_INTERMISSION) ? 10 : 160; +// sw->y = (cg.snap->ps.pm_type == PM_INTERMISSION) ? -20 : -7; // Align from bottom minus offset and height + sw->x = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? 10 : 4; + sw->y = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -20 : -100; // Align from bottom minus offset and height +} + + +void CG_createTopShotsWindow( void ) { + cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FLASH | WFX_FADEIN | WFX_SCROLLUP | WFX_TRUETYPE, 190 ); + + cg.topshotsWindow = sw; + if ( sw == NULL ) { + return; + } + + // Window specific + sw->id = WID_TOPSHOTS; + sw->fontScaleX = cf_wtopshots.value * 0.2f; + sw->fontScaleY = cf_wtopshots.value * 0.2f; + sw->x = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -10 : -20; + sw->y = ( cg.snap->ps.pm_type == PM_INTERMISSION ) ? -20 : -60; // Align from bottom minus offset and height + sw->flashMidpoint = sw->flashPeriod * 0.8f; + memcpy( &sw->colorBackground2, &colorGreen2, sizeof( vec4_t ) ); +} + + +void CG_createMOTDWindow( void ) { + const char *str = CG_ConfigString( CS_CUSTMOTD + 0 ); + + if ( str != NULL && *str != 0 ) { + int i; + cg_window_t *sw = CG_windowAlloc( WFX_TEXTSIZING | WFX_FADEIN, 500 ); + + cg.motdWindow = sw; + if ( sw == NULL ) { + return; + } + + // Window specific + sw->id = WID_MOTD; + sw->fontScaleX = 1.0f; + sw->fontScaleY = 1.0f; + sw->x = 10; + sw->y = -36; + sw->flashMidpoint = sw->flashPeriod * 0.8f; + memcpy( &sw->colorBackground2, &colorGreen2, sizeof( vec4_t ) ); + + // Copy all MOTD info into the window + cg.windowCurrent = sw; + for ( i = 0; i < MAX_MOTDLINES; i++ ) { + str = CG_ConfigString( CS_CUSTMOTD + i ); + if ( str != NULL && *str != 0 ) { + CG_printWindow( (char*)str ); + } else { + break; + } + } + } +} + +/* +void CG_createWstatsMsgWindow(void) +{ + cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FLASH, 0); + + cg.msgWstatsWindow = sw; + if(sw == NULL) return; + + // Window specific + sw->id = WID_NONE; + sw->fontScaleX = 1.0f; + sw->fontScaleY = 1.0f; + sw->x = 300; + sw->y = -1; + sw->flashPeriod = 800; + sw->flashMidpoint = sw->flashPeriod * 0.5f; + + memcpy(&sw->colorBorder, &colorClear, sizeof(vec4_t)); + memcpy(&sw->colorBackground, &colorBrown1, sizeof(vec4_t)); + memcpy(&sw->colorBackground2, &colorGreen1, sizeof(vec4_t)); + + cg.windowCurrent = sw; + CG_printWindow("^3Stats (+wstats)"); +} + + +void CG_createWtopshotsMsgWindow(void) +{ + cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FLASH, 0); + + cg.msgWtopshotsWindow = sw; + if(sw == NULL) return; + + // Window specific + sw->id = WID_NONE; + sw->fontScaleX = 1.0f; + sw->fontScaleY = 1.0f; + sw->x = -20; + sw->y = -1; + sw->flashPeriod = 1000; + sw->flashMidpoint = sw->flashPeriod * 0.7f; + + memcpy(&sw->colorBorder, &colorClear, sizeof(vec4_t)); + memcpy(&sw->colorBackground, &colorBrown1, sizeof(vec4_t)); + memcpy(&sw->colorBackground2, &colorGreen1, sizeof(vec4_t)); + + cg.windowCurrent = sw; + CG_printWindow("^3Rankings (+wtopshots)"); +} +*/ + +/* +void CG_createDemoHelpWindow(void) +{ + int i; + cg_window_t *sw = CG_windowAlloc(WFX_TEXTSIZING|WFX_FADEIN|WFX_TRUETYPE, 250); + const char *help[] = { + "^2TAB ^Nscores", + "^2F1-F5 ^Navidemo record", + "^2F11-F12 ^Nscreenshot", + " ", " ", + "^2KP_DOWN ^Nslow down (--)", + "^2KP_LEFT ^Nslow down (-)", + "^2KP_UP ^Nspeed up (++)", + "^2KP_RIGHT ^Nspeed up (+)", + "^2SPACE ^Nnormal speed", + " ", " ", + "^2ENTER ^NExternal view", + "^2LFT/RGHT ^NChange angle", + "^2UP/DOWN ^NMove in/out" + }; + const char *mvhelp[] = { + " ", " ", + "^2MOUSE1 ^NSelect/move view", + "^2MOUSE2 ^NSwap w/main view", + "^2MOUSE3 ^NToggle on/off", + "^2SHIFT ^NHold to resize", + "^2KP_PGUP ^NEnable a view", + "^2KP_PGDN ^NClose a view" + }; + + cg.demohelpWindow = sw; + if(sw == NULL) return; + + // Window specific + sw->id = WID_DEMOHELP; + sw->fontScaleX = 0.2f; + sw->fontScaleY = 0.3f; + sw->x = 2; + sw->y = 155; + sw->flashMidpoint = sw->flashPeriod * 0.8f; + memcpy(&sw->colorBackground2, &colorGreen2, sizeof(vec4_t)); + + cg.windowCurrent = sw; + for(i=0; i 1) { + for(i=0; iid = WID_SPECHELP; + sw->fontScaleX = 0.2f; + sw->fontScaleY = 0.3f; + sw->x = 2; + sw->y = 155; + sw->flashMidpoint = sw->flashPeriod * 0.8f; + memcpy(&sw->colorBackground2, &colorGreen2, sizeof(vec4_t)); + + for(i=0; i maxlen) { + maxlen = len; + } + } + } + + cg.windowCurrent = sw; + format = va("^2%%%ds ^N%%s", maxlen); + + for(i=0; ieffects = fx; + w->fontScaleX = 0.25; + w->fontScaleY = 0.25; + w->flashPeriod = 1000; + w->flashMidpoint = w->flashPeriod / 2; + w->id = WID_NONE; + w->inuse = qtrue; + w->lineCount = 0; + w->state = ( fx >= WFX_FADEIN ) ? WSTATE_START : WSTATE_COMPLETE; + w->targetTime = ( startupLength > 0 ) ? startupLength : 0; + w->time = trap_Milliseconds(); + w->x = 0; + w->y = 0; + + memcpy( &w->colorBorder, &colorGeneralBorder, sizeof( vec4_t ) ); + memcpy( &w->colorBackground, &colorGeneralFill, sizeof( vec4_t ) ); +} + +// Reserve a window +cg_window_t *CG_windowAlloc( int fx, int startupLength ) { + int i; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + + if ( wh->numActiveWindows == MAX_WINDOW_COUNT ) { + return( NULL ); + } + + for ( i = 0; i < MAX_WINDOW_COUNT; i++ ) { + w = &wh->window[i]; + if ( w->inuse == qfalse ) { + CG_windowReset( w, fx, startupLength ); + wh->activeWindows[wh->numActiveWindows++] = i; + return( w ); + } + } + + // Fail if we're a full airplane + return( NULL ); +} + + +// Free up a window reservation +void CG_windowFree( cg_window_t *w ) { + int i, j; + cg_windowHandler_t *wh = &cg.winHandler; + + if ( w == NULL ) { + return; + } + + if ( w->effects >= WFX_FADEIN && w->state != WSTATE_OFF && w->inuse == qtrue ) { + w->state = WSTATE_SHUTDOWN; + w->time = trap_Milliseconds(); + return; + } + + for ( i = 0; i < wh->numActiveWindows; i++ ) { + if ( w == &wh->window[wh->activeWindows[i]] ) { + for ( j = i; j < wh->numActiveWindows; j++ ) { + if ( j + 1 < wh->numActiveWindows ) { + wh->activeWindows[j] = wh->activeWindows[j + 1]; + } + } + + w->id = WID_NONE; + w->inuse = qfalse; + w->state = WSTATE_OFF; + + CG_removeStrings( w ); + + wh->numActiveWindows--; + + break; + } + } +} + + +void CG_windowCleanup( void ) { + int i; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + + for ( i = 0; i < wh->numActiveWindows; i++ ) { + w = &wh->window[wh->activeWindows[i]]; + if ( !w->inuse || w->state == WSTATE_OFF ) { + CG_windowFree( w ); + i--; + } + } +} + + +void CG_demoAviFPSDraw( void ) { + qboolean fKeyDown = cgs.fKeyPressed[K_F1] | cgs.fKeyPressed[K_F2] | cgs.fKeyPressed[K_F3] | cgs.fKeyPressed[K_F4] | cgs.fKeyPressed[K_F5]; + + if ( cg.demoPlayback && fKeyDown && cgs.aviDemoRate >= 0 ) { + CG_DrawStringExt( 42, 425, + ( ( cgs.aviDemoRate > 0 ) ? va( "^3Record AVI @ ^7%d^2fps", cgs.aviDemoRate ) : "^1Stop AVI Recording" ), + colorWhite, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2, 0 ); + } +} + +void CG_demoTimescaleDraw( void ) { + if ( cg.demoPlayback && cgs.timescaleUpdate > cg.time && demo_drawTimeScale.integer != 0 ) { + char *s = va( "^3TimeScale: ^7%.1f", cg_timescale.value ); + int w = CG_DrawStrlen( s ) * SMALLCHAR_WIDTH; + + CG_FillRect( 42 - 2, 400, w + 5, SMALLCHAR_HEIGHT + 3, colorDkGreen ); + CG_DrawRect( 42 - 2, 400, w + 5, SMALLCHAR_HEIGHT + 3, 1, colorMdYellow ); + + CG_DrawStringExt( 42, 400, s, colorWhite, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0 ); + } +} + + +// Main window-drawing handler +void CG_windowDraw( void ) { + int h, x, y, i, j, milli, t_offset, tmp; + cg_window_t *w; + qboolean fCleanup = qfalse; + // Gordon: FIXME, the limbomenu var no longer exists + qboolean fAllowMV = ( cg.snap != NULL && cg.snap->ps.pm_type != PM_INTERMISSION /*&& !cg.limboMenu*/ ); + vec4_t *bg; + vec4_t textColor, borderColor, bgColor; + + if ( cg.winHandler.numActiveWindows == 0 ) { + // Draw these for demoplayback no matter what + CG_demoAviFPSDraw(); + CG_demoTimescaleDraw(); + return; + } + + milli = trap_Milliseconds(); + memcpy( textColor, colorWhite, sizeof( vec4_t ) ); + + // Mouse cursor position for MV highlighting (offset for cursor pointer position) + // Also allow for swingcam toggling + if ( cg.mvTotalClients > 0 && fAllowMV ) { + CG_cursorUpdate(); + } + + for ( i = 0; i < cg.winHandler.numActiveWindows; i++ ) { + w = &cg.winHandler.window[cg.winHandler.activeWindows[i]]; + + if ( !w->inuse || w->state == WSTATE_OFF ) { + fCleanup = qtrue; + continue; + } + + // Multiview rendering has its own handling + + if ( w->effects & WFX_MULTIVIEW ) { + if ( w != cg.mvCurrentMainview && fAllowMV ) { + CG_mvDraw( w ); + } + continue; + } + + if ( w->effects & WFX_TEXTSIZING ) { + CG_windowNormalizeOnText( w ); + w->effects &= ~WFX_TEXTSIZING; + } + + bg = ( ( w->effects & WFX_FLASH ) && ( milli % w->flashPeriod ) > w->flashMidpoint ) ? &w->colorBackground2 : &w->colorBackground; + + h = w->h; + x = w->x; + y = w->y; + t_offset = milli - w->time; + textColor[3] = 1.0f; + memcpy( &borderColor, w->colorBorder, sizeof( vec4_t ) ); + memcpy( &bgColor, bg, sizeof( vec4_t ) ); + + // TODO: Add in support for ALL scrolling effects + if ( w->state == WSTATE_START ) { + tmp = w->targetTime - t_offset; + if ( w->effects & WFX_SCROLLUP ) { + if ( tmp > 0 ) { + y += ( 480 - y ) * tmp / w->targetTime; //(100 * tmp / w->targetTime) / 100; + } else { + w->state = WSTATE_COMPLETE; + } + + w->curY = y; + } + if ( w->effects & WFX_FADEIN ) { + if ( tmp > 0 ) { + textColor[3] = (float)( (float)t_offset / (float)w->targetTime ); + } else { w->state = WSTATE_COMPLETE;} + } + } else if ( w->state == WSTATE_SHUTDOWN ) { + tmp = w->targetTime - t_offset; + if ( w->effects & WFX_SCROLLUP ) { + if ( tmp > 0 ) { + y = w->curY + ( 480 - w->y ) * t_offset / w->targetTime; //(100 * t_offset / w->targetTime) / 100; + } + if ( tmp < 0 || y >= 480 ) { + w->state = WSTATE_OFF; + fCleanup = qtrue; + continue; + } + } + if ( w->effects & WFX_FADEIN ) { + if ( tmp > 0 ) { + textColor[3] -= (float)( (float)t_offset / (float)w->targetTime ); + } else { + textColor[3] = 0.0f; + w->state = WSTATE_OFF; + } + } + } + + borderColor[3] *= textColor[3]; + bgColor[3] *= textColor[3]; + + CG_FillRect( x, y, w->w, h, bgColor ); + CG_DrawRect( x, y, w->w, h, 1, borderColor ); + + x += 5; + y -= ( w->effects & WFX_TRUETYPE ) ? 3 : 0; + + for ( j = w->lineCount - 1; j >= 0; j-- ) { + if ( w->effects & WFX_TRUETYPE ) { +// CG_Text_Paint(x, y + h, w->fontScale, textColor, (char*)w->lineText[j], 0.0f, 0, 0); + CG_Text_Paint_Ext( x, y + h, w->fontScaleX, w->fontScaleY, textColor, + (char*)w->lineText[j], 0.0f, 0, 0, &cgs.media.limboFont2 ); + } + + h -= ( w->lineHeight[j] + 3 ); + + if ( !( w->effects & WFX_TRUETYPE ) ) { + CG_DrawStringExt2( x, y + h, (char*)w->lineText[j], textColor, + qfalse, qtrue, w->fontWidth, w->fontHeight, 0 ); + } + } + } + + // Wedge in MV info overlay + if ( cg.mvTotalClients > 0 && fAllowMV ) { + CG_mvOverlayDisplay(); + } + + // Extra rate info + CG_demoAviFPSDraw(); + CG_demoTimescaleDraw(); + + // Mouse cursor lays on top of everything + if ( cg.mvTotalClients > 0 && cg.time < cgs.cursorUpdate && fAllowMV ) { + //CG_DrawPic(cgs.cursorX - CURSOR_OFFSETX, cgs.cursorY - CURSOR_OFFSETY, 32, 32, cgs.media.cursor); + CG_DrawPic( cgDC.cursorx, cgDC.cursory, 32, 32, cgs.media.cursorIcon ); + } + + if ( fCleanup ) { + CG_windowCleanup(); + } +} + + +// Set the window width and height based on the windows text/font parameters +void CG_windowNormalizeOnText( cg_window_t *w ) { + int i, tmp; + + if ( w == NULL ) { + return; + } + + w->w = 0; + w->h = 0; + + if ( !( w->effects & WFX_TRUETYPE ) ) { + w->fontWidth = w->fontScaleX * WINDOW_FONTWIDTH; + w->fontHeight = w->fontScaleY * WINDOW_FONTHEIGHT; + } + + for ( i = 0; i < w->lineCount; i++ ) { + if ( w->effects & WFX_TRUETYPE ) { + tmp = CG_Text_Width_Ext( (char*)w->lineText[i], w->fontScaleX, 0, &cgs.media.limboFont2 ); + } else { + tmp = CG_DrawStrlen( (char*)w->lineText[i] ) * w->fontWidth; + } + + if ( tmp > w->w ) { + w->w = tmp; + } + } + + for ( i = 0; i < w->lineCount; i++ ) { + if ( w->effects & WFX_TRUETYPE ) { + w->lineHeight[i] = CG_Text_Height_Ext( (char*)w->lineText[i], w->fontScaleY, 0, &cgs.media.limboFont2 ); + } else { + w->lineHeight[i] = w->fontHeight; + } + + w->h += w->lineHeight[i] + 3; + } + + // Border + margins + w->w += 10; + w->h += 3; + + // Set up bottom alignment + if ( w->x < 0 ) { + w->x += 640 - w->w; + } + if ( w->y < 0 ) { + w->y += 480 - w->h; + } +} + + +void CG_printWindow( char *str ) { + int pos = 0, pos2 = 0; + char buf[MAX_STRING_CHARS]; + cg_window_t *w = cg.windowCurrent; + + if ( w == NULL ) { + return; + } + + // Silly logic for a strict format + Q_strncpyz( buf, str, MAX_STRING_CHARS ); + while ( buf[pos] > 0 && w->lineCount < MAX_WINDOW_LINES ) { + if ( buf[pos] == '\n' ) { + if ( pos2 == pos ) { + if ( !CG_addString( w, " " ) ) { + return; + } + } else { + buf[pos] = 0; + if ( !CG_addString( w, buf + pos2 ) ) { + return; + } + } + pos2 = ++pos; + continue; + } + pos++; + } + + if ( pos2 < pos ) { + CG_addString( w, buf + pos2 ); + } +} + + + +// +// String buffer handling +// +void CG_initStrings( void ) { + int i; + + for ( i = 0; i < MAX_STRINGS; i++ ) { + cg.aStringPool[i].fActive = qfalse; + cg.aStringPool[i].str[0] = 0; + } +} + +qboolean CG_addString( cg_window_t *w, char *buf ) { + int i; + + // Check if we're reusing the current buf + if ( w->lineText[w->lineCount] != NULL ) { + for ( i = 0; i < MAX_STRINGS; i++ ) { + if ( !cg.aStringPool[i].fActive ) { + continue; + } + + if ( w->lineText[w->lineCount] == (char *)&cg.aStringPool[i].str ) { + w->lineCount++; + cg.aStringPool[i].fActive = qtrue; + strcpy( cg.aStringPool[i].str, buf ); + + return( qtrue ); + } + } + } + + for ( i = 0; i < MAX_STRINGS; i++ ) { + if ( !cg.aStringPool[i].fActive ) { + cg.aStringPool[i].fActive = qtrue; + strcpy( cg.aStringPool[i].str, buf ); + w->lineText[w->lineCount++] = (char *)&cg.aStringPool[i].str; + + return( qtrue ); + } + } + + return( qfalse ); +} + +void CG_removeStrings( cg_window_t *w ) { + int i, j; + + for ( i = 0; i < w->lineCount; i++ ) { + char *ref = w->lineText[i]; + + for ( j = 0; j < MAX_STRINGS; j++ ) { + if ( !cg.aStringPool[j].fActive ) { + continue; + } + + if ( ref == (char *)&cg.aStringPool[j].str ) { + w->lineText[i] = NULL; + cg.aStringPool[j].fActive = qfalse; + cg.aStringPool[j].str[0] = 0; + + break; + } + } + } +} + + + + +// +// cgame cursor handling +// + +// Mouse overlay for controlling multiview windows +void CG_cursorUpdate( void ) { + int i, j, x; + float nx, ny; + int nSelectedWindow = -1; + cg_window_t *w; + cg_windowHandler_t *wh = &cg.winHandler; + qboolean fFound = qfalse, fUpdateOverlay = qfalse; + qboolean fSelect, fResize; + + + // Get cursor current position (when connected to a server) + if ( !cg.demoPlayback ) { + + // Allow for limbo'd updates as well + trap_GetUserCmd( trap_GetCurrentCmdNumber(), &cg_pmove.cmd ); + + nx = 640.0 * ( 65536.0 - cg_pmove.cmd.angles[1] ) / 65536.0; + ny = 480.0 / 65536.0 * ( ( int_m_pitch.value < 0.0 ) ? ( 65536.0 - cg_pmove.cmd.angles[0] ) : cg_pmove.cmd.angles[0] ); + + fSelect = ( ( cg_pmove.cmd.buttons & BUTTON_ATTACK ) != 0 ); + + if ( cgs.cursorX == (int)nx && cgs.cursorY == (int)ny && !fSelect ) { + return; + } + + fResize = ( ( cg_pmove.cmd.buttons & BUTTON_SPRINT ) != 0 ); + + cgs.cursorUpdate = cg.time + 5000; + cgs.cursorX = nx; + cgs.cursorY = ny; + } else { + // Already updated in the keycatcher + nx = cgs.cursorX; + ny = cgs.cursorY; + fSelect = cgs.fSelect; + fResize = cgs.fResize; + } + + // For mm4 + cg.mvCurrentActive = cg.mvCurrentMainview; + + // For overlay highlights + for ( i = 0; i < cg.mvTotalClients; i++ ) { + cg.mvOverlay[i].fActive = qfalse; + } + + for ( i = wh->numActiveWindows - 1; i >= 0; i-- ) { + w = &wh->window[wh->activeWindows[i]]; + if ( ( w->effects & WFX_MULTIVIEW ) && w != cg.mvCurrentMainview ) { + // Mouse/window detection + // If the current window is selected, and the button is down, then allow the update + // to occur, as quick mouse movements can move it past the window borders + if ( !fFound && + ( + ( ( w->mvInfo & MV_SELECTED ) && fSelect ) || + ( !fSelect && nx >= w->x && nx < w->x + w->w && ny >= w->y && ny < w->y + w->h ) + ) ) { + if ( !( w->mvInfo & MV_SELECTED ) ) { + w->mvInfo |= MV_SELECTED; + nSelectedWindow = i; + } + + // If not dragging/resizing, prime for later update + if ( !fSelect ) { + w->m_x = -1.0f; + w->m_y = -1.0f; + } else { + if ( w->m_x > 0 && w->m_y > 0 ) { + if ( fResize ) { + w->w += nx - w->m_x; + if ( w->x + w->w > 640 - 2 ) { + w->w = 640 - 2 - w->x; + } + if ( w->w < 64 ) { + w->w = 64; + } + + w->h += ny - w->m_y; + if ( w->y + w->h > 480 - 2 ) { + w->h = 480 - 2 - w->y; + } + if ( w->h < 48 ) { + w->h = 48; + } + } else { + w->x += nx - w->m_x; + if ( w->x + w->w > 640 - 2 ) { + w->x = 640 - 2 - w->w; + } + if ( w->x < 2 ) { + w->x = 2; + } + + w->y += ny - w->m_y; + if ( w->y + w->h > 480 - 2 ) { + w->y = 480 - 2 - w->h; + } + if ( w->y < 2 ) { + w->y = 2; + } + } + } + + w->m_x = nx; + w->m_y = ny; + } + + fFound = qtrue; + cg.mvCurrentActive = w; + + // Reset mouse info for window if it loses focuse + } else if ( w->mvInfo & MV_SELECTED ) { + fUpdateOverlay = qtrue; + w->m_x = -1.0f; + w->m_y = -1.0f; + w->mvInfo &= ~MV_SELECTED; + + if ( fFound ) { + break; // Small optimization: we've found a new window, and cleared the old focus + } + } + } + } + + nx = (float)( MVINFO_RIGHT - ( MVINFO_TEXTSIZE * 3 ) ); + ny = (float)( MVINFO_TOP + ( MVINFO_TEXTSIZE + 1 ) ); + + // Highlight corresponding active window's overlay element + if ( fFound ) { + for ( i = 0; i < cg.mvTotalClients; i++ ) { + if ( cg.mvOverlay[i].pID == ( cg.mvCurrentActive->mvInfo & MV_PID ) ) { + cg.mvOverlay[i].fActive = qtrue; + break; + } + } + } + // Check MV overlay detection here for better perf with more text elements + // General boundary check + else { + // Ugh, have to loop through BOTH team lists + int vOffset = 0; + + for ( i = TEAM_AXIS; i <= TEAM_ALLIES; i++ ) { + if ( cg.mvTotalTeam[i] == 0 ) { + continue; + } + if ( cgs.cursorX >= nx && cgs.cursorY >= ny && cgs.cursorX < MVINFO_RIGHT && + cgs.cursorY < ny + ( cg.mvTotalTeam[i] * ( MVINFO_TEXTSIZE + 1 ) ) ) { + int pos = (int)( cgs.cursorY - ny ) / ( MVINFO_TEXTSIZE + 1 ); + + if ( pos < cg.mvTotalTeam[i] ) { + int x = MVINFO_RIGHT - cg.mvOverlay[( cg.mvTeamList[i][pos] )].width; + int y = MVINFO_TOP + vOffset + ( ( pos + 1 ) * ( MVINFO_TEXTSIZE + 1 ) ); + + // See if we're really over something + if ( cgs.cursorX >= x && cgs.cursorY >= y && + cgs.cursorX <= MVINFO_RIGHT && + cgs.cursorY <= y + MVINFO_TEXTSIZE ) { + // Perform any other window handling here for MV + // views based on element selection + cg.mvOverlay[( cg.mvTeamList[i][pos] )].fActive = qtrue; + + w = CG_mvClientLocate( cg.mvOverlay[( cg.mvTeamList[i][pos] )].pID ); + if ( w != NULL ) { + cg.mvCurrentActive = w; + } + + if ( fSelect ) { + if ( w != NULL ) { + // Swap window-view with mainview + if ( w != cg.mvCurrentMainview ) { + CG_mvMainviewSwap( w ); + } + } else { + // Swap non-view with mainview + cg.mvCurrentMainview->mvInfo = ( cg.mvCurrentMainview->mvInfo & ~MV_PID ) | + ( cg.mvOverlay[cg.mvTeamList[i][pos]].pID & MV_PID ); + fUpdateOverlay = qtrue; + } + } + } + } + } + vOffset += ( cg.mvTotalTeam[i] + 2 ) * ( MVINFO_TEXTSIZE + 1 ); + ny += vOffset; + } + } + + // If we have a new highlight, reorder so our highlight is always + // drawn last (on top of all other windows) + if ( nSelectedWindow >= 0 ) { + fUpdateOverlay = qtrue; + x = wh->activeWindows[nSelectedWindow]; + + for ( j = nSelectedWindow; j < wh->numActiveWindows - 1; j++ ) { + wh->activeWindows[j] = wh->activeWindows[j + 1]; + } + + wh->activeWindows[wh->numActiveWindows - 1] = x; + } + + // Finally, sync the overlay, if needed + if ( fUpdateOverlay ) { + CG_mvOverlayUpdate(); + } +} diff --git a/src/cgame/cgame.def b/src/cgame/cgame.def new file mode 100644 index 0000000..2ee748e --- /dev/null +++ b/src/cgame/cgame.def @@ -0,0 +1,3 @@ +EXPORTS + vmMain + dllEntry diff --git a/src/cgame/cgame.vcproj b/src/cgame/cgame.vcproj new file mode 100644 index 0000000..882072c --- /dev/null +++ b/src/cgame/cgame.vcproj @@ -0,0 +1,506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cgame/tr_types.h b/src/cgame/tr_types.h new file mode 100644 index 0000000..433490a --- /dev/null +++ b/src/cgame/tr_types.h @@ -0,0 +1,352 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#ifndef __TR_TYPES_H +#define __TR_TYPES_H + + +#define MAX_CORONAS 32 //----(SA) not really a reason to limit this other than trying to keep a reasonable count +#define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces +#define MAX_ENTITIES 1023 // can't be increased without changing drawsurf bit packing + +// renderfx flags +#define RF_MINLIGHT 0x000001 // allways have some light (viewmodel, some items) +#define RF_THIRD_PERSON 0x000002 // don't draw through eyes, only mirrors (player bodies, chat sprites) +#define RF_FIRST_PERSON 0x000004 // only draw through eyes (view weapon, damage blood blob) +#define RF_DEPTHHACK 0x000008 // for view weapon Z crunching +#define RF_NOSHADOW 0x000010 // don't add stencil shadows + +#define RF_LIGHTING_ORIGIN 0x000020 // use refEntity->lightingOrigin instead of refEntity->origin + // for lighting. This allows entities to sink into the floor + // with their origin going solid, and allows all parts of a + // player to get the same lighting +#define RF_SHADOW_PLANE 0x000040 // use refEntity->shadowPlane +#define RF_WRAP_FRAMES 0x000080 // mod the model frames by the maxframes to allow continuous + // animation without needing to know the frame count +#define RF_HILIGHT 0x000100 // more than RF_MINLIGHT. For when an object is "Highlighted" (looked at/training identification/etc) +#define RF_BLINK 0x000200 // eyes in 'blink' state +#define RF_FORCENOLOD 0x000400 + +// refdef flags +#define RDF_NOWORLDMODEL 1 // used for player configuration screen +#define RDF_HYPERSPACE 4 // teleportation effect + +// Rafael +#define RDF_SKYBOXPORTAL 8 + +//----(SA) +#define RDF_UNDERWATER ( 1 << 4 ) // so the renderer knows to use underwater fog when the player is underwater +#define RDF_DRAWINGSKY ( 1 << 5 ) +#define RDF_SNOOPERVIEW ( 1 << 6 ) //----(SA) added + + +typedef struct { + vec3_t xyz; + float st[2]; + byte modulate[4]; +} polyVert_t; + +typedef struct poly_s { + qhandle_t hShader; + int numVerts; + polyVert_t *verts; +} poly_t; + +typedef enum { + RT_MODEL, + RT_POLY, + RT_SPRITE, + RT_SPLASH, // ripple effect + RT_BEAM, + RT_RAIL_CORE, + RT_RAIL_CORE_TAPER, // a modified core that creates a properly texture mapped core that's wider at one end + RT_RAIL_RINGS, + RT_LIGHTNING, + RT_PORTALSURFACE, // doesn't draw anything, just info for portals + + RT_MAX_REF_ENTITY_TYPE +} refEntityType_t; + +#define ZOMBIEFX_FADEOUT_TIME 10000 + +#define REFLAG_ONLYHAND 1 // only draw hand surfaces +#define REFLAG_FORCE_LOD 8 // force a low lod +#define REFLAG_ORIENT_LOD 16 // on LOD switch, align the model to the player's camera +#define REFLAG_DEAD_LOD 32 // allow the LOD to go lower than recommended + +typedef struct { + refEntityType_t reType; + int renderfx; + + qhandle_t hModel; // opaque type outside refresh + + // most recent data + vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) + float shadowPlane; // projection shadows go here, stencils go slightly lower + + vec3_t axis[3]; // rotation vectors + vec3_t torsoAxis[3]; // rotation vectors for torso section of skeletal animation + qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale + float origin[3]; // also used as MODEL_BEAM's "from" + int frame; // also used as MODEL_BEAM's diameter + qhandle_t frameModel; + int torsoFrame; // skeletal torso can have frame independant of legs frame + qhandle_t torsoFrameModel; + + // previous data for frame interpolation + float oldorigin[3]; // also used as MODEL_BEAM's "to" + int oldframe; + qhandle_t oldframeModel; + int oldTorsoFrame; + qhandle_t oldTorsoFrameModel; + float backlerp; // 0.0 = current, 1.0 = old + float torsoBacklerp; + + // texturing + int skinNum; // inline skin index + qhandle_t customSkin; // NULL for default skin + qhandle_t customShader; // use one image for the entire thing + + // misc + byte shaderRGBA[4]; // colors used by rgbgen entity shaders + float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers + float shaderTime; // subtracted from refdef time to control effect start times + + // extra sprite information + float radius; + float rotation; + + // Ridah + vec3_t fireRiseDir; + + // Ridah, entity fading (gibs, debris, etc) + int fadeStartTime, fadeEndTime; + + float hilightIntensity; //----(SA) added + + int reFlags; + + int entityNum; // currentState.number, so we can attach rendering effects to specific entities (Zombie) + +} refEntity_t; + +//----(SA) + +// // +// WARNING:: synch FOG_SERVER in sv_ccmds.c if you change anything // +// // +typedef enum { + FOG_NONE, // 0 + + FOG_SKY, // 1 fog values to apply to the sky when using density fog for the world (non-distance clipping fog) (only used if(glfogsettings[FOG_MAP].registered) or if(glfogsettings[FOG_MAP].registered)) + FOG_PORTALVIEW, // 2 used by the portal sky scene + FOG_HUD, // 3 used by the 3D hud scene + + // The result of these for a given frame is copied to the scene.glFog when the scene is rendered + + // the following are fogs applied to the main world scene + FOG_MAP, // 4 use fog parameter specified using the "fogvars" in the sky shader + FOG_WATER, // 5 used when underwater + FOG_SERVER, // 6 the server has set my fog (probably a target_fog) (keep synch in sv_ccmds.c !!!) + FOG_CURRENT, // 7 stores the current values when a transition starts + FOG_LAST, // 8 stores the current values when a transition starts + FOG_TARGET, // 9 the values it's transitioning to. + + FOG_CMD_SWITCHFOG, // 10 transition to the fog specified in the second parameter of R_SetFog(...) (keep synch in sv_ccmds.c !!!) + + NUM_FOGS +} glfogType_t; + + +typedef struct { + int mode; // GL_LINEAR, GL_EXP + int hint; // GL_DONT_CARE + int startTime; // in ms + int finishTime; // in ms + float color[4]; + float start; // near + float end; // far + qboolean useEndForClip; // use the 'far' value for the far clipping plane + float density; // 0.0-1.0 + qboolean registered; // has this fog been set up? + qboolean drawsky; // draw skybox + qboolean clearscreen; // clear the GL color buffer +} glfog_t; + +//----(SA) end + + +#define MAX_RENDER_STRINGS 8 +#define MAX_RENDER_STRING_LENGTH 32 + +typedef struct { + int x, y, width, height; + float fov_x, fov_y; + vec3_t vieworg; + vec3_t viewaxis[3]; // transformation matrix + + int time; // time in milliseconds for shader effects and other time dependent rendering issues + int rdflags; // RDF_NOWORLDMODEL, etc + + // 1 bits will prevent the associated area from rendering at all + byte areamask[MAX_MAP_AREA_BYTES]; + + + + + // text messages for deform text shaders + char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; + + +//----(SA) added (needed to pass fog infos into the portal sky scene) + glfog_t glfog; +//----(SA) end + +} refdef_t; + + +typedef enum { + STEREO_CENTER, + STEREO_LEFT, + STEREO_RIGHT +} stereoFrame_t; + + +/* +** glconfig_t +** +** Contains variables specific to the OpenGL configuration +** being run right now. These are constant once the OpenGL +** subsystem is initialized. +*/ +typedef enum { + TC_NONE, + TC_S3TC, + TC_EXT_COMP_S3TC +} textureCompression_t; + +typedef enum { + GLDRV_ICD, // driver is integrated with window system + // WARNING: there are tests that check for + // > GLDRV_ICD for minidriverness, so this + // should always be the lowest value in this + // enum set + GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver + GLDRV_VOODOO // driver is a 3Dfx standalone driver +} glDriverType_t; + +typedef enum { + GLHW_GENERIC, // where everthing works the way it should + GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is + // the hardware type then there can NOT exist a secondary + // display adapter + GLHW_RIVA128, // where you can't interpolate alpha + GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures + GLHW_PERMEDIA2 // where you don't have src*dst +} glHardwareType_t; + +typedef struct { + char renderer_string[MAX_STRING_CHARS]; + char vendor_string[MAX_STRING_CHARS]; + char version_string[MAX_STRING_CHARS]; + char extensions_string[MAX_STRING_CHARS * 4]; // TTimo - bumping, some cards have a big extension string + + int maxTextureSize; // queried from GL + int maxActiveTextures; // multitexture ability + + int colorBits, depthBits, stencilBits; + + glDriverType_t driverType; + glHardwareType_t hardwareType; + + qboolean deviceSupportsGamma; + textureCompression_t textureCompression; + qboolean textureEnvAddAvailable; + qboolean anisotropicAvailable; //----(SA) added + float maxAnisotropy; //----(SA) added + + // vendor-specific support + // NVidia + qboolean NVFogAvailable; //----(SA) added + int NVFogMode; //----(SA) added + // ATI + int ATIMaxTruformTess; // for truform support + int ATINormalMode; // for truform support + int ATIPointMode; // for truform support + + int vidWidth, vidHeight; + // aspect is the screen's physical width / height, which may be different + // than scrWidth / scrHeight if the pixels are non-square + // normal screens should be 4/3, but wide aspect monitors may be 16/9 + float windowAspect; + + int displayFrequency; + + // synonymous with "does rendering consume the entire screen?", therefore + // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that + // used CDS. + qboolean isFullscreen; + qboolean stereoEnabled; + qboolean smpActive; // dual processor +} glconfig_t; + + +#if !defined _WIN32 + +#define _3DFX_DRIVER_NAME "libMesaVoodooGL.so.3.1" +#define OPENGL_DRIVER_NAME "libGL.so.1" + +#else + +#define _3DFX_DRIVER_NAME "3dfxvgl" +#define OPENGL_DRIVER_NAME "opengl32" +#define WICKED3D_V5_DRIVER_NAME "gl/openglv5.dll" +#define WICKED3D_V3_DRIVER_NAME "gl/openglv3.dll" + +#endif // !defined _WIN32 + + +// ========================================= +// Gordon, these MUST NOT exceed the values for SHADER_MAX_VERTEXES/SHADER_MAX_INDEXES +#define MAX_PB_VERTS 1025 +#define MAX_PB_INDICIES ( MAX_PB_VERTS * 6 ) + +typedef struct polyBuffer_s { + vec4_t xyz[MAX_PB_VERTS]; + vec2_t st[MAX_PB_VERTS]; + byte color[MAX_PB_VERTS][4]; + int numVerts; + + int indicies[MAX_PB_INDICIES]; + int numIndicies; + + qhandle_t shader; +} polyBuffer_t; +// ========================================= + +#endif // __TR_TYPES_H diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c new file mode 100644 index 0000000..20d37da --- /dev/null +++ b/src/client/cl_cgame.c @@ -0,0 +1,1473 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cl_cgame.c -- client system interaction with client game + +#include "client.h" + +#include "../game/botlib.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 ); + +// 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_AddCgameCommand +============== +*/ +void CL_AddCgameCommand( const char *cmdName ) { + Cmd_AddCommand( cmdName, NULL ); +} + +/* +============== +CL_CgameError +============== +*/ +void CL_CgameError( const char *string ) { + Com_Error( ERR_DROP, "%s", string ); +} + +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, va( "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; + } + + + // the clientLevelShot command is used during development + // to generate 128*128 screenshots from the intermission + // point of levels for the menu system to use + // we pass it along to the cgame to make apropriate adjustments, + // but we also clear the console and notify lines here + if ( !strcmp( cmd, "clientLevelShot" ) ) { + // don't do it if we aren't running the server locally, + // otherwise malicious remote servers could overwrite + // the existing thumbnails + if ( !com_sv_running->integer ) { + return qfalse; + } + // close the console + Con_Close(); + // take a special screenshot next frame + Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" ); + return qtrue; + } + + // we may want to put a "connect to other server" command here + + // cgame can now act on the command + return qtrue; +} + +// 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 ) { + int temp; + + *(float *)&temp = f; + + return temp; +} + +//static int numtraces = 0; + +/* +==================== +CL_CgameSystemCalls + +The cgame module is making a system call +==================== +*/ +#define VMA( x ) VM_ArgPtr( args[x] ) +#define VMF( x ) ( (float *)args )[x] + +int CL_CgameSystemCalls( int *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_FS_FOPENFILE: + return FS_FOpenFileByMode( VMA( 1 ), VMA( 2 ), args[3] ); + case CG_FS_READ: + FS_Read( 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_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 ); + 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 ); + 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 ); + 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 ); + 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], args[5] ); + return 0; +//----(SA) added + case CG_S_STARTSOUNDEX: + 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] ); + return 0; + case CG_S_CLEARLOOPINGSOUNDS: + S_ClearLoopingSounds(); + return 0; + case CG_S_CLEARSOUNDS: + 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] ); + return 0; + case CG_S_ADDREALLOOPINGSOUND: + S_AddRealLoopingSound( VMA( 1 ), VMA( 2 ), args[3], args[4], args[5], args[6] ); + return 0; + case CG_S_STOPSTREAMINGSOUND: + 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: + return S_GetVoiceAmplitude( args[1] ); +// done. + case CG_S_GETSOUNDLENGTH: + return S_GetSoundLength( args[1] ); + + // ydnar: for looped sound starts + case CG_S_GETCURRENTSOUNDTIME: + return S_GetCurrentSoundTime(); + + 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 + return 0; + case CG_S_FADESTREAMINGSOUND: + S_FadeStreamingSound( VMF( 1 ), args[2], args[3] ); //----(SA) added music/all-streaming options + return 0; + case CG_S_STARTSTREAMINGSOUND: + return S_StartStreamingSound( VMA( 1 ), VMA( 2 ), args[3], args[4], args[5] ); + case CG_R_LOADWORLDMAP: + re.LoadWorld( VMA( 1 ) ); + return 0; + case CG_R_REGISTERMODEL: + return re.RegisterModel( VMA( 1 ) ); + case CG_R_REGISTERSKIN: + return re.RegisterSkin( VMA( 1 ) ); + + //----(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: + return re.RegisterShader( VMA( 1 ) ); + case CG_R_REGISTERFONT: + re.RegisterFont( VMA( 1 ), args[2], VMA( 3 ) ); + return 0; + case CG_R_REGISTERSHADERNOMIP: + return re.RegisterShaderNoMip( VMA( 1 ) ); + case CG_R_CLEARSCENE: + re.ClearScene(); + return 0; + case CG_R_ADDREFENTITYTOSCENE: + re.AddRefEntityToScene( VMA( 1 ) ); + return 0; + case CG_R_ADDPOLYTOSCENE: + re.AddPolyToScene( args[1], args[2], VMA( 3 ) ); + 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_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; + 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: + return (int)memset( VMA( 1 ), args[2], args[3] ); + case CG_MEMCPY: + return (int)memcpy( VMA( 1 ), VMA( 2 ), args[3] ); + case CG_STRNCPY: + return (int)strncpy( VMA( 1 ), VMA( 2 ), args[3] ); + 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: + Sys_SnapVector( VMA( 1 ) ); + return 0; + + case CG_CIN_PLAYCINEMATIC: + return CIN_PlayCinematic( VMA( 1 ), args[2], args[3], args[4], args[5], args[6] ); + + case CG_CIN_STOPCINEMATIC: + return CIN_StopCinematic( args[1] ); + + case CG_CIN_RUNCINEMATIC: + return CIN_RunCinematic( args[1] ); + + case CG_CIN_DRAWCINEMATIC: + CIN_DrawCinematic( args[1] ); + return 0; + + case CG_CIN_SETEXTENTS: + CIN_SetExtents( args[1], args[2], args[3], args[4], args[5] ); + return 0; + + case CG_R_REMAP_SHADER: + re.RemapShader( VMA( 1 ), VMA( 2 ), VMA( 3 ) ); + return 0; + + case CG_TESTPRINTINT: + Com_Printf( "%s%i\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 ) ); + + 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 ) ); + + 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; + + 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: + 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; + default: + Com_Error( ERR_DROP, "Bad cgame system trap: %i", 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 ); + + // load the dll + cgvm = VM_Create( "cgame", CL_CgameSystemCalls, VMI_NATIVE ); + 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_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; + + // 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", "" ); + } + + Sys_BeginProfiling(); +} + +/* +================== +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/client/cl_cin.c b/src/client/cl_cin.c new file mode 100644 index 0000000..cdc5c32 --- /dev/null +++ b/src/client/cl_cin.c @@ -0,0 +1,1816 @@ +/* +=========================================================================== + +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: 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 glconfig_t glConfig; +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 struct { + byte linbuf[DEFAULT_CIN_WIDTH * DEFAULT_CIN_HEIGHT * 4 * 2]; + byte file[65536]; + short sqrTable[256]; + + unsigned int mcomp[256]; + byte *qStatus[2][32768]; + + long oldXOff, oldYOff, oldysize, oldxsize; + + int currentHandle; +} cinematics_t; + +typedef struct { + char fileName[MAX_OSPATH]; + int CIN_WIDTH, CIN_HEIGHT; + int xpos, ypos, width, height; + qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader; + fileHandle_t iFile; + e_status status; + unsigned int startTime; + unsigned int lastTime; + long tfps; + long RoQPlayed; + long ROQSize; + unsigned int RoQFrameSize; + long onQuad; + long numQuads; + long samplesPerLine; + unsigned int roq_id; + long screenDelta; + + void ( *VQ0 )( byte *status, void *qdata ); + void ( *VQ1 )( byte *status, void *qdata ); + void ( *VQNormal )( byte *status, void *qdata ); + void ( *VQBuffer )( byte *status, void *qdata ); + + long samplesPerPixel; // defaults to 2 + byte* gray; + unsigned int xsize, ysize, maxsize, minsize; + + qboolean half, smootheddouble, inMemory; + long normalBuffer0; + long roq_flags; + long roqF0; + long roqF1; + long t[2]; + long roqFPS; + int playonwalls; + byte* buf; + long drawX, drawY; +} cin_cache; + +static cinematics_t cin; +static cin_cache cinTable[MAX_VIDEO_HANDLES]; +static int currentHandle = -1; +static int CL_handle = -1; + +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() { + 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 < size; z++ ) { + prev = to[z] = (short)( prev + cin.sqrTable[from[z]] ); + } + return size; //*sizeof(short)); +} + + +//----------------------------------------------------------------------------- +// RllDecodeMonoToStereo +// +// Decode mono source data into a stereo buffer. Output is 4 times the number +// of bytes in the input. +// +// Parameters: from -> 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 < size; z += 2 ) { + prevL = (short)( prevL + cin.sqrTable[*zz++] ); + prevR = (short)( prevR + cin.sqrTable[*zz++] ); + to[z + 0] = (short)( prevL ); + to[z + 1] = (short)( prevR ); + } + + return ( size >> 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 < size; z += 1 ) { + prevL = prevL + cin.sqrTable[from[z * 2]]; + prevR = prevR + cin.sqrTable[from[z * 2 + 1]]; + to[z] = (short)( ( prevL + prevR ) / 2 ); + } + + return size; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void move8_32( byte *src, byte *dst, int spl ) { + double *dsrc, *ddst; + int dspl; + + dsrc = (double *)src; + ddst = (double *)dst; + dspl = spl >> 3; + + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void move4_32( byte *src, byte *dst, int spl ) { + double *dsrc, *ddst; + int dspl; + + dsrc = (double *)src; + ddst = (double *)dst; + dspl = spl >> 3; + + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += dspl; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void blit8_32( byte *src, byte *dst, int spl ) { + double *dsrc, *ddst; + int dspl; + + dsrc = (double *)src; + ddst = (double *)dst; + dspl = spl >> 3; + + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; + dsrc += 4; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +#define movs double +static void blit4_32( byte *src, byte *dst, int spl ) { + movs *dsrc, *ddst; + int dspl; + + dsrc = (movs *)src; + ddst = (movs *)dst; + dspl = spl >> 3; + + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += 2; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += 2; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; + dsrc += 2; ddst += dspl; + ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void blit2_32( byte *src, byte *dst, int spl ) { + double *dsrc, *ddst; + int dspl; + + dsrc = (double *)src; + ddst = (double *)dst; + dspl = spl >> 3; + + ddst[0] = dsrc[0]; + ddst[dspl] = dsrc[1]; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void blitVQQuad32fs( byte **status, unsigned char *data ) { + unsigned short newd, celdata, code; + unsigned int index, i; + int spl; + + newd = 0; + celdata = 0; + index = 0; + + spl = cinTable[currentHandle].samplesPerLine; + + do { + if ( !newd ) { + newd = 7; + celdata = data[0] + data[1] * 256; + data += 2; + } else { + newd--; + } + + code = ( unsigned short )( celdata & 0xc000 ); + celdata <<= 2; + + switch ( code ) { + case 0x8000: // vq code + blit8_32( (byte *)&vq8[( *data ) * 128], status[index], spl ); + data++; + index += 5; + break; + case 0xc000: // drop + index++; // skip 8x8 + for ( i = 0; i < 4; i++ ) { + if ( !newd ) { + newd = 7; + celdata = data[0] + data[1] * 256; + data += 2; + } else { + newd--; + } + + code = ( unsigned short )( celdata & 0xc000 ); celdata <<= 2; + + switch ( code ) { // code in top two bits of code + case 0x8000: // 4x4 vq code + blit4_32( (byte *)&vq4[( *data ) * 32], status[index], spl ); + data++; + break; + case 0xc000: // 2x2 vq code + blit2_32( (byte *)&vq2[( *data ) * 8], status[index], spl ); + data++; + blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + 8, spl ); + data++; + blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + spl * 2, spl ); + data++; + blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + spl * 2 + 8, spl ); + data++; + break; + case 0x4000: // motion compensation + move4_32( status[index] + cin.mcomp[( *data )], status[index], spl ); + data++; + break; + } + index++; + } + break; + case 0x4000: // motion compensation + move8_32( status[index] + cin.mcomp[( *data )], status[index], spl ); + data++; + index += 5; + break; + case 0x0000: + index += 5; + break; + } + } while ( status[index] != NULL ); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void ROQ_GenYUVTables( void ) { + float t_ub,t_vr,t_ug,t_vg; + long i; + + t_ub = ( 1.77200f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; + t_vr = ( 1.40200f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; + t_ug = ( 0.34414f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; + t_vg = ( 0.71414f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; + for ( i = 0; i < 256; i++ ) { + float x = (float)( 2 * i - 255 ); + + ROQ_UB_tab[i] = (long)( ( t_ub * x ) + ( 1 << 5 ) ); + ROQ_VR_tab[i] = (long)( ( t_vr * x ) + ( 1 << 5 ) ); + ROQ_UG_tab[i] = (long)( ( -t_ug * x ) ); + ROQ_VG_tab[i] = (long)( ( -t_vg * x ) + ( 1 << 5 ) ); + ROQ_YY_tab[i] = (long)( ( i << 6 ) | ( i >> 2 ) ); + } +} + +#define VQ2TO4( a,b,c,d ) { \ + *c++ = a[0]; \ + *d++ = a[0]; \ + *d++ = a[0]; \ + *c++ = a[1]; \ + *d++ = a[1]; \ + *d++ = a[1]; \ + *c++ = b[0]; \ + *d++ = b[0]; \ + *d++ = b[0]; \ + *c++ = b[1]; \ + *d++ = b[1]; \ + *d++ = b[1]; \ + *d++ = a[0]; \ + *d++ = a[0]; \ + *d++ = a[1]; \ + *d++ = a[1]; \ + *d++ = b[0]; \ + *d++ = b[0]; \ + *d++ = b[1]; \ + *d++ = b[1]; \ + a += 2; b += 2; } + +#define VQ2TO2( a,b,c,d ) { \ + *c++ = *a; \ + *d++ = *a; \ + *d++ = *a; \ + *c++ = *b; \ + *d++ = *b; \ + *d++ = *b; \ + *d++ = *a; \ + *d++ = *a; \ + *d++ = *b; \ + *d++ = *b; \ + a++; b++; } + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static unsigned short yuv_to_rgb( long y, long u, long v ) { + long r,g,b,YY = (long)( ROQ_YY_tab[( y )] ); + + r = ( YY + ROQ_VR_tab[v] ) >> 9; + g = ( YY + ROQ_UG_tab[u] + ROQ_VG_tab[v] ) >> 8; + b = ( YY + ROQ_UB_tab[u] ) >> 9; + + if ( r < 0 ) { + r = 0; + } + if ( g < 0 ) { + g = 0; + } + if ( b < 0 ) { + b = 0; + } + if ( r > 31 ) { + r = 31; + } + if ( g > 63 ) { + g = 63; + } + if ( b > 31 ) { + b = 31; + } + + return ( unsigned short )( ( r << 11 ) + ( g << 5 ) + ( b ) ); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +#if defined( MACOS_X ) + +static inline unsigned int yuv_to_rgb24( long y, long u, long v ) { + long r,g,b,YY; + + 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 ( ( r << 24 ) | ( g << 16 ) | ( b << 8 ) ) | ( 255 ); //+(255<<24)); +} + +#else +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 ) ); +} +#endif + +/****************************************************************************** +* +* 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; + unsigned int *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 < two; i++ ) { + y0 = (long)*input++; + y1 = (long)*input++; + y2 = (long)*input++; + y3 = (long)*input++; + cr = (long)*input++; + cb = (long)*input++; + *bptr++ = yuv_to_rgb( y0, cr, cb ); + *bptr++ = yuv_to_rgb( y1, cr, cb ); + *bptr++ = yuv_to_rgb( y2, cr, cb ); + *bptr++ = yuv_to_rgb( y3, cr, cb ); + } + + cptr = (unsigned short *)vq4; + dptr = (unsigned short *)vq8; + + for ( i = 0; i < four; i++ ) { + aptr = (unsigned short *)vq2 + ( *input++ ) * 4; + bptr = (unsigned short *)vq2 + ( *input++ ) * 4; + for ( j = 0; j < 2; j++ ) + VQ2TO4( aptr,bptr,cptr,dptr ); + } + } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { + ibptr = (unsigned int *)bptr; + for ( i = 0; i < two; i++ ) { + y0 = (long)*input++; + y1 = (long)*input++; + y2 = (long)*input++; + y3 = (long)*input++; + cr = (long)*input++; + cb = (long)*input++; + *ibptr++ = yuv_to_rgb24( y0, cr, cb ); + *ibptr++ = yuv_to_rgb24( y1, cr, cb ); + *ibptr++ = yuv_to_rgb24( y2, cr, cb ); + *ibptr++ = yuv_to_rgb24( y3, cr, cb ); + } + + icptr = (unsigned int *)vq4; + idptr = (unsigned int *)vq8; + + for ( i = 0; i < four; i++ ) { + iaptr = (unsigned int *)vq2 + ( *input++ ) * 4; + ibptr = (unsigned int *)vq2 + ( *input++ ) * 4; + for ( j = 0; j < 2; j++ ) + VQ2TO4( iaptr, ibptr, icptr, idptr ); + } + } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { + bbptr = (byte *)bptr; + for ( i = 0; i < two; i++ ) { + *bbptr++ = cinTable[currentHandle].gray[*input++]; + *bbptr++ = cinTable[currentHandle].gray[*input++]; + *bbptr++ = cinTable[currentHandle].gray[*input++]; + *bbptr++ = cinTable[currentHandle].gray[*input]; input += 3; + } + + bcptr = (byte *)vq4; + bdptr = (byte *)vq8; + + for ( i = 0; i < four; i++ ) { + baptr = (byte *)vq2 + ( *input++ ) * 4; + bbptr = (byte *)vq2 + ( *input++ ) * 4; + for ( j = 0; j < 2; j++ ) + VQ2TO4( baptr,bbptr,bcptr,bdptr ); + } + } + } else { +// +// double height, smoothed +// + if ( cinTable[currentHandle].samplesPerPixel == 2 ) { + for ( i = 0; i < two; i++ ) { + y0 = (long)*input++; + y1 = (long)*input++; + y2 = (long)*input++; + y3 = (long)*input++; + cr = (long)*input++; + cb = (long)*input++; + *bptr++ = yuv_to_rgb( y0, cr, cb ); + *bptr++ = yuv_to_rgb( y1, cr, cb ); + *bptr++ = yuv_to_rgb( ( ( y0 * 3 ) + y2 ) / 4, cr, cb ); + *bptr++ = yuv_to_rgb( ( ( y1 * 3 ) + y3 ) / 4, cr, cb ); + *bptr++ = yuv_to_rgb( ( y0 + ( y2 * 3 ) ) / 4, cr, cb ); + *bptr++ = yuv_to_rgb( ( y1 + ( y3 * 3 ) ) / 4, cr, cb ); + *bptr++ = yuv_to_rgb( y2, cr, cb ); + *bptr++ = yuv_to_rgb( y3, cr, cb ); + } + + cptr = (unsigned short *)vq4; + dptr = (unsigned short *)vq8; + + for ( i = 0; i < four; i++ ) { + aptr = (unsigned short *)vq2 + ( *input++ ) * 8; + bptr = (unsigned short *)vq2 + ( *input++ ) * 8; + for ( j = 0; j < 2; j++ ) { + VQ2TO4( aptr,bptr,cptr,dptr ); + VQ2TO4( aptr,bptr,cptr,dptr ); + } + } + } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { + ibptr = (unsigned int *)bptr; + for ( i = 0; i < two; i++ ) { + y0 = (long)*input++; + y1 = (long)*input++; + y2 = (long)*input++; + y3 = (long)*input++; + cr = (long)*input++; + cb = (long)*input++; + *ibptr++ = yuv_to_rgb24( y0, cr, cb ); + *ibptr++ = yuv_to_rgb24( y1, cr, cb ); + *ibptr++ = yuv_to_rgb24( ( ( y0 * 3 ) + y2 ) / 4, cr, cb ); + *ibptr++ = yuv_to_rgb24( ( ( y1 * 3 ) + y3 ) / 4, cr, cb ); + *ibptr++ = yuv_to_rgb24( ( y0 + ( y2 * 3 ) ) / 4, cr, cb ); + *ibptr++ = yuv_to_rgb24( ( y1 + ( y3 * 3 ) ) / 4, cr, cb ); + *ibptr++ = yuv_to_rgb24( y2, cr, cb ); + *ibptr++ = yuv_to_rgb24( y3, cr, cb ); + } + + icptr = (unsigned int *)vq4; + idptr = (unsigned int *)vq8; + + for ( i = 0; i < four; i++ ) { + iaptr = (unsigned int *)vq2 + ( *input++ ) * 8; + ibptr = (unsigned int *)vq2 + ( *input++ ) * 8; + for ( j = 0; j < 2; j++ ) { + VQ2TO4( iaptr, ibptr, icptr, idptr ); + VQ2TO4( iaptr, ibptr, icptr, idptr ); + } + } + } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { + bbptr = (byte *)bptr; + for ( i = 0; i < two; i++ ) { + y0 = (long)*input++; + y1 = (long)*input++; + y2 = (long)*input++; + y3 = (long)*input; input += 3; + *bbptr++ = cinTable[currentHandle].gray[y0]; + *bbptr++ = cinTable[currentHandle].gray[y1]; + *bbptr++ = cinTable[currentHandle].gray[( ( y0 * 3 ) + y2 ) / 4]; + *bbptr++ = cinTable[currentHandle].gray[( ( y1 * 3 ) + y3 ) / 4]; + *bbptr++ = cinTable[currentHandle].gray[( y0 + ( y2 * 3 ) ) / 4]; + *bbptr++ = cinTable[currentHandle].gray[( y1 + ( y3 * 3 ) ) / 4]; + *bbptr++ = cinTable[currentHandle].gray[y2]; + *bbptr++ = cinTable[currentHandle].gray[y3]; + } + + bcptr = (byte *)vq4; + bdptr = (byte *)vq8; + + for ( i = 0; i < four; i++ ) { + baptr = (byte *)vq2 + ( *input++ ) * 8; + bbptr = (byte *)vq2 + ( *input++ ) * 8; + for ( j = 0; j < 2; j++ ) { + VQ2TO4( baptr,bbptr,bcptr,bdptr ); + VQ2TO4( baptr,bbptr,bcptr,bdptr ); + } + } + } + } + } else { +// +// 1/4 screen +// + if ( cinTable[currentHandle].samplesPerPixel == 2 ) { + for ( i = 0; i < two; i++ ) { + y0 = (long)*input; input += 2; + y2 = (long)*input; input += 2; + cr = (long)*input++; + cb = (long)*input++; + *bptr++ = yuv_to_rgb( y0, cr, cb ); + *bptr++ = yuv_to_rgb( y2, cr, cb ); + } + + cptr = (unsigned short *)vq4; + dptr = (unsigned short *)vq8; + + for ( i = 0; i < four; i++ ) { + aptr = (unsigned short *)vq2 + ( *input++ ) * 2; + bptr = (unsigned short *)vq2 + ( *input++ ) * 2; + for ( j = 0; j < 2; j++ ) { + VQ2TO2( aptr,bptr,cptr,dptr ); + } + } + } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { + bbptr = (byte *)bptr; + + for ( i = 0; i < two; i++ ) { + *bbptr++ = cinTable[currentHandle].gray[*input]; input += 2; + *bbptr++ = cinTable[currentHandle].gray[*input]; input += 4; + } + + bcptr = (byte *)vq4; + bdptr = (byte *)vq8; + + for ( i = 0; i < four; i++ ) { + baptr = (byte *)vq2 + ( *input++ ) * 2; + bbptr = (byte *)vq2 + ( *input++ ) * 2; + for ( j = 0; j < 2; j++ ) { + VQ2TO2( baptr,bbptr,bcptr,bdptr ); + } + } + } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { + ibptr = (unsigned int *) bptr; + for ( i = 0; i < two; i++ ) { + y0 = (long)*input; input += 2; + y2 = (long)*input; input += 2; + cr = (long)*input++; + cb = (long)*input++; + *ibptr++ = yuv_to_rgb24( y0, cr, cb ); + *ibptr++ = yuv_to_rgb24( y2, cr, cb ); + } + + icptr = (unsigned int *)vq4; + idptr = (unsigned int *)vq8; + + for ( i = 0; i < four; i++ ) { + iaptr = (unsigned int *)vq2 + ( *input++ ) * 2; + ibptr = (unsigned int *)vq2 + ( *input++ ) * 2; + for ( j = 0; j < 2; j++ ) { + VQ2TO2( iaptr,ibptr,icptr,idptr ); + } + } + } + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff ) { + byte *scroff; + long bigx, bigy, lowx, lowy, useY; + long offset; + + offset = cinTable[currentHandle].screenDelta; + + lowx = lowy = 0; + bigx = cinTable[currentHandle].xsize; + bigy = cinTable[currentHandle].ysize; + + if ( bigx > 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 < numQuadCels; i++ ) { + cin.qStatus[0][i] = temp; // eoq + cin.qStatus[1][i] = temp; // eoq + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void readQuadInfo( byte *qData ) { + if ( currentHandle < 0 ) { + return; + } + + cinTable[currentHandle].xsize = qData[0] + qData[1] * 256; + cinTable[currentHandle].ysize = qData[2] + qData[3] * 256; + cinTable[currentHandle].maxsize = qData[4] + qData[5] * 256; + cinTable[currentHandle].minsize = qData[6] + qData[7] * 256; + + cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize; + cinTable[currentHandle].CIN_WIDTH = cinTable[currentHandle].xsize; + + cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH * cinTable[currentHandle].samplesPerPixel; + cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT * cinTable[currentHandle].samplesPerLine; + + cinTable[currentHandle].half = qfalse; + cinTable[currentHandle].smootheddouble = qfalse; + + cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal; + cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer; + + cinTable[currentHandle].t[0] = ( 0 - (unsigned int)cin.linbuf ) + (unsigned int)cin.linbuf + cinTable[currentHandle].screenDelta; + cinTable[currentHandle].t[1] = ( 0 - ( (unsigned int)cin.linbuf + cinTable[currentHandle].screenDelta ) ) + (unsigned int)cin.linbuf; + + cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH; + cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT; + + // rage pro is very slow at 512 wide textures, voodoo can't do it at all + if ( glConfig.hardwareType == GLHW_RAGEPRO || glConfig.maxTextureSize <= 256 ) { + if ( cinTable[currentHandle].drawX > 256 ) { + cinTable[currentHandle].drawX = 256; + } + if ( cinTable[currentHandle].drawY > 256 ) { + cinTable[currentHandle].drawY = 256; + } + if ( cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256 ) { + Com_Printf( "HACK: approxmimating cinematic for Rage Pro or Voodoo\n" ); + } + } +//#ifdef __MACOS__ +// cinTable[currentHandle].drawX = 256; +// cinTable[currentHandle].drawX = 256; +//#endif +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQPrepMcomp( long xoff, long yoff ) { + long i, j, x, y, temp, temp2; + + i = cinTable[currentHandle].samplesPerLine; j = cinTable[currentHandle].samplesPerPixel; + if ( cinTable[currentHandle].xsize == ( cinTable[currentHandle].ysize * 4 ) && !cinTable[currentHandle].half ) { + j = j + j; i = i + i; + } + + for ( y = 0; y < 16; y++ ) { + temp2 = ( y + yoff - 8 ) * i; + for ( x = 0; x < 16; x++ ) { + temp = ( x + xoff - 8 ) * j; + cin.mcomp[( x * 16 ) + y] = cinTable[currentHandle].normalBuffer0 - ( temp2 + temp ); + } + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void initRoQ() { + if ( currentHandle < 0 ) { + return; + } + + cinTable[currentHandle].VQNormal = ( void( * ) ( byte *, void * ) )blitVQQuad32fs; + cinTable[currentHandle].VQBuffer = ( void( * ) ( byte *, void * ) )blitVQQuad32fs; + cinTable[currentHandle].samplesPerPixel = 4; + ROQ_GenYUVTables(); + RllSetupTable(); +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +/* +static byte* RoQFetchInterlaced( byte *source ) { + int x, *src, *dst; + + if (currentHandle < 0) return NULL; + + src = (int *)source; + dst = (int *)cinTable[currentHandle].buf2; + + for(x=0;x<256*256;x++) { + *dst = *src; + dst++; src += 2; + } + return cinTable[currentHandle].buf2; +} +*/ +static void RoQReset() { + + if ( currentHandle < 0 ) { + return; + } + + Sys_EndStreamedFile( cinTable[currentHandle].iFile ); + + // DHM - Properly close file so we don't run out of handles + FS_FCloseFile( cinTable[currentHandle].iFile ); + cinTable[currentHandle].iFile = 0; + // dhm - end + + FS_FOpenFileRead( cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue ); + // let the background thread start reading ahead + Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 ); + Sys_StreamedRead( cin.file, 16, 1, cinTable[currentHandle].iFile ); + RoQ_init(); + cinTable[currentHandle].status = FMV_LOOPED; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQInterrupt( void ) { + byte *framedata; + short sbuf[32768]; + int ssize; + + if ( currentHandle < 0 ) { + return; + } + + Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize + 8, 1, cinTable[currentHandle].iFile ); + if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { + if ( cinTable[currentHandle].holdAtEnd == qfalse ) { + if ( cinTable[currentHandle].looping ) { + RoQReset(); + } else { + cinTable[currentHandle].status = FMV_EOF; + } + } else { + cinTable[currentHandle].status = FMV_IDLE; + } + return; + } + + framedata = cin.file; +// +// new frame is ready +// +redump: + switch ( cinTable[currentHandle].roq_id ) + { + case ROQ_QUAD_VQ: + if ( ( cinTable[currentHandle].numQuads & 1 ) ) { + cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1]; + RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); + cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata ); + cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta; + } else { + cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0]; + RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); + cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata ); + cinTable[currentHandle].buf = cin.linbuf; + } + if ( cinTable[currentHandle].numQuads == 0 ) { // first frame + Com_Memcpy( cin.linbuf + cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine * cinTable[currentHandle].ysize ); + } + cinTable[currentHandle].numQuads++; + cinTable[currentHandle].dirty = qtrue; + break; + case ROQ_CODEBOOK: + decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags ); + break; + case ZA_SOUND_MONO: + if ( !cinTable[currentHandle].silent ) { + ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags ); + S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, 1.0f, 0 ); + } + break; + case ZA_SOUND_STEREO: + if ( !cinTable[currentHandle].silent ) { + if ( cinTable[currentHandle].numQuads == -1 ) { + S_Update(); + s_rawend[0] = s_soundtime; + } + ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags ); + S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, 1.0f, 0 ); + } + 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 = (char)framedata[7]; + cinTable[currentHandle].roqF1 = (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 = Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile ); + cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize + 8; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQ_init( void ) { + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds() * com_timescale->value; + + cinTable[currentHandle].RoQPlayed = 24; + +/* get frame rate */ + cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7] * 256; + + if ( !cinTable[currentHandle].roqFPS ) { + cinTable[currentHandle].roqFPS = 30; + } + + cinTable[currentHandle].numQuads = -1; + + cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9] * 256; + cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11] * 256 + cin.file[12] * 65536; + cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15] * 256; + + if ( cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize ) { + return; + } + +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQShutdown( void ) { + const char *s; + + if ( !cinTable[currentHandle].buf ) { + return; + } + + if ( cinTable[currentHandle].status == FMV_IDLE ) { + return; + } + Com_DPrintf( "finished cinematic\n" ); + cinTable[currentHandle].status = FMV_IDLE; + + if ( cinTable[currentHandle].iFile ) { + Sys_EndStreamedFile( 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 ) ); + /*if( com_logosPlaying->integer && !Q_stricmp( s, "cinematic avlogo.roq" ) ) { + Cvar_Set( "nextmap", "cinematic sdlogo.roq" ); // FIXME: sd logo + Cvar_Set( "com_logosPlaying", "0" ); + } else {*/ + Cvar_Set( "nextmap", "" ); + //} + } + CL_handle = -1; + } + cinTable[currentHandle].fileName[0] = 0; + currentHandle = -1; +} + +/* +================== +SCR_StopCinematic +================== +*/ +e_status CIN_StopCinematic( int handle ) { + + if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { + return FMV_EOF; + } + currentHandle = handle; + + Com_DPrintf( "trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName ); + + if ( !cinTable[currentHandle].buf ) { + return FMV_EOF; + } + + if ( cinTable[currentHandle].alterGameState ) { + if ( cls.state != CA_CINEMATIC ) { + return cinTable[currentHandle].status; + } + } + cinTable[currentHandle].status = FMV_EOF; + RoQShutdown(); + + return FMV_EOF; +} + +/* +================== +SCR_RunCinematic + +Fetch and decompress the pending frame +================== +*/ + + +e_status CIN_RunCinematic( int handle ) { + // bk001204 - init + int start = 0; + int thisTime = 0; + + if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { + return FMV_EOF; + } + + if ( cin.currentHandle != handle ) { + currentHandle = handle; + cin.currentHandle = currentHandle; + cinTable[currentHandle].status = FMV_EOF; + RoQReset(); + } + + if ( cinTable[handle].playonwalls < -1 ) { + return cinTable[handle].status; + } + + currentHandle = handle; + + if ( cinTable[currentHandle].alterGameState ) { + if ( cls.state != CA_CINEMATIC ) { + return cinTable[currentHandle].status; + } + } + + if ( cinTable[currentHandle].status == FMV_IDLE ) { + return cinTable[currentHandle].status; + } + + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + thisTime = CL_ScaledMilliseconds() * com_timescale->value; + if ( cinTable[currentHandle].shader && ( abs( thisTime - cinTable[currentHandle].lastTime ) ) > 100 ) { + cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; + } + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].tfps = ( ( ( ( CL_ScaledMilliseconds() * com_timescale->value ) - cinTable[currentHandle].startTime ) * 3 ) / 100 ); + + start = cinTable[currentHandle].startTime; + while ( ( cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads ) + && ( cinTable[currentHandle].status == FMV_PLAY ) ) + { + RoQInterrupt(); + if ( start != cinTable[currentHandle].startTime ) { + // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer + cinTable[currentHandle].tfps = ( ( ( ( CL_ScaledMilliseconds() * com_timescale->value ) + - cinTable[currentHandle].startTime ) * 3 ) / 100 ); + start = cinTable[currentHandle].startTime; + } + } + + cinTable[currentHandle].lastTime = thisTime; + + if ( cinTable[currentHandle].status == FMV_LOOPED ) { + cinTable[currentHandle].status = FMV_PLAY; + } + + if ( cinTable[currentHandle].status == FMV_EOF ) { + if ( cinTable[currentHandle].looping ) { + RoQReset(); + } else { + RoQShutdown(); + } + } + + return cinTable[currentHandle].status; +} + +/* +================== +CL_PlayCinematic + +================== +*/ +int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) { + unsigned short RoQID; + char name[MAX_OSPATH]; + int i; + + if ( strstr( arg, "/" ) == NULL && strstr( arg, "\\" ) == NULL ) { + Com_sprintf( name, sizeof( name ), "video/%s", arg ); + } else { + Com_sprintf( name, sizeof( name ), "%s", arg ); + } + + if ( !( systemBits & CIN_system ) ) { + for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { + if ( !strcmp( cinTable[i].fileName, name ) ) { + return i; + } + } + } + + Com_DPrintf( "SCR_PlayCinematic( %s )\n", arg ); + + Com_Memset( &cin, 0, sizeof( cinematics_t ) ); + currentHandle = CIN_HandleForVideo(); + + cin.currentHandle = currentHandle; + + strcpy( cinTable[currentHandle].fileName, name ); + + cinTable[currentHandle].ROQSize = 0; + cinTable[currentHandle].ROQSize = FS_FOpenFileRead( cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue ); + + if ( cinTable[currentHandle].ROQSize <= 0 ) { + Com_DPrintf( "play(%s), ROQSize<=0\n", arg ); + cinTable[currentHandle].fileName[0] = 0; + return -1; + } + + CIN_SetExtents( currentHandle, x, y, w, h ); + CIN_SetLooping( currentHandle, ( systemBits & CIN_loop ) != 0 ); + + cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; + cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; + cinTable[currentHandle].holdAtEnd = ( systemBits & CIN_hold ) != 0; + cinTable[currentHandle].alterGameState = ( systemBits & CIN_system ) != 0; + cinTable[currentHandle].playonwalls = 1; + cinTable[currentHandle].silent = ( systemBits & CIN_silent ) != 0; + cinTable[currentHandle].shader = ( systemBits & CIN_shader ) != 0; + + if ( cinTable[currentHandle].alterGameState ) { + // close the menu + if ( uivm ) { + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); + } + } else { + cinTable[currentHandle].playonwalls = cl_inGameVideo->integer; + } + + initRoQ(); + + FS_Read( cin.file, 16, cinTable[currentHandle].iFile ); + + RoQID = ( unsigned short )( cin.file[0] ) + ( unsigned short )( cin.file[1] ) * 256; + if ( RoQID == 0x1084 ) { + RoQ_init(); +// FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile); + // let the background thread start reading ahead + Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 ); + + 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; + s_rawend[0] = s_soundtime; + + return currentHandle; + } + Com_DPrintf( "trFMV::play(), invalid RoQ ID\n" ); + + RoQShutdown(); + return -1; +} + +void CIN_SetExtents( int handle, int x, int y, int w, int h ) { + if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { + return; + } + cinTable[handle].xpos = x; + cinTable[handle].ypos = y; + cinTable[handle].width = w; + cinTable[handle].height = h; + cinTable[handle].dirty = qtrue; +} + +void CIN_SetLooping( int handle, qboolean loop ) { + if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { + return; + } + cinTable[handle].looping = loop; +} + +/* +================== +SCR_DrawCinematic + +================== +*/ +void CIN_DrawCinematic( int handle ) { + float x, y, w, h; + byte *buf; + + if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { + return; + } + + if ( !cinTable[handle].buf ) { + return; + } + + x = cinTable[handle].xpos; + y = cinTable[handle].ypos; + w = cinTable[handle].width; + h = cinTable[handle].height; + buf = cinTable[handle].buf; + SCR_AdjustFrom640( &x, &y, &w, &h ); + + if ( cinTable[handle].dirty && ( cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY ) ) { + int ix, iy, *buf2, *buf3, xm, ym, ll; + + xm = cinTable[handle].CIN_WIDTH / 256; + ym = cinTable[handle].CIN_HEIGHT / 256; + ll = 8; + if ( cinTable[handle].CIN_WIDTH == 512 ) { + ll = 9; + } + + buf3 = (int*)buf; + buf2 = Hunk_AllocateTempMemory( 256 * 256 * 4 ); + if ( xm == 2 && ym == 2 ) { + byte *bc2, *bc3; + int ic, iiy; + + bc2 = (byte *)buf2; + bc3 = (byte *)buf3; + for ( iy = 0; iy < 256; iy++ ) { + iiy = iy << 12; + for ( ix = 0; ix < 2048; ix += 8 ) { + for ( ic = ix; ic < ( ix + 4 ); ic++ ) { + *bc2 = ( bc3[iiy + ic] + bc3[iiy + 4 + ic] + bc3[iiy + 2048 + ic] + bc3[iiy + 2048 + 4 + ic] ) >> 2; + bc2++; + } + } + } + } else if ( xm == 2 && ym == 1 ) { + byte *bc2, *bc3; + int ic, iiy; + + bc2 = (byte *)buf2; + bc3 = (byte *)buf3; + for ( iy = 0; iy < 256; iy++ ) { + iiy = iy << 11; + for ( ix = 0; ix < 2048; ix += 8 ) { + for ( ic = ix; ic < ( ix + 4 ); ic++ ) { + *bc2 = ( bc3[iiy + ic] + bc3[iiy + 4 + ic] ) >> 1; + bc2++; + } + } + } + } else { + for ( iy = 0; iy < 256; iy++ ) { + for ( ix = 0; ix < 256; ix++ ) { + buf2[( iy << 8 ) + ix] = buf3[( ( iy * ym ) << ll ) + ( ix * xm )]; + } + } + } + re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue ); + cinTable[handle].dirty = qfalse; + Hunk_FreeTempMemory( buf2 ); + return; + } + + re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty ); + cinTable[handle].dirty = qfalse; +} + +void CL_PlayCinematic_f( void ) { + char *arg, *s; + qboolean holdatend; + int bits = CIN_system; + + // Arnout: don't allow this while on server + if ( cls.state > 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--; + } + } +} diff --git a/src/client/cl_console.c b/src/client/cl_console.c new file mode 100644 index 0000000..49b9f32 --- /dev/null +++ b/src/client/cl_console.c @@ -0,0 +1,831 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// 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; + +// DHM - Nerve :: Must hold CTRL + SHIFT + ~ to get console +cvar_t *con_restricted; + +#define DEFAULT_CONSOLE_WIDTH 78 + +vec4_t console_color = {1.0, 1.0, 1.0, 1.0}; +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; +} + + +/* +================ +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_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; + MAC_STATIC short tbuf[CON_TEXTSIZE]; + + // ydnar: wasn't allowing for larger consoles + // width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2; + width = ( cls.glconfig.vidWidth / SMALLCHAR_WIDTH ) - 2; + + if ( width == con.linewidth ) { + return; + } + + if ( width < 1 ) { // video hasn't been initialized yet + width = DEFAULT_CONSOLE_WIDTH; + con.linewidth = width; + con.totallines = CON_TEXTSIZE / con.linewidth; + for ( i = 0; i < CON_TEXTSIZE; i++ ) + + con.text[i] = ( ColorIndex( COLNSOLE_COLOR ) << 8 ) | ' '; + } else + { + oldwidth = con.linewidth; + con.linewidth = width; + oldtotallines = con.totallines; + con.totallines = CON_TEXTSIZE / con.linewidth; + numlines = oldtotallines; + + if ( con.totallines < numlines ) { + numlines = con.totallines; + } + + numchars = oldwidth; + + if ( con.linewidth < numchars ) { + numchars = con.linewidth; + } + + memcpy( tbuf, con.text, CON_TEXTSIZE * sizeof( short ) ); + for ( i = 0; i < CON_TEXTSIZE; i++ ) + + con.text[i] = ( ColorIndex( COLNSOLE_COLOR ) << 8 ) | ' '; + + + for ( i = 0 ; i < numlines ; i++ ) + { + for ( j = 0 ; j < numchars ; j++ ) + { + con.text[( con.totallines - 1 - i ) * con.linewidth + j] = + tbuf[( ( con.current - i + oldtotallines ) % + oldtotallines ) * oldwidth + j]; + } + } + + Con_ClearNotify(); + } + + con.current = con.totallines - 1; + con.display = con.current; +} + + +/* +================ +Con_Init +================ +*/ +void Con_Init( void ) { + int i; + + con_notifytime = Cvar_Get( "con_notifytime", "7", 0 ); // JPW NERVE increased per id req for obits + con_conspeed = Cvar_Get( "scr_conspeed", "3", 0 ); + con_debug = Cvar_Get( "con_debug", "0", CVAR_ARCHIVE ); //----(SA) added + con_autoclear = Cvar_Get( "con_autoclear", "1", CVAR_ARCHIVE ); + con_restricted = Cvar_Get( "con_restricted", "0", CVAR_INIT ); // DHM - Nerve + + Field_Clear( &g_consoleField ); + g_consoleField.widthInChars = g_console_field_width; + for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) { + Field_Clear( &historyEditLines[i] ); + historyEditLines[i].widthInChars = g_console_field_width; + } + + Cmd_AddCommand( "toggleConsole", Con_ToggleConsole_f ); + Cmd_AddCommand( "clear", Con_Clear_f ); + Cmd_AddCommand( "condump", Con_Dump_f ); + + // ydnar: these are deprecated in favor of cgame/ui based version + Cmd_AddCommand( "clMessageMode", Con_MessageMode_f ); + Cmd_AddCommand( "clMessageMode2", Con_MessageMode2_f ); + Cmd_AddCommand( "clMessageMode3", Con_MessageMode3_f ); +} + + +/* +=============== +Con_Linefeed +=============== +*/ +void Con_Linefeed( qboolean skipnotify ) { + int i; + + // mark time for transparent overlay + if ( con.current >= 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; + } + + 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] <= ' ' ) { + 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; + + if ( cls.state != CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_CONSOLE ) ) { + return; + } + + y = con.vislines - ( SMALLCHAR_HEIGHT * 2 ); + + // hightlight the current autocompleted part + if ( con.acLength ) { + Cmd_TokenizeString( g_consoleField.buffer ); + + if ( strlen( Cmd_Argv( 0 ) ) - con.acLength > 0 ) { + re.SetColor( console_highlightcolor ); + re.DrawStretchPic( con.xadjust + ( 2 + con.acLength ) * SMALLCHAR_WIDTH, + y + 2, + ( strlen( Cmd_Argv( 0 ) ) - con.acLength ) * SMALLCHAR_WIDTH, + SMALLCHAR_HEIGHT - 2, 0, 0, 0, 0, cls.whiteShader ); + } + } + + re.SetColor( con.color ); + + SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' ); + + Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y, + SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue ); +} + + +/* +================ +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 ); + skip = strlen( buf ) + 2; + } else if ( chat_buddy ) { + char buf[128]; + CL_TranslateString( "say_fireteam:", buf ); + SCR_DrawBigString( 8, v, buf, 1.0f ); + skip = strlen( buf ) + 2; + } else { + char buf[128]; + CL_TranslateString( "say:", buf ); + SCR_DrawBigString( 8, v, buf, 1.0f ); + skip = strlen( buf ) + 1; + } + + Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v, + SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, 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; + int currentColor; + vec4_t color; + + lines = cls.glconfig.vidHeight * frac; + if ( lines <= 0 ) { + return; + } + + if ( lines > cls.glconfig.vidHeight ) { + lines = cls.glconfig.vidHeight; + } + + // on wide screens, we will center the text + con.xadjust = 0; + SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL ); + + // draw the background + y = frac * SCREEN_HEIGHT - 2; + if ( y < 1 ) { + y = 0; + } else { + SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader ); + + // NERVE - SMF - merged from WolfSP + if ( frac >= 0.5f ) { + color[0] = color[1] = color[2] = frac * 2.0f; + color[3] = 1.0f; + re.SetColor( color ); + + // draw the logo + SCR_DrawPic( 192, 70, 256, 128, cls.consoleShader2 ); + re.SetColor( NULL ); + } + // -NERVE - SMF + } + + // ydnar: matching light text + color[0] = 0.75; + color[1] = 0.75; + color[2] = 0.75; + color[3] = 1.0f; + if ( frac < 1.0 ) { + SCR_FillRect( 0, y, SCREEN_WIDTH, 1.25, color ); + } + + + // draw the version number + + re.SetColor( g_color_table[ColorIndex( COLNSOLE_COLOR )] ); + + i = strlen( Q3_VERSION ); + + for ( x = 0 ; x < i ; x++ ) { + + SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH, + + ( lines - ( SMALLCHAR_HEIGHT + SMALLCHAR_HEIGHT / 2 ) ), Q3_VERSION[x] ); + + } + + + // draw the text + con.vislines = lines; + rows = ( lines - SMALLCHAR_WIDTH ) / SMALLCHAR_WIDTH; // rows of text to draw + + y = lines - ( SMALLCHAR_HEIGHT * 3 ); + + // draw from the bottom up + if ( con.display != con.current ) { + // draw arrows to show the buffer is backscrolled + re.SetColor( g_color_table[ColorIndex( COLOR_WHITE )] ); + for ( x = 0 ; x < con.linewidth ; x += 4 ) + SCR_DrawSmallChar( con.xadjust + ( x + 1 ) * SMALLCHAR_WIDTH, y, '^' ); + y -= SMALLCHAR_HEIGHT; + rows--; + } + + row = con.display; + + if ( con.x == 0 ) { + row--; + } + + currentColor = 7; + re.SetColor( g_color_table[currentColor] ); + + for ( i = 0 ; i < rows ; i++, y -= SMALLCHAR_HEIGHT, row-- ) + { + 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 < 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( con.xadjust + ( x + 1 ) * SMALLCHAR_WIDTH, y, text[x] & 0xff ); + } + } + + // draw the input prompt, user text, and cursor if desired + Con_DrawInput(); + + re.SetColor( NULL ); +} + +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 + // ydnar: added short console support (via shift+~) + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { + con.finalFrac = con.desiredFrac; + } 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/client/cl_input.c b/src/client/cl_input.c new file mode 100644 index 0000000..c7524d7 --- /dev/null +++ b/src/client/cl_input.c @@ -0,0 +1,1080 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// 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] );} + +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_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]);} + +// 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_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 * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE]; + } else { + cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] ); + } +#endif + if ( kb[KB_MLOOK].active ) { + cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD]; + } else { + cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] ); + } + + cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] ); +} + +/* +================= +CL_MouseMove +================= +*/ +void CL_MouseMove( usercmd_t *cmd ) { + float mx, my; + float accelSensitivity; + float rate; + + // allow mouse smoothing + if ( m_filter->integer ) { + mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5; + my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5; + } else { + mx = cl.mouseDx[cl.mouseIndex]; + my = cl.mouseDy[cl.mouseIndex]; + } + cl.mouseIndex ^= 1; + cl.mouseDx[cl.mouseIndex] = 0; + cl.mouseDy[cl.mouseIndex] = 0; + + rate = sqrt( mx * mx + my * my ) / (float)frame_msec; + accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value; + + // scale by FOV + accelSensitivity *= cl.cgameSensitivity; + +/* 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 ( rate && cl_showMouseRate->integer ) { + Com_Printf( "%f : %f\n", rate, accelSensitivity ); + } + +// 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 *= accelSensitivity; + my *= accelSensitivity; + } + + if ( !mx && !my ) { + return; + } + + // 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 < 7 ; 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 < 8 ; 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 + 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 > 100 ) { + Cvar_Set( "cl_maxpackets", "100" ); + } + 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" ); + } + 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); // this is an exploit nowadays + + 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( "+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); + + // 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( "+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 ); + + 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/client/cl_keys.c b/src/client/cl_keys.c new file mode 100644 index 0000000..b955033 --- /dev/null +++ b/src/client/cl_keys.c @@ -0,0 +1,2040 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "client.h" + +/* + +key up events are sent even if in console mode + +*/ + +field_t historyEditLines[COMMAND_HISTORY]; + +int nextHistoryLine; // the last line in the history buffer, not masked +int historyLine; // the line being displayed from history buffer + // will be <= nextHistoryLine + +field_t g_consoleField; +field_t chatField; +qboolean chat_team; +qboolean chat_buddy; + + + +qboolean key_overstrikeMode; + +qboolean 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}, + + {"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}, + + {"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 + + {"COMMAND", K_COMMAND}, //mac + + {NULL,0} +}; + +keyname_t keynames_d[] = //deutsch +{ + {"TAB", K_TAB}, + {"EINGABETASTE", K_ENTER}, + {"ESC", K_ESCAPE}, + {"LEERTASTE", K_SPACE}, + {"RÜCKTASTE", K_BACKSPACE}, + {"PFEILT.AUF", K_UPARROW}, + {"PFEILT.UNTEN", K_DOWNARROW}, + {"PFEILT.LINKS", K_LEFTARROW}, + {"PFEILT.RECHTS", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"STRG", K_CTRL}, + {"UMSCHALLT", K_SHIFT}, + + {"FESTSTELLT", 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}, + + {"EINFG", K_INS}, + {"ENTF", K_DEL}, + {"BILD-AB", K_PGDN}, + {"BILD-AUF", K_PGUP}, + {"POS1", K_HOME}, + {"ENDE", K_END}, + + {"MAUS1", K_MOUSE1}, + {"MAUS2", K_MOUSE2}, + {"MAUS3", K_MOUSE3}, + {"MAUS4", K_MOUSE4}, + {"MAUS5", K_MOUSE5}, + + {"MRADOBEN", K_MWHEELUP }, + {"MRADUNTEN", 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}, + + {"ZB_POS1", K_KP_HOME }, + {"ZB_PFEILT.AUF", K_KP_UPARROW }, + {"ZB_BILD-AUF", K_KP_PGUP }, + {"ZB_PFEILT.LINKS", K_KP_LEFTARROW }, + {"ZB_5", K_KP_5 }, + {"ZB_PFEILT.RECHTS",K_KP_RIGHTARROW }, + {"ZB_ENDE", K_KP_END }, + {"ZB_PFEILT.UNTEN", K_KP_DOWNARROW }, + {"ZB_BILD-AB", K_KP_PGDN }, + {"ZB_ENTER", K_KP_ENTER }, + {"ZB_EINFG", K_KP_INS }, + {"ZB_ENTF", K_KP_DEL }, + {"ZB_SLASH", K_KP_SLASH }, + {"ZB_MINUS", K_KP_MINUS }, + {"ZB_PLUS", K_KP_PLUS }, + {"ZB_NUM", K_KP_NUMLOCK }, + {"ZB_*", K_KP_STAR }, + {"ZB_EQUALS", K_KP_EQUALS }, + + {"PAUSE", K_PAUSE}, + + {"COMMAND", K_COMMAND}, //mac + {NULL,0} +}; //end german + +keyname_t keynames_f[] = //french +{ + {"TAB", K_TAB}, + {"ENTREE", K_ENTER}, + {"ECHAP", K_ESCAPE}, + {"ESPACE", K_SPACE}, + {"RETOUR", K_BACKSPACE}, + {"HAUT", K_UPARROW}, + {"BAS", K_DOWNARROW}, + {"GAUCHE", K_LEFTARROW}, + {"DROITE", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"MAJ", K_SHIFT}, + + {"VERRMAJ", 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}, + + {"INSER", K_INS}, + {"SUPPR", K_DEL}, + {"PGBAS", K_PGDN}, + {"PGHAUT", K_PGUP}, + {"ORIGINE", K_HOME}, + {"FIN", K_END}, + + {"SOURIS1", K_MOUSE1}, + {"SOURIS2", K_MOUSE2}, + {"SOURIS3", K_MOUSE3}, + {"SOURIS4", K_MOUSE4}, + {"SOURIS5", K_MOUSE5}, + + {"MOLETTEHT.", K_MWHEELUP }, + {"MOLETTEBAS", 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}, + + {"PN_ORIGINE", K_KP_HOME }, + {"PN_HAUT", K_KP_UPARROW }, + {"PN_PGBAS", K_KP_PGUP }, + {"PN_GAUCHE", K_KP_LEFTARROW }, + {"PN_5", K_KP_5 }, + {"PN_DROITE", K_KP_RIGHTARROW }, + {"PN_FIN", K_KP_END }, + {"PN_BAS", K_KP_DOWNARROW }, + {"PN_PGBAS", K_KP_PGDN }, + {"PN_ENTR", K_KP_ENTER }, + {"PN_INSER", K_KP_INS }, + {"PN_SUPPR", K_KP_DEL }, + {"PN_SLASH", K_KP_SLASH }, + {"PN_MOINS", K_KP_MINUS }, + {"PN_PLUS", K_KP_PLUS }, + {"PN_VERRNUM", K_KP_NUMLOCK }, + {"PN_*", K_KP_STAR }, + {"PN_EQUALS", K_KP_EQUALS }, + + {"PAUSE", K_PAUSE}, + + {"COMMAND", K_COMMAND}, //mac + + {NULL,0} +}; //end french + +keyname_t keynames_s[] = //Spanish +{ + {"TABULADOR", K_TAB}, + {"INTRO", K_ENTER}, + {"ESC", K_ESCAPE}, + {"BARRA_ESPACIAD", K_SPACE}, + {"RETROCESO", K_BACKSPACE}, + {"CURSOR_ARRIBA", K_UPARROW}, + {"CURSOR_ABAJO", K_DOWNARROW}, + {"CURSOR_IZQDA", K_LEFTARROW}, + {"CURSOR_DERECHA", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"MAYÚS", K_SHIFT}, + + {"BLOQ_MAYÚS", 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}, + + {"INSERT", K_INS}, + {"SUPR", K_DEL}, + {"AV_PÁG", K_PGDN}, + {"RE_PÁG", K_PGUP}, + {"INICIO", K_HOME}, + {"FIN", K_END}, + + {"RATÓN1", K_MOUSE1}, + {"RATÓN2", K_MOUSE2}, + {"RATÓN3", K_MOUSE3}, + {"RATÓN4", K_MOUSE4}, + {"RATÓN5", K_MOUSE5}, + + {"RUEDA_HACIA_ARRIBA", K_MWHEELUP }, + {"RUEDA_HACIA_ABAJO", 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}, + + {"INICIO(NUM)", K_KP_HOME }, + {"ARRIBA(NUM)", K_KP_UPARROW }, + {"RE_PÁG(NUM)", K_KP_PGUP }, + {"IZQUIERDA(NUM)", K_KP_LEFTARROW }, + {"5(NUM)", K_KP_5 }, + {"DERECHA(NUM)", K_KP_RIGHTARROW }, + {"FIN(NUM)", K_KP_END }, + {"ABAJO(NUM)", K_KP_DOWNARROW }, + {"AV_PÁG(NUM)", K_KP_PGDN }, + {"INTRO(NUM)", K_KP_ENTER }, + {"INS(NUM)", K_KP_INS }, + {"SUPR(NUM)", K_KP_DEL }, + {"/(NUM)", K_KP_SLASH }, + {"-(NUM)", K_KP_MINUS }, + {"+(NUM)", K_KP_PLUS }, + {"BLOQ_NUM", K_KP_NUMLOCK }, + {"*(NUM)", K_KP_STAR }, + {"INTRO(NUM)", K_KP_EQUALS }, + + {"PAUSA", K_PAUSE}, + + {"PUNTO_Y_COMA", ';'}, // because a raw semicolon seperates commands + + {"COMANDO", K_COMMAND}, //mac + + {NULL,0} +}; + +keyname_t keynames_i[] = //Italian +{ + {"TAB", K_TAB}, + {"INVIO", K_ENTER}, + {"ESC", K_ESCAPE}, + {"SPAZIO", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + {"FRECCIASU", K_UPARROW}, + {"FRECCIAGIÙ", K_DOWNARROW}, + {"FRECCIASX", K_LEFTARROW}, + {"FRECCIADX", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"MAIUSC", K_SHIFT}, + + {"BLOCMAIUSC", 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}, + + {"INS", K_INS}, + {"CANC", K_DEL}, + {"PAGGIÙ", K_PGDN}, + {"PAGGSU", K_PGUP}, + {"HOME", K_HOME}, + {"FINE", K_END}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + {"MOUSE4", K_MOUSE4}, + {"MOUSE5", K_MOUSE5}, + + {"ROTELLASU", K_MWHEELUP }, + {"ROTELLAGIÙ", 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}, + + {"TN_HOME", K_KP_HOME }, + {"TN_FRECCIASU", K_KP_UPARROW }, + {"TN_PAGGSU", K_KP_PGUP }, + {"TN_FRECCIASX", K_KP_LEFTARROW }, + {"TN_5", K_KP_5 }, + {"TN_FRECCIA_DX", K_KP_RIGHTARROW }, + {"TN_FINE", K_KP_END }, + {"TN_FRECCIAGIÙ", K_KP_DOWNARROW }, + {"TN_PAGGIÙ", K_KP_PGDN }, + {"TN_INVIO", K_KP_ENTER }, + {"TN_INS", K_KP_INS }, + {"TN_CANC", K_KP_DEL }, + {"TN_/", K_KP_SLASH }, + {"TN_-", K_KP_MINUS }, + {"TN_+", K_KP_PLUS }, + {"TN_BLOCNUM", K_KP_NUMLOCK }, + {"TN_*", K_KP_STAR }, + {"TN_=", K_KP_EQUALS }, + + {"PAUSA", K_PAUSE}, + + {"ò", ';'}, // because a raw semicolon seperates commands + + {"COMMAND", K_COMMAND}, //mac + + {NULL,0} +}; + +/* +============================================================================= + +EDIT FIELDS + +============================================================================= +*/ + + +/* +=================== +Field_Draw + +Handles horizontal scrolling and cursor blinking +x, y, amd width are in pixels +=================== +*/ +void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor ) { + int len; + int drawLen; + int prestep; + int cursorChar; + char str[MAX_STRING_CHARS]; + int i; + + drawLen = edit->widthInChars; + len = strlen( edit->buffer ) + 1; + + // 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 ( edit->cursor < len - drawLen ) { + prestep = edit->cursor; // cursor at start + } else { + prestep = len - drawLen; + } +*/ + } + + 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" ); + } + + memcpy( str, edit->buffer + prestep, drawLen ); + str[ drawLen ] = 0; + + // draw it + if ( size == SMALLCHAR_WIDTH ) { + float color[4]; + + color[0] = color[1] = color[2] = color[3] = 1.0; + SCR_DrawSmallStringExt( x, y, str, color, qfalse ); + } else { + // draw big string with drop shadow + SCR_DrawBigString( x, y, str, 1.0 ); + } + + // draw the cursor + if ( !showCursor ) { + return; + } + + if ( (int)( cls.realtime >> 8 ) & 1 ) { + return; // off blink + } + + if ( key_overstrikeMode ) { + cursorChar = 11; + } else { + cursorChar = 10; + } + + i = drawLen - ( Q_PrintStrlen( str ) + 1 ); + + if ( size == SMALLCHAR_WIDTH ) { + SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar ); + } else { + str[0] = cursorChar; + str[1] = 0; + SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0 ); + + } +} + +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ) { + Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor ); +} + +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ) { + Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor ); +} + +/* +================ +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 ); + + if ( key == K_DEL || key == K_KP_DEL ) { + if ( edit->cursor < len ) { + memmove( edit->buffer + edit->cursor, + edit->buffer + edit->cursor + 1, len - edit->cursor ); + } + return; + } + + if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) { + if ( edit->cursor < len ) { + edit->cursor++; + } + + if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) { + edit->scroll++; + } + return; + } + + if ( key == K_LEFTARROW || key == K_KP_LEFTARROW ) { + if ( edit->cursor > 0 ) { + edit->cursor--; + } + if ( edit->cursor < edit->scroll ) { + edit->scroll--; + } + return; + } + + if ( key == K_HOME || key == K_KP_HOME || ( tolower( key ) == 'a' && keys[K_CTRL].down ) ) { + edit->cursor = 0; + return; + } + + if ( key == K_END || key == K_KP_END || ( tolower( key ) == 'e' && keys[K_CTRL].down ) ) { + edit->cursor = len; + return; + } + + if ( key == K_INS || key == K_KP_INS ) { + key_overstrikeMode = !key_overstrikeMode; + return; + } +} + +/* +================== +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; + field_t temp; + + edit = &g_consoleField; + + if ( !con.acLength ) { + // only look at the first token for completion purposes + Cmd_TokenizeString( edit->buffer ); + + Q_strncpyz( completionString, Cmd_Argv( 0 ), sizeof( completionString ) ); + if ( completionString[0] == '\\' || completionString[0] == '/' ) { + // rain - in strcpy, src and dest cannot overlap + //Q_strncpyz( completionString, completionString+1, sizeof(completionString) ); + memmove( completionString, completionString + 1, sizeof( completionString ) - 1 ); + } + + matchCount = 0; + matchIndex = 0; + currentMatch[0] = 0; + + if ( strlen( completionString ) == 0 ) { + return; + } + + Cmd_CommandCompletion( FindMatches ); + Cvar_CommandCompletion( FindMatches ); + + if ( matchCount == 0 ) { + return; // no matches + } + + Com_Memcpy( &temp, edit, sizeof( field_t ) ); + + if ( matchCount == 1 ) { + Com_sprintf( edit->buffer, sizeof( edit->buffer ), "\\%s", currentMatch ); + if ( Cmd_Argc() == 1 ) { + Q_strcat( g_consoleField.buffer, sizeof( g_consoleField.buffer ), " " ); + } else { + ConcatRemaining( temp.buffer, completionString ); + } + edit->cursor = strlen( edit->buffer ); + return; + } + + // multiple matches, complete to shortest + Com_sprintf( edit->buffer, sizeof( edit->buffer ), "\\%s", currentMatch ); + con.acLength = edit->cursor = strlen( edit->buffer ); + ConcatRemaining( temp.buffer, completionString ); + + Com_Printf( "]%s\n", edit->buffer ); + + // run through again, printing matches + Cmd_CommandCompletion( PrintMatches ); + Cvar_CommandCompletion( PrintCvarMatches ); + } else { + if ( matchCount != 1 ) { + // get the next match and show instead + char lastMatch[MAX_TOKEN_CHARS]; + + Q_strncpyz( lastMatch, currentMatch, sizeof( lastMatch ) ); + + matchIndex++; + if ( matchIndex == matchCount ) { + matchIndex = 0; + } + findMatchIndex = 0; + Cmd_CommandCompletion( FindIndexMatch ); + Cvar_CommandCompletion( FindIndexMatch ); + + Com_Memcpy( &temp, edit, sizeof( field_t ) ); + + // and print it + Com_sprintf( edit->buffer, sizeof( edit->buffer ), "\\%s", currentMatch ); + edit->cursor = strlen( edit->buffer ); + ConcatRemaining( temp.buffer, lastMatch ); + } + } +} + + +/* +==================== +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 prepent 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 + historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField; + nextHistoryLine++; + historyLine = nextHistoryLine; + + Field_Clear( &g_consoleField ); + + g_consoleField.widthInChars = g_console_field_width; + + if ( cls.state == CA_DISCONNECTED ) { + SCR_UpdateScreen(); // force an update, because the command + } // may take some time + 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 ) ) { + if ( nextHistoryLine - historyLine < COMMAND_HISTORY + && historyLine > 0 ) { + historyLine--; + } + g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; + 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 ) ) { + if ( historyLine == nextHistoryLine ) { + return; + } + historyLine++; + g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ]; + 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 == -1 ) { + 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] ) { + // Always lowercase + Q_strlwr( str ); + return str[0]; + } + + // check for hex code + if ( str[0] == '0' && str[1] == 'x' && strlen( str ) == 4 ) { + int n1, n2; + + n1 = str[2]; + if ( n1 >= '0' && n1 <= '9' ) { + n1 -= '0'; + } else if ( n1 >= 'a' && n1 <= 'f' ) { + n1 = n1 - 'a' + 10; + } else { + n1 = 0; + } + + n2 = str[3]; + if ( n2 >= '0' && n2 <= '9' ) { + n2 -= '0'; + } else if ( n2 >= 'a' && n2 <= 'f' ) { + n2 = n2 - 'a' + 10; + } else { + n2 = 0; + } + + return n1 * 16 + n2; + } + + // 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, qboolean bTranslate ) { + keyname_t *kn; + static char tinystr[5]; + int i, j; + + if ( keynum == -1 ) { + return ""; + } + + if ( keynum < 0 || keynum > 255 ) { + return ""; + } + + // check for printable ascii (don't use quote) + if ( keynum > 32 && keynum < 127 && keynum != '"' ) { + tinystr[0] = keynum; + tinystr[1] = 0; + if ( keynum == ';' && !bTranslate ) { + //fall through and use keyname table + } else { + return tinystr; + } + } + + + kn = keynames; //init to english +#ifndef __MACOS__ //DAJ USA + if ( bTranslate ) { + if ( cl_language->integer - 1 == LANGUAGE_FRENCH ) { + kn = keynames_f; //use french + } else if ( cl_language->integer - 1 == LANGUAGE_GERMAN ) { + kn = keynames_d; //use german + } else if ( cl_language->integer - 1 == LANGUAGE_ITALIAN ) { + kn = keynames_i; //use italian + } else if ( cl_language->integer - 1 == LANGUAGE_SPANISH ) { + kn = keynames_s; //use spanish + } + } +#endif + + // check for a key string + for ( ; 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 == -1 ) { + 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 == -1 ) { + 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 < 256 ; 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 < 256 ; 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 < 256 ; i++ ) { + if ( keys[i].binding && keys[i].binding[0] ) { + FS_Printf( f, "bind %s \"%s\"\n", Key_KeynumToString( i, qfalse ), keys[i].binding ); + + } + + } +} + + +/* +============ +Key_Bindlist_f + +============ +*/ +void Key_Bindlist_f( void ) { + int i; + + for ( i = 0 ; i < 256 ; i++ ) { + if ( keys[i].binding && keys[i].binding[0] ) { + Com_Printf( "%s \"%s\"\n", Key_KeynumToString( i, qfalse ), keys[i].binding ); + } + } +} + + +/* +=================== +CL_InitKeyCommands +=================== +*/ +void CL_InitKeyCommands( void ) { + // register our functions + Cmd_AddCommand( "bind",Key_Bind_f ); + Cmd_AddCommand( "unbind",Key_Unbind_f ); + 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; + } + } + +#ifdef __linux__ + if ( key == K_ENTER ) { + if ( down ) { + if ( keys[K_ALT].down ) { + Key_ClearStates(); + if ( Cvar_VariableValue( "r_fullscreen" ) == 0 ) { + Com_Printf( "Switching to fullscreen rendering\n" ); + Cvar_Set( "r_fullscreen", "1" ); + } else + { + Com_Printf( "Switching to windowed rendering\n" ); + Cvar_Set( "r_fullscreen", "0" ); + } + Cbuf_ExecuteText( EXEC_APPEND, "vid_restart\n" ); + return; + } + } + } +#endif + + // console key is hardcoded, so the user can never unbind it + if ( key == '`' || key == '~' ) { + if ( !down ) { + return; + } + Con_ToggleConsole_f(); + + // the console key should never be used as a char + consoleButtonWasPressed = qtrue; + return; + } else { + consoleButtonWasPressed = qfalse; + } + +//----(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, qfalse ) ); + } + } 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 = qfalse; + + 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/client/cl_main.c b/src/client/cl_main.c new file mode 100644 index 0000000..72f78f5 --- /dev/null +++ b/src/client/cl_main.c @@ -0,0 +1,5224 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cl_main.c -- client main loop + +#include "client.h" +#include + +#include "snd_local.h" // fretn + +cvar_t *cl_wavefilerecord; +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_avidemo; +cvar_t *cl_forceavidemo; + +cvar_t *cl_freelook; +cvar_t *cl_sensitivity; + +cvar_t *cl_mouseAccel; +cvar_t *cl_showMouseRate; + +cvar_t *m_pitch; +cvar_t *m_yaw; +cvar_t *m_forward; +cvar_t *m_side; +cvar_t *m_filter; + +cvar_t *cl_activeAction; + +cvar_t *cl_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; +// DHM - Nerve + +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 + +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 ); + +/* +=============== +CL_CDDialog + +Called by Com_Error when a cd is needed +=============== +*/ +void CL_CDDialog( void ) { + cls.cddialog = qtrue; // start it next frame +} + +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(); +} + +/* +======================================================================= + +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 ( !Cvar_VariableValue( "g_synchronousClients" ) ) { + // Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n"); + //} + + if ( Cmd_Argc() == 2 ) { + s = Cmd_Argv( 1 ); + Q_strncpyz( demoName, s, sizeof( demoName ) ); + Com_sprintf( name, sizeof( name ), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); + } else { + int number; + + // scan for a free demo name + for ( number = 0 ; number <= 9999 ; number++ ) { + CL_DemoFilename( number, demoName ); + Com_sprintf( name, sizeof( name ), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); + + 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; +} + +extern int s_soundtime; +extern int s_paintedtime; +extern portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +portable_samplepair_t wavbuffer[PAINTBUFFER_SIZE]; + +void CL_WriteWaveFilePacket( int endtime ) { + int total, i; + + if ( !clc.waverecording || !clc.wavefile ) { + return; + } + + if ( clc.wavetime == -1 ) { + clc.wavetime = s_soundtime; + + memcpy( wavbuffer, paintbuffer, sizeof( wavbuffer ) ); + return; + } + + if ( s_soundtime <= clc.wavetime ) { + return; + } + + total = s_soundtime - clc.wavetime; + + if ( total < 1 ) { + return; + } + + clc.wavetime = s_soundtime; + + for ( i = 0; i < total; i++ ) { + int parm; + short out; + + parm = ( wavbuffer[i].left ) >> 8; + if ( parm > 32767 ) { + parm = 32767; + } + if ( parm < -32768 ) { + parm = -32768; + } + out = parm; + FS_Write( &out, 2, clc.wavefile ); + + parm = ( wavbuffer[i].right ) >> 8; + if ( parm > 32767 ) { + parm = 32767; + } + if ( parm < -32768 ) { + parm = -32768; + } + out = parm; + FS_Write( &out, 2, clc.wavefile ); + hdr.NumSamples++; + } + memcpy( wavbuffer, paintbuffer, sizeof( wavbuffer ) ); + + Cvar_Set( "cl_waveoffset", va( "%d", FS_FTell( clc.wavefile ) ) ); +} + +/* +==================== +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 = PROTOCOL_VERSION - 1; + while ( prot_ver <= PROTOCOL_VERSION && !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 ); + // 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'; + } + + 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( qtrue ); //----(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; + + // 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 ); + } +} + +/* +=================== +CL_RequestMotd + +=================== +*/ +void CL_RequestMotd( void ) { + char info[MAX_INFO_STRING]; + + if ( !cl_motd->integer ) { + return; + } + Com_Printf( "Resolving %s\n", MOTD_SERVER_NAME ); + if ( !NET_StringToAdr( MOTD_SERVER_NAME, &cls.updateServer ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + cls.updateServer.port = BigShort( PORT_MOTD ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", MOTD_SERVER_NAME, + cls.updateServer.ip[0], cls.updateServer.ip[1], + cls.updateServer.ip[2], cls.updateServer.ip[3], + BigShort( cls.updateServer.port ) ); + + info[0] = 0; + Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", rand() ); + + Info_SetValueForKey( info, "challenge", cls.updateChallenge ); + Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); + Info_SetValueForKey( info, "version", com_version->string ); + + NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); +} + +#ifdef AUTHORIZE_SUPPORT + +/* +=================== +CL_RequestAuthorization + +Authorization server protocol +----------------------------- + +All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff). + +Whenever the client tries to get a challenge from the server it wants to +connect to, it also blindly fires off a packet to the authorize server: + +getKeyAuthorize + +cdkey may be "demo" + + +#OLD The authorize server returns a: +#OLD +#OLD keyAthorize +#OLD +#OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP +#OLD address in the last 15 minutes. + + +The server sends a: + +getIpAuthorize + +The authorize server returns a: + +ipAuthorize + +A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes. +If no response is received from the authorize server after two tries, the client will be let +in anyway. +=================== +*/ +void CL_RequestAuthorization( void ) { + char nums[64]; + int i, j, l; + cvar_t *fs; + + if ( !cls.authorizeServer.port ) { + Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); + if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + + cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, + cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], + cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], + BigShort( cls.authorizeServer.port ) ); + } + if ( cls.authorizeServer.type == NA_BAD ) { + return; + } + + if ( Cvar_VariableValue( "fs_restrict" ) ) { + Q_strncpyz( nums, "ettest", sizeof( nums ) ); + } else { + // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces + j = 0; + l = strlen( cl_cdkey ); + if ( l > 32 ) { + l = 32; + } + for ( i = 0 ; i < l ; i++ ) { + if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' ) + || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' ) + || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) + ) { + nums[j] = cl_cdkey[i]; + j++; + } + } + nums[j] = 0; + } + + fs = Cvar_Get( "cl_anonymous", "0", CVAR_INIT | CVAR_SYSTEMINFO ); + NET_OutOfBandPrint( NS_CLIENT, cls.authorizeServer, va( "getKeyAuthorize %i %s", fs->integer, nums ) ); +} +#endif // AUTHORIZE_SUPPORT + +/* +====================================================================== + +CONSOLE COMMANDS + +====================================================================== +*/ + +/* +================== +CL_ForwardToServer_f +================== +*/ +void CL_ForwardToServer_f( void ) { + if ( cls.state != CA_ACTIVE || clc.demoplaying ) { + Com_Printf( "Not connected to a server.\n" ); + return; + } + + // don't forward the first argument + if ( Cmd_Argc() > 1 ) { + CL_AddReliableCommand( Cmd_Args() ); + } +} + +/* +================== +CL_Setenv_f + +Mostly for controlling voodoo environment variables +================== +*/ +void CL_Setenv_f( void ) { + int argc = Cmd_Argc(); + + if ( argc > 2 ) { + char buffer[1024]; + int i; + + strcpy( buffer, Cmd_Argv( 1 ) ); + strcat( buffer, "=" ); + + for ( i = 2; i < argc; i++ ) { + strcat( buffer, Cmd_Argv( i ) ); + strcat( buffer, " " ); + } + + Q_putenv( buffer ); + } else if ( argc == 2 ) { + char *env = getenv( Cmd_Argv( 1 ) ); + + if ( env ) { + Com_Printf( "%s=%s\n", Cmd_Argv( 1 ), env ); + } else { + Com_Printf( "%s undefined\n", Cmd_Argv( 1 ) ); + } + } +} + + +/* +================== +CL_Disconnect_f +================== +*/ +void CL_Disconnect_f( void ) { + SCR_StopCinematic(); + Cvar_Set( "savegame_loading", "0" ); + Cvar_Set( "g_reloading", "0" ); + if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) { + Com_Error( ERR_DISCONNECT, "Disconnected from server" ); + } +} + + +/* +================ +CL_Reconnect_f + +================ +*/ +void CL_Reconnect_f( void ) { + if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) { + Com_Printf( "Can't reconnect to localhost.\n" ); + return; + } + Cbuf_AddText( va( "connect %s\n", cls.servername ) ); +} + +/* +================ +CL_Connect_f + +================ +*/ +void CL_Connect_f( void ) { + char *server; + char ip_port[MAX_STRING_CHARS]; + + if ( Cmd_Argc() != 2 ) { + Com_Printf( "usage: connect [server]\n" ); + return; + } + + S_StopAllSounds(); // NERVE - SMF + + // starting to load a map so we get out of full screen ui mode + Cvar_Set( "r_uiFullScreen", "0" ); + Cvar_Set( "ui_connecting", "1" ); + + // fire a message off to the motd server + CL_RequestMotd(); + + // clear any previous "server full" type messages + clc.serverMessage[0] = 0; + + server = Cmd_Argv( 1 ); + + if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) { + // if running a local server, kill it + SV_Shutdown( "Server quit\n" ); + } + + // make sure a local server is killed + Cvar_Set( "sv_killserver", "1" ); + SV_Frame( 0 ); + + CL_Disconnect( qtrue ); + Con_Close(); + + Q_strncpyz( cls.servername, server, sizeof( cls.servername ) ); + + if ( !NET_StringToAdr( cls.servername, &clc.serverAddress ) ) { + Com_Printf( "Bad server address\n" ); + cls.state = CA_DISCONNECTED; + Cvar_Set( "ui_connecting", "0" ); + return; + } + if ( clc.serverAddress.port == 0 ) { + clc.serverAddress.port = BigShort( PORT_SERVER ); + } + + Q_strncpyz( ip_port, NET_AdrToString( clc.serverAddress ), sizeof( ip_port ) ); + Com_Printf( "%s resolved to %s\n", cls.servername, ip_port ); + + // if we aren't playing on a lan, we need to authenticate + // with the cd key + if ( NET_IsLocalAddress( clc.serverAddress ) ) { + cls.state = CA_CHALLENGING; + } else { + cls.state = CA_CONNECTING; + } + + + Cvar_Set( "cl_avidemo", "0" ); + + // show_bug.cgi?id=507 + // prepare to catch a connection process that would turn bad + Cvar_Set( "com_errorDiagnoseIP", NET_AdrToString( clc.serverAddress ) ); + // ATVI Wolfenstein Misc #439 + // we need to setup a correct default for this, otherwise the first val we set might reappear + Cvar_Set( "com_errorMessage", "" ); + + cls.keyCatchers = 0; + clc.connectTime = -99999; // CL_CheckForResend() will fire immediately + clc.connectPacketCount = 0; + + // server connection string + Cvar_Set( "cl_currentServerAddress", server ); + Cvar_Set( "cl_currentServerIP", ip_port ); + + // Gordon: um, couldnt this be handled + // NERVE - SMF - reset some cvars + Cvar_Set( "mp_playerType", "0" ); + Cvar_Set( "mp_currentPlayerType", "0" ); + Cvar_Set( "mp_weapon", "0" ); + Cvar_Set( "mp_team", "0" ); + Cvar_Set( "mp_currentTeam", "0" ); + + Cvar_Set( "ui_limboOptions", "0" ); + Cvar_Set( "ui_limboPrevOptions", "0" ); + Cvar_Set( "ui_limboObjective", "0" ); + // -NERVE - SMF + +} + + +/* +===================== +CL_Rcon_f + + Send the rest of the command line over as + an unconnected command. +===================== +*/ +void CL_Rcon_f( void ) { + char message[1024]; + netadr_t to; + + if ( !rcon_client_password->string ) { + Com_Printf( "You must set 'rconPassword' before\n" + "issuing an rcon command.\n" ); + return; + } + + message[0] = -1; + message[1] = -1; + message[2] = -1; + message[3] = -1; + message[4] = 0; + + strcat( message, "rcon " ); + + strcat( message, rcon_client_password->string ); + strcat( message, " " ); + + // ATVI Wolfenstein Misc #284 + strcat( message, Cmd_Cmd() + 5 ); + + if ( cls.state >= CA_CONNECTED ) { + to = clc.netchan.remoteAddress; + } else { + if ( !strlen( rconAddress->string ) ) { + Com_Printf( "You must either be connected,\n" + "or set the 'rconAddress' cvar\n" + "to issue rcon commands\n" ); + + return; + } + NET_StringToAdr( rconAddress->string, &to ); + if ( to.port == 0 ) { + to.port = BigShort( PORT_SERVER ); + } + } + + NET_SendPacket( NS_CLIENT, strlen( message ) + 1, message, to ); +} + +/* +================= +CL_SendPureChecksums +================= +*/ +void CL_SendPureChecksums( void ) { + const char *pChecksums; + char cMsg[MAX_INFO_VALUE]; + int i; + + // if we are pure we need to send back a command with our referenced pk3 checksums + pChecksums = FS_ReferencedPakPureChecksums(); + + // "cp" + Com_sprintf( cMsg, sizeof( cMsg ), "Va " ); + Q_strcat( cMsg, sizeof( cMsg ), va( "%d ", cl.serverId ) ); + Q_strcat( cMsg, sizeof( cMsg ), pChecksums ); + for ( i = 0; i < 2; i++ ) { + cMsg[i] += 13 + ( i * 2 ); + } + CL_AddReliableCommand( cMsg ); +} + +/* +================= +CL_ResetPureClientAtServer +================= +*/ +void CL_ResetPureClientAtServer( void ) { + CL_AddReliableCommand( va( "vdr" ) ); +} + +/* +================= +CL_Vid_Restart_f + +Restart the video subsystem + +we also have to reload the UI and CGame because the renderer +doesn't know what graphics to reload +================= +*/ + +#ifdef _WIN32 +extern void Sys_In_Restart_f( void ); // fretn +#endif + +void CL_Vid_Restart_f( void ) { + + // RF, don't show percent bar, since the memory usage will just sit at the same level anyway + com_expectedhunkusage = -1; + + // don't let them loop during the restart + S_StopAllSounds(); + // shutdown the UI + CL_ShutdownUI(); + // shutdown the CGame + CL_ShutdownCGame(); + // shutdown the renderer and clear the renderer interface + CL_ShutdownRef(); + // client is no longer pure untill new checksums are sent + CL_ResetPureClientAtServer(); + // clear pak references + FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF ); + // reinitialize the filesystem if the game directory or checksum has changed + FS_ConditionalRestart( clc.checksumFeed ); + + S_BeginRegistration(); // all sound handles are now invalid + + cls.rendererStarted = qfalse; + cls.uiStarted = qfalse; + cls.cgameStarted = qfalse; + cls.soundRegistered = qfalse; + autoupdateChecked = qfalse; + + // unpause so the cgame definately gets a snapshot and renders a frame + Cvar_Set( "cl_paused", "0" ); + + // if not running a server clear the whole hunk + if ( !com_sv_running->integer ) { + // clear the whole hunk + Hunk_Clear(); + } else { + // clear all the client data on the hunk + Hunk_ClearToMark(); + } + + // initialize the renderer interface + CL_InitRef(); + + // startup all the client stuff + CL_StartHunkUsers(); + +#ifdef _WIN32 + Sys_In_Restart_f(); // fretn +#endif + // start the cgame if connected + if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) { + cls.cgameStarted = qtrue; + CL_InitCGame(); + // send pure checksums + CL_SendPureChecksums(); + } +} + +/* +================= +CL_UI_Restart_f + +Restart the ui subsystem +================= +*/ +void CL_UI_Restart_f( void ) { // NERVE - SMF + // shutdown the UI + CL_ShutdownUI(); + + autoupdateChecked = qfalse; + + // init the UI + CL_InitUI(); +} + +/* +================= +CL_Snd_Reload_f + +Reloads sounddata from disk, retains soundhandles. +================= +*/ +void CL_Snd_Reload_f( void ) { + S_Reload(); +} + + +/* +================= +CL_Snd_Restart_f + +Restart the sound subsystem +The cgame and game must also be forced to restart because +handles will be invalid +================= +*/ +void CL_Snd_Restart_f( void ) { + S_Shutdown(); + S_Init(); + + CL_Vid_Restart_f(); +} + + +/* +================== +CL_PK3List_f +================== +*/ +void CL_OpenedPK3List_f( void ) { + Com_Printf( "Opened PK3 Names: %s\n", FS_LoadedPakNames() ); +} + +/* +================== +CL_PureList_f +================== +*/ +void CL_ReferencedPK3List_f( void ) { + Com_Printf( "Referenced PK3 Names: %s\n", FS_ReferencedPakNames() ); +} + +/* +================== +CL_Configstrings_f +================== +*/ +void CL_Configstrings_f( void ) { + int i; + int ofs; + + if ( cls.state != CA_ACTIVE ) { + Com_Printf( "Not connected to a server.\n" ); + return; + } + + for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { + ofs = cl.gameState.stringOffsets[ i ]; + if ( !ofs ) { + continue; + } + Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs ); + } +} + +/* +============== +CL_Clientinfo_f +============== +*/ +void CL_Clientinfo_f( void ) { + Com_Printf( "--------- Client Information ---------\n" ); + Com_Printf( "state: %i\n", cls.state ); + Com_Printf( "Server: %s\n", cls.servername ); + Com_Printf( "User info settings:\n" ); + Info_Print( Cvar_InfoString( CVAR_USERINFO ) ); + Com_Printf( "--------------------------------------\n" ); +} + +/* +============== +CL_EatMe_f + +Eat misc console commands to prevent exploits +============== +*/ +void CL_EatMe_f( void ) { + //do nothing kthxbye +} + +/* +============== +CL_WavRecord_f +============== +*/ + +void CL_WavRecord_f( void ) { + if ( clc.wavefile ) { + Com_Printf( "Already recording a wav file\n" ); + return; + } + + CL_WriteWaveOpen(); +} + +/* +============== +CL_WavStopRecord_f +============== +*/ + +void CL_WavStopRecord_f( void ) { + if ( !clc.wavefile ) { + Com_Printf( "Not recording a wav file\n" ); + return; + } + + CL_WriteWaveClose(); + Cvar_Set( "cl_waverecording", "0" ); + Cvar_Set( "cl_wavefilename", "" ); + Cvar_Set( "cl_waveoffset", "0" ); + clc.waverecording = qfalse; +} + + +//==================================================================== + +/* +================= +CL_DownloadsComplete + +Called when all downloading has been completed +================= +*/ +void CL_DownloadsComplete( void ) { + +#ifndef _WIN32 + char *fs_write_path; +#endif + char *fn; + + // DHM - Nerve :: Auto-update (not finished yet) + if ( autoupdateStarted ) { + + if ( autoupdateFilename && ( strlen( autoupdateFilename ) > 4 ) ) { +#ifdef _WIN32 + // win32's Sys_StartProcess prepends the current dir + fn = va( "%s/%s", FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT ), autoupdateFilename ); +#else + fs_write_path = Cvar_VariableString( "fs_homepath" ); + fn = FS_BuildOSPath( fs_write_path, FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT ), autoupdateFilename ); +#ifndef __MACOS__ + Sys_Chmod( fn, S_IXUSR ); +#endif +#endif + // will either exit with a successful process spawn, or will Com_Error ERR_DROP + // so we need to clear the disconnected download data if needed + if ( cls.bWWWDlDisconnected ) { + cls.bWWWDlDisconnected = qfalse; + CL_ClearStaticDownload(); + } + Sys_StartProcess( fn, qtrue ); + } + + // NOTE - TTimo: that code is never supposed to be reached? + + autoupdateStarted = qfalse; + + if ( !cls.bWWWDlDisconnected ) { + CL_Disconnect( qtrue ); + } + // we can reset that now + cls.bWWWDlDisconnected = qfalse; + CL_ClearStaticDownload(); + + return; + } + + // if we downloaded files we need to restart the file system + if ( cls.downloadRestart ) { + cls.downloadRestart = qfalse; + + FS_Restart( clc.checksumFeed ); // We possibly downloaded a pak, restart the file system to load it + + if ( !cls.bWWWDlDisconnected ) { + // inform the server so we get new gamestate info + CL_AddReliableCommand( "donedl" ); + } + // we can reset that now + cls.bWWWDlDisconnected = qfalse; + CL_ClearStaticDownload(); + + // by sending the donedl command we request a new gamestate + // so we don't want to load stuff yet + return; + } + + // TTimo: I wonder if that happens - it should not but I suspect it could happen if a download fails in the middle or is aborted + assert( !cls.bWWWDlDisconnected ); + + // let the client game init and load data + cls.state = CA_LOADING; + + // Pump the loop, this may change gamestate! + Com_EventLoop(); + + // if the gamestate was changed by calling Com_EventLoop + // then we loaded everything already and we don't want to do it again. + if ( cls.state != CA_LOADING ) { + return; + } + + // starting to load a map so we get out of full screen ui mode + Cvar_Set( "r_uiFullScreen", "0" ); + + // flush client memory and start loading stuff + // this will also (re)load the UI + // if this is a local client then only the client part of the hunk + // will be cleared, note that this is done after the hunk mark has been set + CL_FlushMemory(); + + // initialize the CGame + cls.cgameStarted = qtrue; + CL_InitCGame(); + + // set pure checksums + CL_SendPureChecksums(); + + CL_WritePacket(); + CL_WritePacket(); + CL_WritePacket(); +} + +/* +================= +CL_BeginDownload + +Requests a file to download from the server. Stores it in the current +game directory. +================= +*/ +void CL_BeginDownload( const char *localName, const char *remoteName ) { + + Com_DPrintf( "***** CL_BeginDownload *****\n" + "Localname: %s\n" + "Remotename: %s\n" + "****************************\n", localName, remoteName ); + + Q_strncpyz( cls.downloadName, localName, sizeof( cls.downloadName ) ); + Com_sprintf( cls.downloadTempName, sizeof( cls.downloadTempName ), "%s.tmp", localName ); + + // Set so UI gets access to it + Cvar_Set( "cl_downloadName", remoteName ); + Cvar_Set( "cl_downloadSize", "0" ); + Cvar_Set( "cl_downloadCount", "0" ); + Cvar_SetValue( "cl_downloadTime", cls.realtime ); + + clc.downloadBlock = 0; // Starting new file + clc.downloadCount = 0; + + CL_AddReliableCommand( va( "download %s", remoteName ) ); +} + +/* +================= +CL_NextDownload + +A download completed or failed +================= +*/ +void CL_NextDownload( void ) { + char *s; + char *remoteName, *localName; + + // We are looking to start a download here + if ( *clc.downloadList ) { + s = clc.downloadList; + + // format is: + // @remotename@localname@remotename@localname, etc. + + if ( *s == '@' ) { + s++; + } + remoteName = s; + + if ( ( s = strchr( s, '@' ) ) == NULL ) { + CL_DownloadsComplete(); + return; + } + + *s++ = 0; + localName = s; + if ( ( s = strchr( s, '@' ) ) != NULL ) { + *s++ = 0; + } else { + s = localName + strlen( localName ); // point at the nul byte + + } + CL_BeginDownload( localName, remoteName ); + + cls.downloadRestart = qtrue; + + // move over the rest + memmove( clc.downloadList, s, strlen( s ) + 1 ); + + return; + } + + CL_DownloadsComplete(); +} + +/* +================= +CL_InitDownloads + +After receiving a valid game state, we valid the cgame and local zip files here +and determine if we need to download them +================= +*/ +void CL_InitDownloads( void ) { +#ifndef PRE_RELEASE_DEMO + char missingfiles[1024]; + char *dir = FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT ); + + // TTimo + // init some of the www dl data + clc.bWWWDl = qfalse; + clc.bWWWDlAborting = qfalse; + cls.bWWWDlDisconnected = qfalse; + CL_ClearStaticDownload(); + + if ( autoupdateStarted && NET_CompareAdr( cls.autoupdateServer, clc.serverAddress ) ) { + if ( strlen( cl_updatefiles->string ) > 4 ) { + Q_strncpyz( autoupdateFilename, cl_updatefiles->string, sizeof( autoupdateFilename ) ); + Q_strncpyz( clc.downloadList, va( "@%s/%s@%s/%s", dir, cl_updatefiles->string, dir, cl_updatefiles->string ), MAX_INFO_STRING ); + cls.state = CA_CONNECTED; + CL_NextDownload(); + return; + } + } else + { + // whatever autodownlad configuration, store missing files in a cvar, use later in the ui maybe + if ( FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) ) { + Cvar_Set( "com_missingFiles", missingfiles ); + } else { + Cvar_Set( "com_missingFiles", "" ); + } + + // reset the redirect checksum tracking + clc.redirectedList[0] = '\0'; + + if ( cl_allowDownload->integer && FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ), qtrue ) ) { + // this gets printed to UI, i18n + Com_Printf( CL_TranslateStringBuf( "Need paks: %s\n" ), clc.downloadList ); + + if ( *clc.downloadList ) { + // if autodownloading is not enabled on the server + cls.state = CA_CONNECTED; + CL_NextDownload(); + return; + } + } + } +#endif + + CL_DownloadsComplete(); +} + +/* +================= +CL_CheckForResend + +Resend a connect message if the last one has timed out +================= +*/ +void CL_CheckForResend( void ) { + int port, i; + char info[MAX_INFO_STRING]; + char data[MAX_INFO_STRING]; + char pkt[1024 + 1] ; // EVEN BALANCE - T.RAY + int pktlen ; // EVEN BALANCE - T.RAY + + // don't send anything if playing back a demo + if ( clc.demoplaying ) { + return; + } + + // resend if we haven't gotten a reply yet + if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) { + return; + } + + if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) { + return; + } + + clc.connectTime = cls.realtime; // for retransmit requests + clc.connectPacketCount++; + + switch ( cls.state ) { + case CA_CONNECTING: + // requesting a challenge +#ifdef AUTHORIZE_SUPPORT + if ( !Sys_IsLANAddress( clc.serverAddress ) ) { + CL_RequestAuthorization(); + } +#endif // AUTHORIZE_SUPPORT + + // EVEN BALANCE - T.RAY + strcpy( pkt, "getchallenge" ) ; + pktlen = strlen( pkt ) ; + NET_OutOfBandPrint( NS_CLIENT, clc.serverAddress, pkt ); + break; + + case CA_CHALLENGING: + // sending back the challenge + port = Cvar_VariableValue( "net_qport" ); + + Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) ); + Info_SetValueForKey( info, "protocol", va( "%i", PROTOCOL_VERSION ) ); + Info_SetValueForKey( info, "qport", va( "%i", port ) ); + Info_SetValueForKey( info, "challenge", va( "%i", clc.challenge ) ); + + strcpy( data, "connect " ); + + data[8] = '\"'; // NERVE - SMF - spaces in name bugfix + + for ( i = 0; i < strlen( info ); i++ ) { + data[9 + i] = info[i]; // + (clc.challenge)&0x3; + } + data[9 + i] = '\"'; // NERVE - SMF - spaces in name bugfix + data[10 + i] = 0; + + // EVEN BALANCE - T.RAY + pktlen = i + 10 ; + memcpy( pkt, &data[0], pktlen ) ; + + NET_OutOfBandData( NS_CLIENT, clc.serverAddress, pkt, pktlen ); + // the most current userinfo has been sent, so watch for any + // newer changes to userinfo variables + cvar_modifiedFlags &= ~CVAR_USERINFO; + break; + + default: + Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" ); + } +} + +/* +=================== +CL_DisconnectPacket + +Sometimes the server can drop the client and the netchan based +disconnect can be lost. If the client continues to send packets +to the server, the server will send out of band disconnect packets +to the client so it doesn't have to wait for the full timeout period. +=================== +*/ +void CL_DisconnectPacket( netadr_t from ) { + const char* message; + + if ( cls.state < CA_AUTHORIZING ) { + return; + } + + // if not from our server, ignore it + if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { + return; + } + + // if we have received packets within three seconds, ignore (it might be a malicious spoof) + // NOTE TTimo: + // there used to be a clc.lastPacketTime = cls.realtime; line in CL_PacketEvent before calling CL_ConnectionLessPacket + // therefore .. packets never got through this check, clients never disconnected + // switched the clc.lastPacketTime = cls.realtime to happen after the connectionless packets have been processed + // you still can't spoof disconnects, cause legal netchan packets will maintain realtime - lastPacketTime below the threshold + if ( cls.realtime - clc.lastPacketTime < 3000 ) { + return; + } + + // if we are doing a disconnected download, leave the 'connecting' screen on with the progress information + if ( !cls.bWWWDlDisconnected ) { + // drop the connection + message = "Server disconnected for unknown reason"; + Com_Printf( message ); + Cvar_Set( "com_errorMessage", message ); + CL_Disconnect( qtrue ); + } else { + CL_Disconnect( qfalse ); + Cvar_Set( "ui_connecting", "1" ); + Cvar_Set( "ui_dl_running", "1" ); + } +} + + +/* +=================== +CL_MotdPacket + +=================== +*/ +void CL_MotdPacket( netadr_t from ) { + char *challenge; + char *info; + + // if not from our server, ignore it + if ( !NET_CompareAdr( from, cls.updateServer ) ) { + return; + } + + info = Cmd_Argv( 1 ); + + // check challenge + challenge = Info_ValueForKey( info, "challenge" ); + if ( strcmp( challenge, cls.updateChallenge ) ) { + return; + } + + challenge = Info_ValueForKey( info, "motd" ); + + Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) ); + Cvar_Set( "cl_motdString", challenge ); +} + +/* +=================== +CL_PrintPackets +an OOB message from server, with potential markups +print OOB are the only messages we handle markups in +[err_dialog]: used to indicate that the connection should be aborted + no further information, just do an error diagnostic screen afterwards +[err_prot]: HACK. This is a protocol error. The client uses a custom + protocol error message (client sided) in the diagnostic window. + The space for the error message on the connection screen is limited + to 256 chars. +=================== +*/ +void CL_PrintPacket( netadr_t from, msg_t *msg ) { + char *s; + s = MSG_ReadBigString( msg ); + if ( !Q_stricmpn( s, "[err_dialog]", 12 ) ) { + Q_strncpyz( clc.serverMessage, s + 12, sizeof( clc.serverMessage ) ); + // Cvar_Set("com_errorMessage", clc.serverMessage ); + Com_Error( ERR_DROP, clc.serverMessage ); + } else if ( !Q_stricmpn( s, "[err_prot]", 10 ) ) { + Q_strncpyz( clc.serverMessage, s + 10, sizeof( clc.serverMessage ) ); + // Cvar_Set("com_errorMessage", CL_TranslateStringBuf( PROTOCOL_MISMATCH_ERROR_LONG ) ); + Com_Error( ERR_DROP, CL_TranslateStringBuf( PROTOCOL_MISMATCH_ERROR_LONG ) ); + } else if ( !Q_stricmpn( s, "[err_update]", 12 ) ) { + Q_strncpyz( clc.serverMessage, s + 12, sizeof( clc.serverMessage ) ); + Com_Error( ERR_AUTOUPDATE, clc.serverMessage ); + } else if ( !Q_stricmpn( s, "ET://", 5 ) ) { // fretn + Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); + Cvar_Set( "com_errorMessage", clc.serverMessage ); + Com_Error( ERR_DROP, clc.serverMessage ); + } else { + Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); + } + Com_Printf( "%s", clc.serverMessage ); +} + +/* +=================== +CL_InitServerInfo +=================== +*/ +void CL_InitServerInfo( serverInfo_t *server, serverAddress_t *address ) { + server->adr.type = NA_IP; + server->adr.ip[0] = address->ip[0]; + server->adr.ip[1] = address->ip[1]; + server->adr.ip[2] = address->ip[2]; + server->adr.ip[3] = address->ip[3]; + server->adr.port = address->port; + server->clients = 0; + server->hostName[0] = '\0'; + server->mapName[0] = '\0'; + server->maxClients = 0; + server->maxPing = 0; + server->minPing = 0; + server->ping = -1; + server->game[0] = '\0'; + server->gameType = 0; + server->netType = 0; + server->allowAnonymous = 0; +} + +#define MAX_SERVERSPERPACKET 256 + +/* +=================== +CL_ServersResponsePacket +=================== +*/ +void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { + int i, count, max, total; + serverAddress_t addresses[MAX_SERVERSPERPACKET]; + int numservers; + byte* buffptr; + byte* buffend; + + Com_Printf( "CL_ServersResponsePacket\n" ); + + if ( cls.numglobalservers == -1 ) { + // state to detect lack of servers or lack of response + cls.numglobalservers = 0; + cls.numGlobalServerAddresses = 0; + } + + // parse through server response string + numservers = 0; + buffptr = msg->data; + buffend = buffptr + msg->cursize; + while ( buffptr + 1 < buffend ) { + // advance to initial token + do { + if ( *buffptr++ == '\\' ) { + break; + } + } + while ( buffptr < buffend ); + + if ( buffptr >= buffend - 6 ) { + break; + } + + // parse out ip + addresses[numservers].ip[0] = *buffptr++; + addresses[numservers].ip[1] = *buffptr++; + addresses[numservers].ip[2] = *buffptr++; + addresses[numservers].ip[3] = *buffptr++; + + // parse out port + addresses[numservers].port = ( *buffptr++ ) << 8; + addresses[numservers].port += *buffptr++; + addresses[numservers].port = BigShort( addresses[numservers].port ); + + // syntax check + if ( *buffptr != '\\' ) { + break; + } + + Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers, + addresses[numservers].ip[0], + addresses[numservers].ip[1], + addresses[numservers].ip[2], + addresses[numservers].ip[3], + addresses[numservers].port ); + + numservers++; + if ( numservers >= MAX_SERVERSPERPACKET ) { + break; + } + + // parse out EOT + if ( buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T' ) { + break; + } + } + + if ( cls.masterNum == 0 ) { + count = cls.numglobalservers; + max = MAX_GLOBAL_SERVERS; + } else { + // shut up compiler + count = 0; + max = 1; + } + + for ( i = 0; i < numservers && count < max; i++ ) { + // build net address + //serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count]; + serverInfo_t *server = &cls.globalServers[count]; + + CL_InitServerInfo( server, &addresses[i] ); + // advance to next slot + count++; + } + + // if getting the global list and there are too many servers + if ( cls.masterNum == 0 && count >= max ) { + for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++ ) { + serverAddress_t *addr; + // just store the addresses in an additional list + addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++]; + addr->ip[0] = addresses[i].ip[0]; + addr->ip[1] = addresses[i].ip[1]; + addr->ip[2] = addresses[i].ip[2]; + addr->ip[3] = addresses[i].ip[3]; + addr->port = addresses[i].port; + } + } + + if ( cls.masterNum == 0 ) { + cls.numglobalservers = count; + total = count + cls.numGlobalServerAddresses; + } else { + total = cls.numglobalservers = 0; + } + + Com_Printf( "%d servers parsed (total %d)\n", numservers, total ); +} + +/* +================= +CL_ConnectionlessPacket + +Responses to broadcasts, etc +================= +*/ +void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { + char *s; + char *c; + + MSG_BeginReadingOOB( msg ); + MSG_ReadLong( msg ); // skip the -1 + + s = MSG_ReadStringLine( msg ); + + Cmd_TokenizeString( s ); + + c = Cmd_Argv( 0 ); + + Com_DPrintf( "CL packet %s: %s\n", NET_AdrToString( from ), c ); + + // challenge from the server we are connecting to + if ( !Q_stricmp( c, "challengeResponse" ) ) { + if ( cls.state != CA_CONNECTING ) { + Com_Printf( "Unwanted challenge response received. Ignored.\n" ); + } else { + // start sending challenge repsonse instead of challenge request packets + clc.challenge = atoi( Cmd_Argv( 1 ) ); + if ( Cmd_Argc() > 2 ) { + clc.onlyVisibleClients = atoi( Cmd_Argv( 2 ) ); // DHM - Nerve + } else { + clc.onlyVisibleClients = 0; + } + cls.state = CA_CHALLENGING; + clc.connectPacketCount = 0; + clc.connectTime = -99999; + + // take this address as the new server address. This allows + // a server proxy to hand off connections to multiple servers + clc.serverAddress = from; + Com_DPrintf( "challenge: %d\n", clc.challenge ); + } + return; + } + + // server connection + if ( !Q_stricmp( c, "connectResponse" ) ) { + if ( cls.state >= CA_CONNECTED ) { + Com_Printf( "Dup connect received. Ignored.\n" ); + return; + } + if ( cls.state != CA_CHALLENGING ) { + Com_Printf( "connectResponse packet while not connecting. Ignored.\n" ); + return; + } + if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) { + Com_Printf( "connectResponse from a different address. Ignored.\n" ); + Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), + NET_AdrToString( clc.serverAddress ) ); + return; + } + + // DHM - Nerve :: If we have completed a connection to the Auto-Update server... + if ( autoupdateChecked && NET_CompareAdr( cls.autoupdateServer, clc.serverAddress ) ) { + // Mark the client as being in the process of getting an update + if ( cl_updateavailable->integer ) { + autoupdateStarted = qtrue; + } + } + + Netchan_Setup( NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) ); + cls.state = CA_CONNECTED; + clc.lastPacketSentTime = -9999; // send first packet immediately + return; + } + + // server responding to an info broadcast + if ( !Q_stricmp( c, "infoResponse" ) ) { + CL_ServerInfoPacket( from, msg ); + return; + } + + // server responding to a get playerlist + if ( !Q_stricmp( c, "statusResponse" ) ) { + CL_ServerStatusResponse( from, msg ); + return; + } + + // a disconnect message from the server, which will happen if the server + // dropped the connection but it is still getting packets from us + if ( !Q_stricmp( c, "disconnect" ) ) { + CL_DisconnectPacket( from ); + return; + } + + // echo request from server + if ( !Q_stricmp( c, "echo" ) ) { + NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ) ); + return; + } + + // cd check + if ( !Q_stricmp( c, "keyAuthorize" ) ) { + // we don't use these now, so dump them on the floor + return; + } + + // global MOTD from id + if ( !Q_stricmp( c, "motd" ) ) { + CL_MotdPacket( from ); + return; + } + + // echo request from server + if ( !Q_stricmp( c, "print" ) ) { + CL_PrintPacket( from, msg ); + return; + } + + // DHM - Nerve :: Auto-update server response message + if ( !Q_stricmp( c, "updateResponse" ) ) { + CL_UpdateInfoPacket( from ); + return; + } + // DHM - Nerve + + // NERVE - SMF - bugfix, make this compare first n chars so it doesnt bail if token is parsed incorrectly + // echo request from server + if ( !Q_strncmp( c, "getserversResponse", 18 ) ) { + CL_ServersResponsePacket( from, msg ); + return; + } + + Com_DPrintf( "Unknown connectionless packet command.\n" ); +} + + +/* +================= +CL_PacketEvent + +A packet has arrived from the main event loop +================= +*/ +void CL_PacketEvent( netadr_t from, msg_t *msg ) { + int headerBytes; + + if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) { + CL_ConnectionlessPacket( from, msg ); + return; + } + + clc.lastPacketTime = cls.realtime; + + if ( cls.state < CA_CONNECTED ) { + return; // can't be a valid sequenced packet + } + + if ( msg->cursize < 4 ) { + Com_Printf( "%s: Runt packet\n",NET_AdrToString( from ) ); + return; + } + + // + // packet from server + // + if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { + Com_DPrintf( "%s:sequenced packet without connection\n" + ,NET_AdrToString( from ) ); + // FIXME: send a client disconnect? + return; + } + + if ( !CL_Netchan_Process( &clc.netchan, msg ) ) { + return; // out of order, duplicated, etc + } + + // the header is different lengths for reliable and unreliable messages + headerBytes = msg->readcount; + + // track the last message received so it can be returned in + // client messages, allowing the server to detect a dropped + // gamestate + clc.serverMessageSequence = LittleLong( *(int *)msg->data ); + + clc.lastPacketTime = cls.realtime; + CL_ParseServerMessage( msg ); + + // + // we don't know if it is ok to save a demo message until + // after we have parsed the frame + // + + if ( clc.demorecording && !clc.demowaiting ) { + CL_WriteDemoMessage( msg, headerBytes ); + } +} + +/* +================== +CL_CheckTimeout + +================== +*/ +void CL_CheckTimeout( void ) { + // + // check timeout + // + if ( ( !cl_paused->integer || !sv_paused->integer ) + && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC + && cls.realtime - clc.lastPacketTime > cl_timeout->value * 1000 ) { + if ( ++cl.timeoutcount > 5 ) { // timeoutcount saves debugger + Cvar_Set( "com_errorMessage", "Server connection timed out." ); + CL_Disconnect( qtrue ); + return; + } + } else { + cl.timeoutcount = 0; + } +} + + +//============================================================================ + +/* +================== +CL_CheckUserinfo + +================== +*/ +void CL_CheckUserinfo( void ) { + // don't add reliable commands when not yet connected + if ( cls.state < CA_CHALLENGING ) { + return; + } + // don't overflow the reliable command buffer when paused + if ( cl_paused->integer ) { + return; + } + // send a reliable userinfo update if needed + if ( cvar_modifiedFlags & CVAR_USERINFO ) { + cvar_modifiedFlags &= ~CVAR_USERINFO; + CL_AddReliableCommand( va( "userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) ); + } +} + +/* +================== +CL_WWWDownload +================== +*/ +void CL_WWWDownload( void ) { + char *to_ospath; + dlStatus_t ret; + static qboolean bAbort = qfalse; + + if ( clc.bWWWDlAborting ) { + if ( !bAbort ) { + Com_DPrintf( "CL_WWWDownload: WWWDlAborting\n" ); + bAbort = qtrue; + } + return; + } + if ( bAbort ) { + Com_DPrintf( "CL_WWWDownload: WWWDlAborting done\n" ); + bAbort = qfalse; + } + + ret = DL_DownloadLoop(); + + if ( ret == DL_CONTINUE ) { + return; + } + + if ( ret == DL_DONE ) { + // taken from CL_ParseDownload + // we work with OS paths + clc.download = 0; + to_ospath = FS_BuildOSPath( Cvar_VariableString( "fs_homepath" ), cls.originalDownloadName, "" ); + to_ospath[strlen( to_ospath ) - 1] = '\0'; + if ( rename( cls.downloadTempName, to_ospath ) ) { + FS_CopyFile( cls.downloadTempName, to_ospath ); + remove( cls.downloadTempName ); + } + *cls.downloadTempName = *cls.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + if ( cls.bWWWDlDisconnected ) { + // for an auto-update in disconnected mode, we'll be spawning the setup in CL_DownloadsComplete + if ( !autoupdateStarted ) { + // reconnect to the server, which might send us to a new disconnected download + Cbuf_ExecuteText( EXEC_APPEND, "reconnect\n" ); + } + } else { + CL_AddReliableCommand( "wwwdl done" ); + // tracking potential web redirects leading us to wrong checksum - only works in connected mode + if ( strlen( clc.redirectedList ) + strlen( cls.originalDownloadName ) + 1 >= sizeof( clc.redirectedList ) ) { + // just to be safe + Com_Printf( "ERROR: redirectedList overflow (%s)\n", clc.redirectedList ); + } else { + strcat( clc.redirectedList, "@" ); + strcat( clc.redirectedList, cls.originalDownloadName ); + } + } + } else + { + if ( cls.bWWWDlDisconnected ) { + // in a connected download, we'd tell the server about failure and wait for a reply + // but in this case we can't get anything from server + // if we just reconnect it's likely we'll get the same disconnected download message, and error out again + // this may happen for a regular dl or an auto update + const char *error = va( "Download failure while getting '%s'\n", cls.downloadName ); // get the msg before clearing structs + cls.bWWWDlDisconnected = qfalse; // need clearing structs before ERR_DROP, or it goes into endless reload + CL_ClearStaticDownload(); + Com_Error( ERR_DROP, error ); + } else { + // see CL_ParseDownload, same abort strategy + Com_Printf( "Download failure while getting '%s'\n", cls.downloadName ); + CL_AddReliableCommand( "wwwdl fail" ); + clc.bWWWDlAborting = qtrue; + } + return; + } + + clc.bWWWDl = qfalse; + CL_NextDownload(); +} + +/* +================== +CL_WWWBadChecksum + +FS code calls this when doing FS_ComparePaks +we can detect files that we got from a www dl redirect with a wrong checksum +this indicates that the redirect setup is broken, and next dl attempt should NOT redirect +================== +*/ +qboolean CL_WWWBadChecksum( const char *pakname ) { + if ( strstr( clc.redirectedList, va( "@%s", pakname ) ) ) { + Com_Printf( "WARNING: file %s obtained through download redirect has wrong checksum\n", pakname ); + Com_Printf( " this likely means the server configuration is broken\n" ); + if ( strlen( clc.badChecksumList ) + strlen( pakname ) + 1 >= sizeof( clc.badChecksumList ) ) { + Com_Printf( "ERROR: badChecksumList overflowed (%s)\n", clc.badChecksumList ); + return qfalse; + } + strcat( clc.badChecksumList, "@" ); + strcat( clc.badChecksumList, pakname ); + Com_DPrintf( "bad checksums: %s\n", clc.badChecksumList ); + return qtrue; + } + return qfalse; +} + +/* +================== +CL_Frame + +================== +*/ +void CL_Frame( int msec ) { + + if ( !com_cl_running->integer ) { + return; + } + + if ( cls.cddialog ) { + // bring up the cd error dialog if needed + cls.cddialog = qfalse; + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD ); + } else if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) + && !com_sv_running->integer ) { + // if disconnected, bring up the menu + S_StopAllSounds(); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + } + + // if recording an avi, lock to a fixed fps + if ( cl_avidemo->integer && msec ) { + // save the current screen + if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer ) { + Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" ); + } + // fixed time for next frame + msec = ( 1000 / cl_avidemo->integer ) * com_timescale->value; + if ( msec == 0 ) { + msec = 1; + } + } + + // save the msec before checking pause + cls.realFrametime = msec; + + // decide the simulation time + cls.frametime = msec; + + cls.realtime += cls.frametime; + + if ( cl_timegraph->integer ) { + SCR_DebugGraph( cls.realFrametime * 0.25, 0 ); + } + + // see if we need to update any userinfo + CL_CheckUserinfo(); + + // if we haven't gotten a packet in a long time, + // drop the connection + CL_CheckTimeout(); + + // wwwdl download may survive a server disconnect + if ( ( cls.state == CA_CONNECTED && clc.bWWWDl ) || cls.bWWWDlDisconnected ) { + CL_WWWDownload(); + } + + // send intentions now + CL_SendCmd(); + + // resend a connection request if necessary + CL_CheckForResend(); + + // decide on the serverTime to render + CL_SetCGameTime(); + + // update the screen + SCR_UpdateScreen(); + + // update the sound + S_Update(); + + // advance local effects for next frame + SCR_RunCinematic(); + + Con_RunConsole(); + + cls.framecount++; +} + + +//============================================================================ +// Ridah, startup-caching system +typedef struct { + char name[MAX_QPATH]; + int hits; + int lastSetIndex; +} cacheItem_t; +typedef enum { + CACHE_SOUNDS, + CACHE_MODELS, + CACHE_IMAGES, + + CACHE_NUMGROUPS +} cacheGroup_t; +static cacheItem_t cacheGroups[CACHE_NUMGROUPS] = { + {{'s','o','u','n','d',0}, CACHE_SOUNDS}, + {{'m','o','d','e','l',0}, CACHE_MODELS}, + {{'i','m','a','g','e',0}, CACHE_IMAGES}, +}; +#define MAX_CACHE_ITEMS 4096 +#define CACHE_HIT_RATIO 0.75 // if hit on this percentage of maps, it'll get cached + +static int cacheIndex; +static cacheItem_t cacheItems[CACHE_NUMGROUPS][MAX_CACHE_ITEMS]; + +static void CL_Cache_StartGather_f( void ) { + cacheIndex = 0; + memset( cacheItems, 0, sizeof( cacheItems ) ); + + Cvar_Set( "cl_cacheGathering", "1" ); +} + +static void CL_Cache_UsedFile_f( void ) { + char groupStr[MAX_QPATH]; + char itemStr[MAX_QPATH]; + int i,group; + cacheItem_t *item; + + if ( Cmd_Argc() < 2 ) { + Com_Error( ERR_DROP, "usedfile without enough parameters\n" ); + return; + } + + strcpy( groupStr, Cmd_Argv( 1 ) ); + + strcpy( itemStr, Cmd_Argv( 2 ) ); + for ( i = 3; i < Cmd_Argc(); i++ ) { + strcat( itemStr, " " ); + strcat( itemStr, Cmd_Argv( i ) ); + } + Q_strlwr( itemStr ); + + // find the cache group + for ( i = 0; i < CACHE_NUMGROUPS; i++ ) { + if ( !Q_strncmp( groupStr, cacheGroups[i].name, MAX_QPATH ) ) { + break; + } + } + if ( i == CACHE_NUMGROUPS ) { + Com_Error( ERR_DROP, "usedfile without a valid cache group\n" ); + return; + } + + // see if it's already there + group = i; + for ( i = 0, item = cacheItems[group]; i < MAX_CACHE_ITEMS; i++, item++ ) { + if ( !item->name[0] ) { + // didn't find it, so add it here + Q_strncpyz( item->name, itemStr, MAX_QPATH ); + if ( cacheIndex > 9999 ) { // hack, but yeh + item->hits = cacheIndex; + } else { + item->hits++; + } + item->lastSetIndex = cacheIndex; + break; + } + if ( item->name[0] == itemStr[0] && !Q_strncmp( item->name, itemStr, MAX_QPATH ) ) { + if ( item->lastSetIndex != cacheIndex ) { + item->hits++; + item->lastSetIndex = cacheIndex; + } + break; + } + } +} + +static void CL_Cache_SetIndex_f( void ) { + if ( Cmd_Argc() < 2 ) { + Com_Error( ERR_DROP, "setindex needs an index\n" ); + return; + } + + cacheIndex = atoi( Cmd_Argv( 1 ) ); +} + +static void CL_Cache_MapChange_f( void ) { + cacheIndex++; +} + +static void CL_Cache_EndGather_f( void ) { + // save the frequently used files to the cache list file + int i, j, handle, cachePass; + char filename[MAX_QPATH]; + + cachePass = (int)floor( (float)cacheIndex * CACHE_HIT_RATIO ); + + for ( i = 0; i < CACHE_NUMGROUPS; i++ ) { + Q_strncpyz( filename, cacheGroups[i].name, MAX_QPATH ); + Q_strcat( filename, MAX_QPATH, ".cache" ); + + handle = FS_FOpenFileWrite( filename ); + + for ( j = 0; j < MAX_CACHE_ITEMS; j++ ) { + // if it's a valid filename, and it's been hit enough times, cache it + if ( cacheItems[i][j].hits >= cachePass && strstr( cacheItems[i][j].name, "/" ) ) { + FS_Write( cacheItems[i][j].name, strlen( cacheItems[i][j].name ), handle ); + FS_Write( "\n", 1, handle ); + } + } + + FS_FCloseFile( handle ); + } + + Cvar_Set( "cl_cacheGathering", "0" ); +} + +// done. +//============================================================================ + +/* +================ +CL_SetRecommended_f +================ +*/ +void CL_SetRecommended_f( void ) { + Com_SetRecommended(); +} + + + +/* +================ +CL_RefPrintf + +DLL glue +================ +*/ +void QDECL CL_RefPrintf( int print_level, const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start( argptr,fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + if ( print_level == PRINT_ALL ) { + Com_Printf( "%s", msg ); + } else if ( print_level == PRINT_WARNING ) { + Com_Printf( S_COLOR_YELLOW "%s", msg ); // yellow + } else if ( print_level == PRINT_DEVELOPER ) { + Com_DPrintf( S_COLOR_RED "%s", msg ); // red + } +} + + + +/* +============ +CL_ShutdownRef +============ +*/ +void CL_ShutdownRef( void ) { + if ( !re.Shutdown ) { + return; + } + re.Shutdown( qtrue ); + memset( &re, 0, sizeof( re ) ); +} + +/* +============ +CL_InitRenderer +============ +*/ +void CL_InitRenderer( void ) { + // this sets up the renderer and calls R_Init + re.BeginRegistration( &cls.glconfig ); + + // load character sets + cls.charSetShader = re.RegisterShader( "gfx/2d/consolechars" ); + cls.whiteShader = re.RegisterShader( "white" ); + +// JPW NERVE + + cls.consoleShader = re.RegisterShader( "console-16bit" ); // JPW NERVE shader works with 16bit + cls.consoleShader2 = re.RegisterShader( "console2-16bit" ); // JPW NERVE same + + g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2; + g_consoleField.widthInChars = g_console_field_width; +} + +/* +============================ +CL_StartHunkUsers + +After the server has cleared the hunk, these will need to be restarted +This is the only place that any of these functions are called from +============================ +*/ +void CL_StartHunkUsers( void ) { + if ( !com_cl_running ) { + return; + } + + if ( !com_cl_running->integer ) { + return; + } + + if ( !cls.rendererStarted ) { + cls.rendererStarted = qtrue; + CL_InitRenderer(); + } + + if ( !cls.soundStarted ) { + cls.soundStarted = qtrue; + S_Init(); + } + + if ( !cls.soundRegistered ) { + cls.soundRegistered = qtrue; + S_BeginRegistration(); + } + + if ( !cls.uiStarted ) { + cls.uiStarted = qtrue; + CL_InitUI(); + } +} + +// DHM - Nerve +void CL_CheckAutoUpdate( void ) { +#ifndef PRE_RELEASE_DEMO + + if ( !cl_autoupdate->integer ) { + return; + } + + // Only check once per session + if ( autoupdateChecked ) { + return; + } + + srand( Com_Milliseconds() ); + + // Resolve update server + if ( !NET_StringToAdr( cls.autoupdateServerNames[0], &cls.autoupdateServer ) ) { + Com_DPrintf( "Failed to resolve any Auto-update servers.\n" ); + + cls.autoUpdateServerChecked[0] = qtrue; + + autoupdateChecked = qtrue; + return; + } + + cls.autoupdatServerIndex = 0; + + cls.autoupdatServerFirstIndex = cls.autoupdatServerIndex; + + cls.autoUpdateServerChecked[cls.autoupdatServerIndex] = qtrue; + + cls.autoupdateServer.port = BigShort( PORT_SERVER ); + Com_DPrintf( "autoupdate server at: %i.%i.%i.%i:%i\n", cls.autoupdateServer.ip[0], cls.autoupdateServer.ip[1], + cls.autoupdateServer.ip[2], cls.autoupdateServer.ip[3], + BigShort( cls.autoupdateServer.port ) ); + + NET_OutOfBandPrint( NS_CLIENT, cls.autoupdateServer, "getUpdateInfo \"%s\" \"%s\"\n", Q3_VERSION, CPUSTRING ); + +#endif // !PRE_RELEASE_DEMO + + CL_RequestMotd(); + + autoupdateChecked = qtrue; +} + +qboolean CL_NextUpdateServer( void ) { + char *servername; + +#ifdef PRE_RELEASE_DEMO + return qfalse; +#endif // PRE_RELEASE_DEMO + + if ( !cl_autoupdate->integer ) { + return qfalse; + } + +#ifdef _DEBUG + Com_Printf( S_COLOR_MAGENTA "Autoupdate hardcoded OFF in debug build\n" ); + return qfalse; +#endif + + while ( cls.autoUpdateServerChecked[cls.autoupdatServerFirstIndex] ) { + cls.autoupdatServerIndex++; + + if ( cls.autoupdatServerIndex > MAX_AUTOUPDATE_SERVERS ) { + cls.autoupdatServerIndex = 0; + } + + if ( cls.autoupdatServerIndex == cls.autoupdatServerFirstIndex ) { + // went through all of them already + return qfalse; + } + } + + servername = cls.autoupdateServerNames[cls.autoupdatServerIndex]; + + Com_DPrintf( "Resolving AutoUpdate Server... " ); + if ( !NET_StringToAdr( servername, &cls.autoupdateServer ) ) { + Com_DPrintf( "Couldn't resolve address, trying next one..." ); + + cls.autoUpdateServerChecked[cls.autoupdatServerIndex] = qtrue; + + return CL_NextUpdateServer(); + } + + cls.autoUpdateServerChecked[cls.autoupdatServerIndex] = qtrue; + + cls.autoupdateServer.port = BigShort( PORT_SERVER ); + Com_DPrintf( "%i.%i.%i.%i:%i\n", cls.autoupdateServer.ip[0], cls.autoupdateServer.ip[1], + cls.autoupdateServer.ip[2], cls.autoupdateServer.ip[3], + BigShort( cls.autoupdateServer.port ) ); + + return qtrue; +} + +void CL_GetAutoUpdate( void ) { + + // Don't try and get an update if we haven't checked for one + if ( !autoupdateChecked ) { + return; + } + + // Make sure there's a valid update file to request + if ( strlen( cl_updatefiles->string ) < 5 ) { + return; + } + + Com_DPrintf( "Connecting to auto-update server...\n" ); + + S_StopAllSounds(); // NERVE - SMF + + // starting to load a map so we get out of full screen ui mode + Cvar_Set( "r_uiFullScreen", "0" ); + + // toggle on all the download related cvars + Cvar_Set( "cl_allowDownload", "1" ); // general flag + Cvar_Set( "cl_wwwDownload", "1" ); // ftp/http support + + // clear any previous "server full" type messages + clc.serverMessage[0] = 0; + + if ( com_sv_running->integer ) { + // if running a local server, kill it + SV_Shutdown( "Server quit\n" ); + } + + // make sure a local server is killed + Cvar_Set( "sv_killserver", "1" ); + SV_Frame( 0 ); + + CL_Disconnect( qtrue ); + Con_Close(); + + Q_strncpyz( cls.servername, "Auto-Updater", sizeof( cls.servername ) ); + + if ( cls.autoupdateServer.type == NA_BAD ) { + Com_Printf( "Bad server address\n" ); + cls.state = CA_DISCONNECTED; + Cvar_Set( "ui_connecting", "0" ); + return; + } + + // Copy auto-update server address to Server connect address + memcpy( &clc.serverAddress, &cls.autoupdateServer, sizeof( netadr_t ) ); + + Com_DPrintf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername, + clc.serverAddress.ip[0], clc.serverAddress.ip[1], + clc.serverAddress.ip[2], clc.serverAddress.ip[3], + BigShort( clc.serverAddress.port ) ); + + cls.state = CA_CONNECTING; + + cls.keyCatchers = 0; + clc.connectTime = -99999; // CL_CheckForResend() will fire immediately + clc.connectPacketCount = 0; + + // server connection string + Cvar_Set( "cl_currentServerAddress", "Auto-Updater" ); +} +// DHM - Nerve + +/* +============ +CL_RefMalloc +============ +*/ +#ifdef ZONE_DEBUG +void *CL_RefMallocDebug( int size, char *label, char *file, int line ) { + return Z_TagMallocDebug( size, TAG_RENDERER, label, file, line ); +} +#else +void *CL_RefMalloc( int size ) { + return Z_TagMalloc( size, TAG_RENDERER ); +} +#endif + +/* +============ +CL_RefTagFree +============ +*/ +void CL_RefTagFree( void ) { + Z_FreeTags( TAG_RENDERER ); + return; +} + +int CL_ScaledMilliseconds( void ) { + return Sys_Milliseconds() * com_timescale->value; +} + +/* +============ +CL_InitRef +============ +*/ +void CL_InitRef( void ) { + refimport_t ri; + refexport_t *ret; + + Com_Printf( "----- Initializing Renderer ----\n" ); + + ri.Cmd_AddCommand = Cmd_AddCommand; + ri.Cmd_RemoveCommand = Cmd_RemoveCommand; + ri.Cmd_Argc = Cmd_Argc; + ri.Cmd_Argv = Cmd_Argv; + ri.Cmd_ExecuteText = Cbuf_ExecuteText; + ri.Printf = CL_RefPrintf; + ri.Error = Com_Error; + ri.Milliseconds = CL_ScaledMilliseconds; +#ifdef ZONE_DEBUG + ri.Z_MallocDebug = CL_RefMallocDebug; +#else + ri.Z_Malloc = CL_RefMalloc; +#endif + ri.Free = Z_Free; + ri.Tag_Free = CL_RefTagFree; + ri.Hunk_Clear = Hunk_ClearToMark; +#ifdef HUNK_DEBUG + ri.Hunk_AllocDebug = Hunk_AllocDebug; +#else + ri.Hunk_Alloc = Hunk_Alloc; +#endif + ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory; + ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory; + ri.CM_DrawDebugSurface = CM_DrawDebugSurface; + ri.FS_ReadFile = FS_ReadFile; + ri.FS_FreeFile = FS_FreeFile; + ri.FS_WriteFile = FS_WriteFile; + ri.FS_FreeFileList = FS_FreeFileList; + ri.FS_ListFiles = FS_ListFiles; + ri.FS_FileIsInPAK = FS_FileIsInPAK; + ri.FS_FileExists = FS_FileExists; + ri.Cvar_Get = Cvar_Get; + ri.Cvar_Set = Cvar_Set; + + // cinematic stuff + + ri.CIN_UploadCinematic = CIN_UploadCinematic; + ri.CIN_PlayCinematic = CIN_PlayCinematic; + ri.CIN_RunCinematic = CIN_RunCinematic; + + ret = GetRefAPI( REF_API_VERSION, &ri ); + + Com_Printf( "-------------------------------\n" ); + + if ( !ret ) { + Com_Error( ERR_FATAL, "Couldn't initialize refresh" ); + } + + re = *ret; + + // unpause so the cgame definately gets a snapshot and renders a frame + Cvar_Set( "cl_paused", "0" ); +} + +// RF, trap manual client damage commands so users can't issue them manually +void CL_ClientDamageCommand( void ) { + // do nothing +} + +// NERVE - SMF +/*void CL_startSingleplayer_f( void ) { +#if defined(__linux__) + Sys_StartProcess( "./wolfsp.x86", qtrue ); +#else + Sys_StartProcess( "WolfSP.exe", qtrue ); +#endif +}*/ + +// NERVE - SMF +// fretn unused +#if 0 +void CL_buyNow_f( void ) { + Sys_OpenURL( "http://www.activision.com/games/wolfenstein/purchase.html", qtrue ); +} + +// NERVE - SMF +void CL_singlePlayLink_f( void ) { + Sys_OpenURL( "http://www.activision.com/games/wolfenstein/home.html", qtrue ); +} +#endif + +#if !defined( __MACOS__ ) +void CL_SaveTranslations_f( void ) { + CL_SaveTransTable( "scripts/translation.cfg", qfalse ); +} + +void CL_SaveNewTranslations_f( void ) { + char fileName[512]; + + if ( Cmd_Argc() != 2 ) { + Com_Printf( "usage: SaveNewTranslations \n" ); + return; + } + + strcpy( fileName, va( "translations/%s.cfg", Cmd_Argv( 1 ) ) ); + + CL_SaveTransTable( fileName, qtrue ); +} + +void CL_LoadTranslations_f( void ) { + CL_ReloadTranslation(); +} +// -NERVE - SMF +#endif + +//=========================================================================================== + +/* +==================== +CL_Init +==================== +*/ +void CL_Init( void ) { + Com_Printf( "----- Client Initialization -----\n" ); + + Con_Init(); + + CL_ClearState(); + + cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED + + cls.realtime = 0; + + CL_InitInput(); + + // + // register our variables + // + cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); + cl_motd = Cvar_Get( "cl_motd", "1", 0 ); + cl_autoupdate = Cvar_Get( "cl_autoupdate", "1", CVAR_ARCHIVE ); + + cl_timeout = Cvar_Get( "cl_timeout", "200", 0 ); + + cl_wavefilerecord = Cvar_Get( "cl_wavefilerecord", "0", CVAR_TEMP ); + + cl_timeNudge = Cvar_Get( "cl_timeNudge", "0", CVAR_TEMP ); + cl_shownet = Cvar_Get( "cl_shownet", "0", CVAR_TEMP ); + cl_shownuments = Cvar_Get( "cl_shownuments", "0", CVAR_TEMP ); + cl_visibleClients = Cvar_Get( "cl_visibleClients", "0", CVAR_TEMP ); + cl_showServerCommands = Cvar_Get( "cl_showServerCommands", "0", 0 ); + cl_showSend = Cvar_Get( "cl_showSend", "0", CVAR_TEMP ); + cl_showTimeDelta = Cvar_Get( "cl_showTimeDelta", "0", CVAR_TEMP ); + cl_freezeDemo = Cvar_Get( "cl_freezeDemo", "0", CVAR_TEMP ); + rcon_client_password = Cvar_Get( "rconPassword", "", CVAR_TEMP ); + cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); + cl_autorecord = Cvar_Get( "cl_autorecord", "0", CVAR_TEMP ); + + cl_timedemo = Cvar_Get( "timedemo", "0", 0 ); + cl_avidemo = Cvar_Get( "cl_avidemo", "0", 0 ); + cl_forceavidemo = Cvar_Get( "cl_forceavidemo", "0", 0 ); + + rconAddress = Cvar_Get( "rconAddress", "", 0 ); + + cl_yawspeed = Cvar_Get( "cl_yawspeed", "140", CVAR_ARCHIVE ); + cl_pitchspeed = Cvar_Get( "cl_pitchspeed", "140", CVAR_ARCHIVE ); + cl_anglespeedkey = Cvar_Get( "cl_anglespeedkey", "1.5", 0 ); + + cl_maxpackets = Cvar_Get( "cl_maxpackets", "30", CVAR_ARCHIVE ); + cl_packetdup = Cvar_Get( "cl_packetdup", "1", CVAR_ARCHIVE ); + + cl_run = Cvar_Get( "cl_run", "1", CVAR_ARCHIVE ); + cl_sensitivity = Cvar_Get( "sensitivity", "5", CVAR_ARCHIVE ); + cl_mouseAccel = Cvar_Get( "cl_mouseAccel", "0", CVAR_ARCHIVE ); + cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); + + cl_showMouseRate = Cvar_Get( "cl_showmouserate", "0", 0 ); + + cl_allowDownload = Cvar_Get( "cl_allowDownload", "1", CVAR_ARCHIVE ); + cl_wwwDownload = Cvar_Get( "cl_wwwDownload", "1", CVAR_USERINFO | CVAR_ARCHIVE ); + + cl_profile = Cvar_Get( "cl_profile", "", CVAR_ROM ); + cl_defaultProfile = Cvar_Get( "cl_defaultProfile", "", CVAR_ROM ); + + // init autoswitch so the ui will have it correctly even + // if the cgame hasn't been started + // -NERVE - SMF - disabled autoswitch by default + Cvar_Get( "cg_autoswitch", "0", CVAR_ARCHIVE ); + + // Rafael - particle switch + Cvar_Get( "cg_wolfparticles", "1", CVAR_ARCHIVE ); + // done + + cl_conXOffset = Cvar_Get( "cl_conXOffset", "0", 0 ); + cl_inGameVideo = Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); + + cl_serverStatusResendTime = Cvar_Get( "cl_serverStatusResendTime", "750", 0 ); + + // RF + cl_recoilPitch = Cvar_Get( "cg_recoilPitch", "0", CVAR_ROM ); + + cl_bypassMouseInput = Cvar_Get( "cl_bypassMouseInput", "0", 0 ); //CVAR_ROM ); // NERVE - SMF + + cl_doubletapdelay = Cvar_Get( "cl_doubletapdelay", "350", CVAR_ARCHIVE ); // Arnout: double tap + + m_pitch = Cvar_Get( "m_pitch", "0.022", CVAR_ARCHIVE ); + m_yaw = Cvar_Get( "m_yaw", "0.022", CVAR_ARCHIVE ); + m_forward = Cvar_Get( "m_forward", "0.25", CVAR_ARCHIVE ); + m_side = Cvar_Get( "m_side", "0.25", CVAR_ARCHIVE ); + m_filter = Cvar_Get( "m_filter", "0", CVAR_ARCHIVE ); + + cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM ); + + //bani - make these cvars visible to cgame + cl_demorecording = Cvar_Get( "cl_demorecording", "0", CVAR_ROM ); + cl_demofilename = Cvar_Get( "cl_demofilename", "", CVAR_ROM ); + cl_demooffset = Cvar_Get( "cl_demooffset", "0", CVAR_ROM ); + cl_waverecording = Cvar_Get( "cl_waverecording", "0", CVAR_ROM ); + cl_wavefilename = Cvar_Get( "cl_wavefilename", "", CVAR_ROM ); + cl_waveoffset = Cvar_Get( "cl_waveoffset", "0", CVAR_ROM ); + + //bani + cl_packetloss = Cvar_Get( "cl_packetloss", "0", CVAR_CHEAT ); + cl_packetdelay = Cvar_Get( "cl_packetdelay", "0", CVAR_CHEAT ); + + Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE ); + + // NERVE - SMF + Cvar_Get( "cg_drawCompass", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_drawNotifyText", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_quickMessageAlt", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_popupLimboMenu", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_descriptiveText", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_drawTeamOverlay", "2", CVAR_ARCHIVE ); +// Cvar_Get( "cg_uselessNostalgia", "0", CVAR_ARCHIVE ); // JPW NERVE + Cvar_Get( "cg_drawGun", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_cursorHints", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_voiceSpriteTime", "6000", CVAR_ARCHIVE ); +// Cvar_Get( "cg_teamChatsOnly", "0", CVAR_ARCHIVE ); +// Cvar_Get( "cg_noVoiceChats", "0", CVAR_ARCHIVE ); +// Cvar_Get( "cg_noVoiceText", "0", CVAR_ARCHIVE ); + Cvar_Get( "cg_crosshairSize", "48", CVAR_ARCHIVE ); + Cvar_Get( "cg_drawCrosshair", "1", CVAR_ARCHIVE ); + Cvar_Get( "cg_zoomDefaultSniper", "20", CVAR_ARCHIVE ); + Cvar_Get( "cg_zoomstepsniper", "2", CVAR_ARCHIVE ); + +// Cvar_Get( "mp_playerType", "0", 0 ); +// Cvar_Get( "mp_currentPlayerType", "0", 0 ); +// Cvar_Get( "mp_weapon", "0", 0 ); +// Cvar_Get( "mp_team", "0", 0 ); +// Cvar_Get( "mp_currentTeam", "0", 0 ); + // -NERVE - SMF + + // userinfo + Cvar_Get( "name", "ETPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get( "rate", "5000", CVAR_USERINFO | CVAR_ARCHIVE ); // NERVE - SMF - changed from 3000 + Cvar_Get( "snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); +// Cvar_Get ("model", "american", CVAR_USERINFO | CVAR_ARCHIVE ); // temp until we have an skeletal american model +// Arnout - no need // Cvar_Get ("model", "multi", CVAR_USERINFO | CVAR_ARCHIVE ); +// Arnout - no need // Cvar_Get ("head", "default", CVAR_USERINFO | CVAR_ARCHIVE ); +// Arnout - no need // Cvar_Get ("color", "4", CVAR_USERINFO | CVAR_ARCHIVE ); +// Arnout - no need // Cvar_Get ("handicap", "0", CVAR_USERINFO | CVAR_ARCHIVE ); +// Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); + Cvar_Get( "cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE ); + + Cvar_Get( "password", "", CVAR_USERINFO ); + Cvar_Get( "cg_predictItems", "1", CVAR_ARCHIVE ); + +//----(SA) added + Cvar_Get( "cg_autoactivate", "1", CVAR_ARCHIVE ); +//----(SA) end + + // cgame might not be initialized before menu is used + Cvar_Get( "cg_viewsize", "100", CVAR_ARCHIVE ); + + Cvar_Get( "cg_autoReload", "1", CVAR_ARCHIVE ); + + cl_missionStats = Cvar_Get( "g_missionStats", "0", CVAR_ROM ); + cl_waitForFire = Cvar_Get( "cl_waitForFire", "0", CVAR_ROM ); + + // NERVE - SMF - localization + cl_language = Cvar_Get( "cl_language", "0", CVAR_ARCHIVE ); + cl_debugTranslation = Cvar_Get( "cl_debugTranslation", "0", 0 ); + // -NERVE - SMF + + // DHM - Nerve :: Auto-update + cl_updateavailable = Cvar_Get( "cl_updateavailable", "0", CVAR_ROM ); + cl_updatefiles = Cvar_Get( "cl_updatefiles", "", CVAR_ROM ); + + Q_strncpyz( cls.autoupdateServerNames[0], AUTOUPDATE_SERVER1_NAME, MAX_QPATH ); + Q_strncpyz( cls.autoupdateServerNames[1], AUTOUPDATE_SERVER2_NAME, MAX_QPATH ); + Q_strncpyz( cls.autoupdateServerNames[2], AUTOUPDATE_SERVER3_NAME, MAX_QPATH ); + Q_strncpyz( cls.autoupdateServerNames[3], AUTOUPDATE_SERVER4_NAME, MAX_QPATH ); + Q_strncpyz( cls.autoupdateServerNames[4], AUTOUPDATE_SERVER5_NAME, MAX_QPATH ); + // DHM - Nerve + + // + // register our commands + // + Cmd_AddCommand( "cmd", CL_ForwardToServer_f ); + Cmd_AddCommand( "configstrings", CL_Configstrings_f ); + Cmd_AddCommand( "clientinfo", CL_Clientinfo_f ); + Cmd_AddCommand( "snd_reload", CL_Snd_Reload_f ); + Cmd_AddCommand( "snd_restart", CL_Snd_Restart_f ); + Cmd_AddCommand( "vid_restart", CL_Vid_Restart_f ); + Cmd_AddCommand( "ui_restart", CL_UI_Restart_f ); // NERVE - SMF + Cmd_AddCommand( "disconnect", CL_Disconnect_f ); + Cmd_AddCommand( "record", CL_Record_f ); + Cmd_AddCommand( "demo", CL_PlayDemo_f ); + Cmd_AddCommand( "cinematic", CL_PlayCinematic_f ); + Cmd_AddCommand( "stoprecord", CL_StopRecord_f ); + Cmd_AddCommand( "connect", CL_Connect_f ); + Cmd_AddCommand( "reconnect", CL_Reconnect_f ); + Cmd_AddCommand( "localservers", CL_LocalServers_f ); + Cmd_AddCommand( "globalservers", CL_GlobalServers_f ); + Cmd_AddCommand( "rcon", CL_Rcon_f ); + Cmd_AddCommand( "setenv", CL_Setenv_f ); + Cmd_AddCommand( "ping", CL_Ping_f ); + Cmd_AddCommand( "serverstatus", CL_ServerStatus_f ); + Cmd_AddCommand( "showip", CL_ShowIP_f ); + Cmd_AddCommand( "fs_openedList", CL_OpenedPK3List_f ); + Cmd_AddCommand( "fs_referencedList", CL_ReferencedPK3List_f ); + + // Ridah, startup-caching system + Cmd_AddCommand( "cache_startgather", CL_Cache_StartGather_f ); + Cmd_AddCommand( "cache_usedfile", CL_Cache_UsedFile_f ); + Cmd_AddCommand( "cache_setindex", CL_Cache_SetIndex_f ); + Cmd_AddCommand( "cache_mapchange", CL_Cache_MapChange_f ); + Cmd_AddCommand( "cache_endgather", CL_Cache_EndGather_f ); + + Cmd_AddCommand( "updatehunkusage", CL_UpdateLevelHunkUsage ); + Cmd_AddCommand( "updatescreen", SCR_UpdateScreen ); + // done. +#ifndef __MACOS__ //DAJ USA + Cmd_AddCommand( "SaveTranslations", CL_SaveTranslations_f ); // NERVE - SMF - localization + Cmd_AddCommand( "SaveNewTranslations", CL_SaveNewTranslations_f ); // NERVE - SMF - localization + Cmd_AddCommand( "LoadTranslations", CL_LoadTranslations_f ); // NERVE - SMF - localization +#endif + // NERVE - SMF - don't do this in multiplayer + // RF, add this command so clients can't bind a key to send client damage commands to the server +// Cmd_AddCommand ("cld", CL_ClientDamageCommand ); + +// Cmd_AddCommand ( "startSingleplayer", CL_startSingleplayer_f ); // NERVE - SMF +// fretn - unused +// Cmd_AddCommand ( "buyNow", CL_buyNow_f ); // NERVE - SMF +// Cmd_AddCommand ( "singlePlayLink", CL_singlePlayLink_f ); // NERVE - SMF + + Cmd_AddCommand( "setRecommended", CL_SetRecommended_f ); + + //bani - we eat these commands to prevent exploits + Cmd_AddCommand( "userinfo", CL_EatMe_f ); + + Cmd_AddCommand( "wav_record", CL_WavRecord_f ); + Cmd_AddCommand( "wav_stoprecord", CL_WavStopRecord_f ); + + CL_InitRef(); + + SCR_Init(); + + Cbuf_Execute(); + + Cvar_Set( "cl_running", "1" ); + + // DHM - Nerve + autoupdateChecked = qfalse; + autoupdateStarted = qfalse; + +#ifndef __MACOS__ //DAJ USA + CL_InitTranslation(); // NERVE - SMF - localization +#endif + + Com_Printf( "----- Client Initialization Complete -----\n" ); +} + + +/* +=============== +CL_Shutdown + +=============== +*/ +void CL_Shutdown( void ) { + static qboolean recursive = qfalse; + + Com_Printf( "----- CL_Shutdown -----\n" ); + + if ( recursive ) { + printf( "recursive shutdown\n" ); + return; + } + recursive = qtrue; + + if ( clc.waverecording ) { // fretn - write wav header when we quit + CL_WavStopRecord_f(); + } + + CL_Disconnect( qtrue ); + + S_Shutdown(); + DL_Shutdown(); + CL_ShutdownRef(); + + CL_ShutdownUI(); + + Cmd_RemoveCommand( "cmd" ); + Cmd_RemoveCommand( "configstrings" ); + Cmd_RemoveCommand( "userinfo" ); + Cmd_RemoveCommand( "snd_reload" ); + Cmd_RemoveCommand( "snd_restart" ); + Cmd_RemoveCommand( "vid_restart" ); + Cmd_RemoveCommand( "disconnect" ); + Cmd_RemoveCommand( "record" ); + Cmd_RemoveCommand( "demo" ); + Cmd_RemoveCommand( "cinematic" ); + Cmd_RemoveCommand( "stoprecord" ); + Cmd_RemoveCommand( "connect" ); + Cmd_RemoveCommand( "localservers" ); + Cmd_RemoveCommand( "globalservers" ); + Cmd_RemoveCommand( "rcon" ); + Cmd_RemoveCommand( "setenv" ); + Cmd_RemoveCommand( "ping" ); + Cmd_RemoveCommand( "serverstatus" ); + Cmd_RemoveCommand( "showip" ); + Cmd_RemoveCommand( "model" ); + + // Ridah, startup-caching system + Cmd_RemoveCommand( "cache_startgather" ); + Cmd_RemoveCommand( "cache_usedfile" ); + Cmd_RemoveCommand( "cache_setindex" ); + Cmd_RemoveCommand( "cache_mapchange" ); + Cmd_RemoveCommand( "cache_endgather" ); + + Cmd_RemoveCommand( "updatehunkusage" ); + Cmd_RemoveCommand( "wav_record" ); + Cmd_RemoveCommand( "wav_stoprecord" ); + // done. + + Cvar_Set( "cl_running", "0" ); + + recursive = qfalse; + + memset( &cls, 0, sizeof( cls ) ); + + Com_Printf( "-----------------------\n" ); +} + + +static void CL_SetServerInfo( serverInfo_t *server, const char *info, int ping ) { + if ( server ) { + if ( info ) { + server->clients = atoi( Info_ValueForKey( info, "clients" ) ); + Q_strncpyz( server->hostName,Info_ValueForKey( info, "hostname" ), MAX_NAME_LENGTH ); + server->load = atoi( Info_ValueForKey( info, "serverload" ) ); + Q_strncpyz( server->mapName, Info_ValueForKey( info, "mapname" ), MAX_NAME_LENGTH ); + server->maxClients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + Q_strncpyz( server->game,Info_ValueForKey( info, "game" ), MAX_NAME_LENGTH ); + server->gameType = atoi( Info_ValueForKey( info, "gametype" ) ); + server->netType = atoi( Info_ValueForKey( info, "nettype" ) ); + server->minPing = atoi( Info_ValueForKey( info, "minping" ) ); + server->maxPing = atoi( Info_ValueForKey( info, "maxping" ) ); + server->allowAnonymous = atoi( Info_ValueForKey( info, "sv_allowAnonymous" ) ); + server->friendlyFire = atoi( Info_ValueForKey( info, "friendlyFire" ) ); // NERVE - SMF + server->maxlives = atoi( Info_ValueForKey( info, "maxlives" ) ); // NERVE - SMF + server->needpass = atoi( Info_ValueForKey( info, "needpass" ) ); // NERVE - SMF + server->punkbuster = atoi( Info_ValueForKey( info, "punkbuster" ) ); // DHM - Nerve + Q_strncpyz( server->gameName, Info_ValueForKey( info, "gamename" ), MAX_NAME_LENGTH ); // Arnout + server->antilag = atoi( Info_ValueForKey( info, "g_antilag" ) ); + server->weaprestrict = atoi( Info_ValueForKey( info, "weaprestrict" ) ); + server->balancedteams = atoi( Info_ValueForKey( info, "balancedteams" ) ); + } + server->ping = ping; + } +} + +static void CL_SetServerInfoByAddress( netadr_t from, const char *info, int ping ) { + int i; + + for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) { + if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) { + CL_SetServerInfo( &cls.localServers[i], info, ping ); + } + } + + for ( i = 0; i < MAX_GLOBAL_SERVERS; i++ ) { + if ( NET_CompareAdr( from, cls.globalServers[i].adr ) ) { + CL_SetServerInfo( &cls.globalServers[i], info, ping ); + } + } + + for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) { + if ( NET_CompareAdr( from, cls.favoriteServers[i].adr ) ) { + CL_SetServerInfo( &cls.favoriteServers[i], info, ping ); + } + } + +} + +/* +=================== +CL_ServerInfoPacket +=================== +*/ +void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { + int i, type; + char info[MAX_INFO_STRING]; + char* str; + char *infoString; + int prot; + char *gameName; + int debug_protocol; + int protocol = PROTOCOL_VERSION; + + debug_protocol = Cvar_VariableIntegerValue( "debug_protocol" ); + if ( debug_protocol ) { + protocol = debug_protocol; + } + + infoString = MSG_ReadString( msg ); + + // if this isn't the correct protocol version, ignore it + prot = atoi( Info_ValueForKey( infoString, "protocol" ) ); + if ( prot != protocol ) { + Com_DPrintf( "Different protocol info packet: %s\n", infoString ); + return; + } + + // Arnout: if this isn't the correct game, ignore it + gameName = Info_ValueForKey( infoString, "gamename" ); + if ( !gameName[0] || Q_stricmp( gameName, GAMENAME_STRING ) ) { + Com_DPrintf( "Different game info packet: %s\n", infoString ); + return; + } + + // iterate servers waiting for ping response + for ( i = 0; i < MAX_PINGREQUESTS; i++ ) + { + if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) ) { + // calc ping time + cl_pinglist[i].time = cls.realtime - cl_pinglist[i].start + 1; + Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) ); + + // save of info + Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) ); + + // tack on the net type + // NOTE: make sure these types are in sync with the netnames strings in the UI + switch ( from.type ) + { + case NA_BROADCAST: + case NA_IP: + str = "udp"; + type = 1; + break; + + case NA_IPX: + case NA_BROADCAST_IPX: + str = "ipx"; + type = 2; + break; + + default: + str = "???"; + type = 0; + break; + } + Info_SetValueForKey( cl_pinglist[i].info, "nettype", va( "%d", type ) ); + CL_SetServerInfoByAddress( from, infoString, cl_pinglist[i].time ); + + return; + } + } + + // if not just sent a local broadcast or pinging local servers + if ( cls.pingUpdateSource != AS_LOCAL ) { + return; + } + + for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) { + // empty slot + if ( cls.localServers[i].adr.port == 0 ) { + break; + } + + // avoid duplicate + if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) { + return; + } + } + + if ( i == MAX_OTHER_SERVERS ) { + Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" ); + return; + } + + // add this to the list + cls.numlocalservers = i + 1; + cls.localServers[i].adr = from; + cls.localServers[i].clients = 0; + cls.localServers[i].hostName[0] = '\0'; + cls.localServers[i].load = -1; + cls.localServers[i].mapName[0] = '\0'; + cls.localServers[i].maxClients = 0; + cls.localServers[i].maxPing = 0; + cls.localServers[i].minPing = 0; + cls.localServers[i].ping = -1; + cls.localServers[i].game[0] = '\0'; + cls.localServers[i].gameType = 0; + cls.localServers[i].netType = from.type; + cls.localServers[i].allowAnonymous = 0; + cls.localServers[i].friendlyFire = 0; // NERVE - SMF + cls.localServers[i].maxlives = 0; // NERVE - SMF + cls.localServers[i].needpass = 0; + cls.localServers[i].punkbuster = 0; // DHM - Nerve + cls.localServers[i].antilag = 0; + cls.localServers[i].weaprestrict = 0; + cls.localServers[i].balancedteams = 0; + cls.localServers[i].gameName[0] = '\0'; // Arnout + + Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING ); + if ( strlen( info ) ) { + if ( info[strlen( info ) - 1] != '\n' ) { + strncat( info, "\n", sizeof( info ) ); + } + Com_Printf( "%s: %s", NET_AdrToString( from ), info ); + } +} + +/* +=================== +CL_UpdateInfoPacket +=================== +*/ +void CL_UpdateInfoPacket( netadr_t from ) { + + if ( cls.autoupdateServer.type == NA_BAD ) { + Com_DPrintf( "CL_UpdateInfoPacket: Auto-Updater has bad address\n" ); + return; + } + + Com_DPrintf( "Auto-Updater resolved to %i.%i.%i.%i:%i\n", + cls.autoupdateServer.ip[0], cls.autoupdateServer.ip[1], + cls.autoupdateServer.ip[2], cls.autoupdateServer.ip[3], + BigShort( cls.autoupdateServer.port ) ); + + if ( !NET_CompareAdr( from, cls.autoupdateServer ) ) { + Com_DPrintf( "CL_UpdateInfoPacket: Received packet from %i.%i.%i.%i:%i\n", + from.ip[0], from.ip[1], from.ip[2], from.ip[3], + BigShort( from.port ) ); + return; + } + + Cvar_Set( "cl_updateavailable", Cmd_Argv( 1 ) ); + + if ( !Q_stricmp( cl_updateavailable->string, "1" ) ) { + Cvar_Set( "cl_updatefiles", Cmd_Argv( 2 ) ); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_AUTOUPDATE ); + } +} +// DHM - Nerve + +/* +=================== +CL_GetServerStatus +=================== +*/ +serverStatus_t *CL_GetServerStatus( netadr_t from ) { + serverStatus_t *serverStatus; + int i, oldest, oldestTime; + + serverStatus = NULL; + for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) { + if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { + return &cl_serverStatusList[i]; + } + } + for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) { + if ( cl_serverStatusList[i].retrieved ) { + return &cl_serverStatusList[i]; + } + } + oldest = -1; + oldestTime = 0; + for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) { + if ( oldest == -1 || cl_serverStatusList[i].startTime < oldestTime ) { + oldest = i; + oldestTime = cl_serverStatusList[i].startTime; + } + } + if ( oldest != -1 ) { + return &cl_serverStatusList[oldest]; + } + serverStatusCount++; + return &cl_serverStatusList[serverStatusCount & ( MAX_SERVERSTATUSREQUESTS - 1 )]; +} + +/* +=================== +CL_ServerStatus +=================== +*/ +int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) { + int i; + netadr_t to; + serverStatus_t *serverStatus; + + // if no server address then reset all server status requests + if ( !serverAddress ) { + for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) { + cl_serverStatusList[i].address.port = 0; + cl_serverStatusList[i].retrieved = qtrue; + } + return qfalse; + } + // get the address + if ( !NET_StringToAdr( serverAddress, &to ) ) { + return qfalse; + } + serverStatus = CL_GetServerStatus( to ); + // if no server status string then reset the server status request for this address + if ( !serverStatusString ) { + serverStatus->retrieved = qtrue; + return qfalse; + } + + // if this server status request has the same address + if ( NET_CompareAdr( to, serverStatus->address ) ) { + // if we recieved an response for this server status request + if ( !serverStatus->pending ) { + Q_strncpyz( serverStatusString, serverStatus->string, maxLen ); + serverStatus->retrieved = qtrue; + serverStatus->startTime = 0; + return qtrue; + } + // resend the request regularly + else if ( serverStatus->startTime < Sys_Milliseconds() - cl_serverStatusResendTime->integer ) { + serverStatus->print = qfalse; + serverStatus->pending = qtrue; + serverStatus->retrieved = qfalse; + serverStatus->time = 0; + serverStatus->startTime = Sys_Milliseconds(); + NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + return qfalse; + } + } + // if retrieved + else if ( serverStatus->retrieved ) { + serverStatus->address = to; + serverStatus->print = qfalse; + serverStatus->pending = qtrue; + serverStatus->retrieved = qfalse; + serverStatus->startTime = Sys_Milliseconds(); + serverStatus->time = 0; + NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + return qfalse; + } + return qfalse; +} + +/* +=================== +CL_ServerStatusResponse +=================== +*/ +void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) { + char *s; + char info[MAX_INFO_STRING]; + int i, l, score, ping; + int len; + serverStatus_t *serverStatus; + + serverStatus = NULL; + for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) { + if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { + serverStatus = &cl_serverStatusList[i]; + break; + } + } + // if we didn't request this server status + if ( !serverStatus ) { + return; + } + + s = MSG_ReadStringLine( msg ); + + len = 0; + Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "%s", s ); + + if ( serverStatus->print ) { + Com_Printf( "Server settings:\n" ); + // print cvars + while ( *s ) { + for ( i = 0; i < 2 && *s; i++ ) { + if ( *s == '\\' ) { + s++; + } + l = 0; + while ( *s ) { + info[l++] = *s; + if ( l >= MAX_INFO_STRING - 1 ) { + break; + } + s++; + if ( *s == '\\' ) { + break; + } + } + info[l] = '\0'; + if ( i ) { + Com_Printf( "%s\n", info ); + } else { + Com_Printf( "%-24s", info ); + } + } + } + } + + len = strlen( serverStatus->string ); + Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\" ); + + if ( serverStatus->print ) { + Com_Printf( "\nPlayers:\n" ); + Com_Printf( "num: score: ping: name:\n" ); + } + for ( i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++ ) { + + len = strlen( serverStatus->string ); + Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\%s", s ); + + if ( serverStatus->print ) { + score = ping = 0; + sscanf( s, "%d %d", &score, &ping ); + s = strchr( s, ' ' ); + if ( s ) { + s = strchr( s + 1, ' ' ); + } + if ( s ) { + s++; + } else { + s = "unknown"; + } + Com_Printf( "%-2d %-3d %-3d %s\n", i, score, ping, s ); + } + } + len = strlen( serverStatus->string ); + Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\" ); + + serverStatus->time = Sys_Milliseconds(); + serverStatus->address = from; + serverStatus->pending = qfalse; + if ( serverStatus->print ) { + serverStatus->retrieved = qtrue; + } +} + +/* +================== +CL_LocalServers_f +================== +*/ +void CL_LocalServers_f( void ) { + char *message; + int i, j; + netadr_t to; + + Com_Printf( "Scanning for servers on the local network...\n" ); + + // reset the list, waiting for response + cls.numlocalservers = 0; + cls.pingUpdateSource = AS_LOCAL; + + for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) { + qboolean b = cls.localServers[i].visible; + Com_Memset( &cls.localServers[i], 0, sizeof( cls.localServers[i] ) ); + cls.localServers[i].visible = b; + } + Com_Memset( &to, 0, sizeof( to ) ); + + // The 'xxx' in the message is a challenge that will be echoed back + // by the server. We don't care about that here, but master servers + // can use that to prevent spoofed server responses from invalid ip + message = "\377\377\377\377getinfo xxx"; + + // send each message twice in case one is dropped + for ( i = 0 ; i < 2 ; i++ ) { + // send a broadcast packet on each server port + // we support multiple server ports so a single machine + // can nicely run multiple servers + for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) { + to.port = BigShort( (short)( PORT_SERVER + j ) ); + + to.type = NA_BROADCAST; + NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + + to.type = NA_BROADCAST_IPX; + NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); + } + } +} + +/* +================== +CL_GlobalServers_f +================== +*/ +void CL_GlobalServers_f( void ) { + netadr_t to; + int i; + int count; + char *buffptr; + char command[1024]; + + if ( Cmd_Argc() < 3 ) { + Com_Printf( "usage: globalservers [keywords]\n" ); + return; + } + + cls.masterNum = atoi( Cmd_Argv( 1 ) ); + + Com_Printf( "Requesting servers from the master...\n" ); + + // reset the list, waiting for response + // -1 is used to distinguish a "no response" + + if ( cls.masterNum == 0 ) { + NET_StringToAdr( MASTER_SERVER_NAME, &to ); + cls.numglobalservers = -1; + cls.pingUpdateSource = AS_GLOBAL; + } + to.type = NA_IP; + to.port = BigShort( PORT_MASTER ); + + sprintf( command, "getservers %s", Cmd_Argv( 2 ) ); + + // tack on keywords + buffptr = command + strlen( command ); + count = Cmd_Argc(); + for ( i = 3; i < count; i++ ) + buffptr += sprintf( buffptr, " %s", Cmd_Argv( i ) ); + + // if we are a demo, automatically add a "demo" keyword + if ( Cvar_VariableValue( "fs_restrict" ) ) { + buffptr += sprintf( buffptr, " demo" ); + } + + NET_OutOfBandPrint( NS_SERVER, to, command ); +} + + +/* +================== +CL_GetPing +================== +*/ +void CL_GetPing( int n, char *buf, int buflen, int *pingtime ) { + const char *str; + int time; + int maxPing; + + if ( n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port ) { + // empty slot + buf[0] = '\0'; + *pingtime = 0; + return; + } + + str = NET_AdrToString( cl_pinglist[n].adr ); + Q_strncpyz( buf, str, buflen ); + + time = cl_pinglist[n].time; + if ( !time ) { + // check for timeout + time = cls.realtime - cl_pinglist[n].start; + maxPing = Cvar_VariableIntegerValue( "cl_maxPing" ); + if ( maxPing < 100 ) { + maxPing = 100; + } + if ( time < maxPing ) { + // not timed out yet + time = 0; + } + } + + CL_SetServerInfoByAddress( cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time ); + + *pingtime = time; +} + +/* +================== +CL_UpdateServerInfo +================== +*/ +void CL_UpdateServerInfo( int n ) { + if ( n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port ) { + return; + } + + CL_SetServerInfoByAddress( cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time ); +} + +/* +================== +CL_GetPingInfo +================== +*/ +void CL_GetPingInfo( int n, char *buf, int buflen ) { + if ( n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port ) { + // empty slot + if ( buflen ) { + buf[0] = '\0'; + } + return; + } + + Q_strncpyz( buf, cl_pinglist[n].info, buflen ); +} + +/* +================== +CL_ClearPing +================== +*/ +void CL_ClearPing( int n ) { + if ( n < 0 || n >= MAX_PINGREQUESTS ) { + return; + } + + cl_pinglist[n].adr.port = 0; +} + +/* +================== +CL_GetPingQueueCount +================== +*/ +int CL_GetPingQueueCount( void ) { + int i; + int count; + ping_t* pingptr; + + count = 0; + pingptr = cl_pinglist; + + for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ ) { + if ( pingptr->adr.port ) { + count++; + } + } + + return ( count ); +} + +/* +================== +CL_GetFreePing +================== +*/ +ping_t* CL_GetFreePing( void ) { + ping_t* pingptr; + ping_t* best; + int oldest; + int i; + int time; + + pingptr = cl_pinglist; + for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ ) + { + // find free ping slot + if ( pingptr->adr.port ) { + if ( !pingptr->time ) { + if ( cls.realtime - pingptr->start < 500 ) { + // still waiting for response + continue; + } + } else if ( pingptr->time < 500 ) { + // results have not been queried + continue; + } + } + + // clear it + pingptr->adr.port = 0; + return ( pingptr ); + } + + // use oldest entry + pingptr = cl_pinglist; + best = cl_pinglist; + oldest = INT_MIN; + for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ ) + { + // scan for oldest + time = cls.realtime - pingptr->start; + if ( time > oldest ) { + oldest = time; + best = pingptr; + } + } + + return ( best ); +} + +/* +================== +CL_Ping_f +================== +*/ +void CL_Ping_f( void ) { + netadr_t to; + ping_t* pingptr; + char* server; + + if ( Cmd_Argc() != 2 ) { + Com_Printf( "usage: ping [server]\n" ); + return; + } + + memset( &to, 0, sizeof( netadr_t ) ); + + server = Cmd_Argv( 1 ); + + if ( !NET_StringToAdr( server, &to ) ) { + return; + } + + pingptr = CL_GetFreePing(); + + memcpy( &pingptr->adr, &to, sizeof( netadr_t ) ); + pingptr->start = cls.realtime; + pingptr->time = 0; + + CL_SetServerInfoByAddress( pingptr->adr, NULL, 0 ); + + NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" ); +} + +/* +================== +CL_UpdateVisiblePings_f +================== +*/ +qboolean CL_UpdateVisiblePings_f( int source ) { + int slots, i; + char buff[MAX_STRING_CHARS]; + int pingTime; + int max; + qboolean status = qfalse; + + if ( source < 0 || source > AS_FAVORITES ) { + return qfalse; + } + + cls.pingUpdateSource = source; + + slots = CL_GetPingQueueCount(); + if ( slots < MAX_PINGREQUESTS ) { + serverInfo_t *server = NULL; + + max = ( source == AS_GLOBAL ) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS; + switch ( source ) { + case AS_LOCAL: + server = &cls.localServers[0]; + max = cls.numlocalservers; + break; + case AS_GLOBAL: + server = &cls.globalServers[0]; + max = cls.numglobalservers; + break; + case AS_FAVORITES: + server = &cls.favoriteServers[0]; + max = cls.numfavoriteservers; + break; + } + for ( i = 0; i < max; i++ ) { + if ( server[i].visible ) { + if ( server[i].ping == -1 ) { + int j; + + if ( slots >= MAX_PINGREQUESTS ) { + break; + } + for ( j = 0; j < MAX_PINGREQUESTS; j++ ) { + if ( !cl_pinglist[j].adr.port ) { + continue; + } + if ( NET_CompareAdr( cl_pinglist[j].adr, server[i].adr ) ) { + // already on the list + break; + } + } + if ( j >= MAX_PINGREQUESTS ) { + status = qtrue; + for ( j = 0; j < MAX_PINGREQUESTS; j++ ) { + if ( !cl_pinglist[j].adr.port ) { + break; + } + } + memcpy( &cl_pinglist[j].adr, &server[i].adr, sizeof( netadr_t ) ); + cl_pinglist[j].start = cls.realtime; + cl_pinglist[j].time = 0; + NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" ); + slots++; + } + } + // if the server has a ping higher than cl_maxPing or + // the ping packet got lost + else if ( server[i].ping == 0 ) { + // if we are updating global servers + if ( source == AS_GLOBAL ) { + // + if ( cls.numGlobalServerAddresses > 0 ) { + // overwrite this server with one from the additional global servers + cls.numGlobalServerAddresses--; + CL_InitServerInfo( &server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses] ); + // NOTE: the server[i].visible flag stays untouched + } + } + } + } + } + } + + if ( slots ) { + status = qtrue; + } + for ( i = 0; i < MAX_PINGREQUESTS; i++ ) { + if ( !cl_pinglist[i].adr.port ) { + continue; + } + CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime ); + if ( pingTime != 0 ) { + CL_ClearPing( i ); + status = qtrue; + } + } + + return status; +} + +/* +================== +CL_ServerStatus_f +================== +*/ +void CL_ServerStatus_f( void ) { + netadr_t to; + char *server; + serverStatus_t *serverStatus; + + Com_Memset( &to, 0, sizeof( netadr_t ) ); + + if ( Cmd_Argc() != 2 ) { + if ( cls.state != CA_ACTIVE || clc.demoplaying ) { + Com_Printf( "Not connected to a server.\n" ); + Com_Printf( "Usage: serverstatus [server]\n" ); + return; + } + server = cls.servername; + } else { + server = Cmd_Argv( 1 ); + } + + if ( !NET_StringToAdr( server, &to ) ) { + return; + } + + NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); + + serverStatus = CL_GetServerStatus( to ); + serverStatus->address = to; + serverStatus->print = qtrue; + serverStatus->pending = qtrue; +} + +/* +================== +CL_ShowIP_f +================== +*/ +void CL_ShowIP_f( void ) { + Sys_ShowIP(); +} + +/* +================= +bool CL_CDKeyValidate +================= +*/ +qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { + char ch; + byte sum; + char chs[3]; + int i, len; + + len = strlen( key ); + if ( len != CDKEY_LEN ) { + return qfalse; + } + + if ( checksum && strlen( checksum ) != CDCHKSUM_LEN ) { + return qfalse; + } + + sum = 0; + // for loop gets rid of conditional assignment warning + for ( i = 0; i < len; i++ ) { + ch = *key++; + if ( ch >= 'a' && ch <= 'z' ) { + ch -= 32; + } + switch ( ch ) { + case '2': + case '3': + case '7': + case 'A': + case 'B': + case 'C': + case 'D': + case 'G': + case 'H': + case 'J': + case 'L': + case 'P': + case 'R': + case 'S': + case 'T': + case 'W': + sum = ( sum << 1 ) ^ ch; + continue; + default: + return qfalse; + } + } + + + sprintf( chs, "%02x", sum ); + + if ( checksum && !Q_stricmp( chs, checksum ) ) { + return qtrue; + } + + if ( !checksum ) { + return qtrue; + } + + return qfalse; +} + +// NERVE - SMF +/* +======================= +CL_AddToLimboChat + +======================= +*/ +void CL_AddToLimboChat( const char *str ) { + int len; + char *p, *ls; + int lastcolor; + int chatHeight; + int i; + + chatHeight = LIMBOCHAT_HEIGHT; + cl.limboChatPos = LIMBOCHAT_HEIGHT - 1; + len = 0; + + // copy old strings + for ( i = cl.limboChatPos; i > 0; i-- ) { + strcpy( cl.limboChatMsgs[i], cl.limboChatMsgs[i - 1] ); + } + + // copy new string + p = cl.limboChatMsgs[0]; + *p = 0; + + lastcolor = '7'; + + ls = NULL; + while ( *str ) { + if ( len > LIMBOCHAT_WIDTH - 1 ) { + break; + } + + if ( Q_IsColorString( str ) ) { + *p++ = *str++; + lastcolor = *str; + *p++ = *str++; + continue; + } + if ( *str == ' ' ) { + ls = p; + } + *p++ = *str++; + len++; + } + *p = 0; +} + +/* +======================= +CL_GetLimboString + +======================= +*/ +qboolean CL_GetLimboString( int index, char *buf ) { + if ( index >= LIMBOCHAT_HEIGHT ) { + return qfalse; + } + + strncpy( buf, cl.limboChatMsgs[index], 140 ); + return qtrue; +} +// -NERVE - SMF + + + +// NERVE - SMF - Localization code +#define FILE_HASH_SIZE 1024 +#define MAX_VA_STRING 32000 +#define MAX_TRANS_STRING 4096 + +#ifndef __MACOS__ //DAJ USA +typedef struct trans_s { + char original[MAX_TRANS_STRING]; + char translated[MAX_LANGUAGES][MAX_TRANS_STRING]; + struct trans_s *next; + float x_offset; + float y_offset; + qboolean fromFile; +} trans_t; + +static trans_t* transTable[FILE_HASH_SIZE]; + +/* +======================= +AllocTrans +======================= +*/ +static trans_t* AllocTrans( char *original, char *translated[MAX_LANGUAGES] ) { + trans_t *t; + int i; + + t = malloc( sizeof( trans_t ) ); + memset( t, 0, sizeof( trans_t ) ); + + if ( original ) { + strncpy( t->original, original, MAX_TRANS_STRING ); + } + + if ( translated ) { + for ( i = 0; i < MAX_LANGUAGES; i++ ) + strncpy( t->translated[i], translated[i], MAX_TRANS_STRING ); + } + + return t; +} + +/* +======================= +generateHashValue +======================= +*/ +static long generateHashValue( const char *fname ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while ( fname[i] != '\0' ) { + letter = tolower( fname[i] ); + hash += (long)( letter ) * ( i + 119 ); + i++; + } + hash &= ( FILE_HASH_SIZE - 1 ); + return hash; +} + +/* +======================= +LookupTrans +======================= +*/ +static trans_t* LookupTrans( char *original, char *translated[MAX_LANGUAGES], qboolean isLoading ) { + trans_t *t, *newt, *prev = NULL; + long hash; + + hash = generateHashValue( original ); + + for ( t = transTable[hash]; t; prev = t, t = t->next ) { + if ( !Q_stricmp( original, t->original ) ) { + if ( isLoading ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: Duplicate string found: \"%s\"\n", original ); + } + return t; + } + } + + newt = AllocTrans( original, translated ); + + if ( prev ) { + prev->next = newt; + } else { + transTable[hash] = newt; + } + + if ( cl_debugTranslation->integer >= 1 && !isLoading ) { + Com_Printf( "Missing translation: \'%s\'\n", original ); + } + + // see if we want to save out the translation table everytime a string is added + if ( cl_debugTranslation->integer == 2 && !isLoading ) { + CL_SaveTransTable(); + } + + return newt; +} + +/* +======================= +CL_SaveTransTable +======================= +*/ +void CL_SaveTransTable( const char *fileName, qboolean newOnly ) { + int bucketlen, bucketnum, maxbucketlen, avebucketlen; + int untransnum, transnum; + const char *buf; + fileHandle_t f; + trans_t *t; + int i, j, len; + + if ( cl.corruptedTranslationFile ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Cannot save corrupted translation file. Please reload first." ); + return; + } + + FS_FOpenFileByMode( fileName, &f, FS_WRITE ); + + bucketnum = 0; + maxbucketlen = 0; + avebucketlen = 0; + transnum = 0; + untransnum = 0; + + // write out version, if one + if ( strlen( cl.translationVersion ) ) { + buf = va( "#version\t\t\"%s\"\n", cl.translationVersion ); + } else { + buf = va( "#version\t\t\"1.0 01/01/01\"\n" ); + } + + len = strlen( buf ); + FS_Write( buf, len, f ); + + // write out translated strings + for ( j = 0; j < 2; j++ ) { + + for ( i = 0; i < FILE_HASH_SIZE; i++ ) { + t = transTable[i]; + + if ( !t || ( newOnly && t->fromFile ) ) { + continue; + } + + bucketlen = 0; + + for ( ; t; t = t->next ) { + bucketlen++; + + if ( strlen( t->translated[0] ) ) { + if ( j ) { + continue; + } + transnum++; + } else { + if ( !j ) { + continue; + } + untransnum++; + } + + buf = va( "{\n\tenglish\t\t\"%s\"\n", t->original ); + len = strlen( buf ); + FS_Write( buf, len, f ); + + buf = va( "\tfrench\t\t\"%s\"\n", t->translated[LANGUAGE_FRENCH] ); + len = strlen( buf ); + FS_Write( buf, len, f ); + + buf = va( "\tgerman\t\t\"%s\"\n", t->translated[LANGUAGE_GERMAN] ); + len = strlen( buf ); + FS_Write( buf, len, f ); + + buf = va( "\titalian\t\t\"%s\"\n", t->translated[LANGUAGE_ITALIAN] ); + len = strlen( buf ); + FS_Write( buf, len, f ); + + buf = va( "\tspanish\t\t\"%s\"\n", t->translated[LANGUAGE_SPANISH] ); + len = strlen( buf ); + FS_Write( buf, len, f ); + + buf = "}\n"; + len = strlen( buf ); + FS_Write( buf, len, f ); + } + + if ( bucketlen > maxbucketlen ) { + maxbucketlen = bucketlen; + } + + if ( bucketlen ) { + bucketnum++; + avebucketlen += bucketlen; + } + } + } + + Com_Printf( "Saved translation table.\nTotal = %i, Translated = %i, Untranslated = %i, aveblen = %2.2f, maxblen = %i\n", + transnum + untransnum, transnum, untransnum, (float)avebucketlen / bucketnum, maxbucketlen ); + + FS_FCloseFile( f ); +} + +/* +======================= +CL_CheckTranslationString + +NERVE - SMF - compare formatting characters +======================= +*/ +qboolean CL_CheckTranslationString( char *original, char *translated ) { + char format_org[128], format_trans[128]; + int len, i; + + memset( format_org, 0, 128 ); + memset( format_trans, 0, 128 ); + + // generate formatting string for original + len = strlen( original ); + + for ( i = 0; i < len; i++ ) { + if ( original[i] != '%' ) { + continue; + } + + strcat( format_org, va( "%c%c ", '%', original[i + 1] ) ); + } + + // generate formatting string for translated + len = strlen( translated ); + if ( !len ) { + return qtrue; + } + + for ( i = 0; i < len; i++ ) { + if ( translated[i] != '%' ) { + continue; + } + + strcat( format_trans, va( "%c%c ", '%', translated[i + 1] ) ); + } + + // compare + len = strlen( format_org ); + + if ( len != strlen( format_trans ) ) { + return qfalse; + } + + for ( i = 0; i < len; i++ ) { + if ( format_org[i] != format_trans[i] ) { + return qfalse; + } + } + + return qtrue; +} + +/* +======================= +CL_LoadTransTable +======================= +*/ +void CL_LoadTransTable( const char *fileName ) { + char translated[MAX_LANGUAGES][MAX_VA_STRING]; + char original[MAX_VA_STRING]; + qboolean aborted; + char *text; + fileHandle_t f; + char *text_p; + char *token; + int len, i; + trans_t *t; + int count; + + count = 0; + aborted = qfalse; + cl.corruptedTranslationFile = qfalse; + + len = FS_FOpenFileByMode( fileName, &f, FS_READ ); + if ( len <= 0 ) { + return; + } + + // Gordon: shouldn't this be a z_malloc or something? + text = malloc( len + 1 ); + if ( !text ) { + return; + } + + FS_Read( text, len, f ); + text[len] = 0; + FS_FCloseFile( f ); + + // parse the text + text_p = text; + + do { + token = COM_Parse( &text_p ); + if ( Q_stricmp( "{", token ) ) { + // parse version number + if ( !Q_stricmp( "#version", token ) ) { + token = COM_Parse( &text_p ); + strcpy( cl.translationVersion, token ); + continue; + } + + break; + } + + // english + token = COM_Parse( &text_p ); + if ( Q_stricmp( "english", token ) ) { + aborted = qtrue; + break; + } + + token = COM_Parse( &text_p ); + strcpy( original, token ); + + if ( cl_debugTranslation->integer == 3 ) { + Com_Printf( "%i Loading: \"%s\"\n", count, original ); + } + + // french + token = COM_Parse( &text_p ); + if ( Q_stricmp( "french", token ) ) { + aborted = qtrue; + break; + } + + token = COM_Parse( &text_p ); + strcpy( translated[LANGUAGE_FRENCH], token ); + if ( !CL_CheckTranslationString( original, translated[LANGUAGE_FRENCH] ) ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" ); + aborted = qtrue; + break; + } + + // german + token = COM_Parse( &text_p ); + if ( Q_stricmp( "german", token ) ) { + aborted = qtrue; + break; + } + + token = COM_Parse( &text_p ); + strcpy( translated[LANGUAGE_GERMAN], token ); + if ( !CL_CheckTranslationString( original, translated[LANGUAGE_GERMAN] ) ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" ); + aborted = qtrue; + break; + } + + // italian + token = COM_Parse( &text_p ); + if ( Q_stricmp( "italian", token ) ) { + aborted = qtrue; + break; + } + + token = COM_Parse( &text_p ); + strcpy( translated[LANGUAGE_ITALIAN], token ); + if ( !CL_CheckTranslationString( original, translated[LANGUAGE_ITALIAN] ) ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" ); + aborted = qtrue; + break; + } + + // spanish + token = COM_Parse( &text_p ); + if ( Q_stricmp( "spanish", token ) ) { + aborted = qtrue; + break; + } + + token = COM_Parse( &text_p ); + strcpy( translated[LANGUAGE_SPANISH], token ); + if ( !CL_CheckTranslationString( original, translated[LANGUAGE_SPANISH] ) ) { + Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" ); + aborted = qtrue; + break; + } + + // do lookup + t = LookupTrans( original, NULL, qtrue ); + + if ( t ) { + t->fromFile = qtrue; + + for ( i = 0; i < MAX_LANGUAGES; i++ ) + strncpy( t->translated[i], translated[i], MAX_TRANS_STRING ); + } + + token = COM_Parse( &text_p ); + + // set offset if we have one + if ( !Q_stricmp( "offset", token ) ) { + token = COM_Parse( &text_p ); + t->x_offset = atof( token ); + + token = COM_Parse( &text_p ); + t->y_offset = atof( token ); + + token = COM_Parse( &text_p ); + } + + if ( Q_stricmp( "}", token ) ) { + aborted = qtrue; + break; + } + + count++; + } while ( token ); + + if ( aborted ) { + int i, line = 1; + + for ( i = 0; i < len && ( text + i ) < text_p; i++ ) { + if ( text[i] == '\n' ) { + line++; + } + } + + Com_Printf( S_COLOR_YELLOW "WARNING: Problem loading %s on line %i\n", fileName, line ); + cl.corruptedTranslationFile = qtrue; + } else { + Com_Printf( "Loaded %i translation strings from %s\n", count, fileName ); + } + + // cleanup + free( text ); +} + +/* +======================= +CL_ReloadTranslation +======================= +*/ +void CL_ReloadTranslation() { + char **fileList; + int numFiles, i; + + for ( i = 0; i < FILE_HASH_SIZE; i++ ) { + if ( transTable[i] ) { + free( transTable[i] ); + } + } + + memset( transTable, 0, sizeof( trans_t* ) * FILE_HASH_SIZE ); + CL_LoadTransTable( "scripts/translation.cfg" ); + + fileList = FS_ListFiles( "translations", "cfg", &numFiles ); + + for ( i = 0; i < numFiles; i++ ) { + CL_LoadTransTable( va( "translations/%s", fileList[i] ) ); + } +} + +/* +======================= +CL_InitTranslation +======================= +*/ +void CL_InitTranslation() { + char **fileList; + int numFiles, i; + + memset( transTable, 0, sizeof( trans_t* ) * FILE_HASH_SIZE ); + CL_LoadTransTable( "scripts/translation.cfg" ); + + fileList = FS_ListFiles( "translations", ".cfg", &numFiles ); + + for ( i = 0; i < numFiles; i++ ) { + CL_LoadTransTable( va( "translations/%s", fileList[i] ) ); + } +} + +#else +typedef struct trans_s { + char original[MAX_TRANS_STRING]; + struct trans_s *next; + float x_offset; + float y_offset; +} trans_t; + +#endif //DAJ USA + +/* +======================= +CL_TranslateString +======================= +*/ +void CL_TranslateString( const char *string, char *dest_buffer ) { + int i, count, currentLanguage; + trans_t *t; + qboolean newline = qfalse; + char *buf; + + buf = dest_buffer; + currentLanguage = cl_language->integer - 1; + + // early bail if we only want english or bad language type + if ( !string ) { + strcpy( buf, "(null)" ); + return; + } else if ( currentLanguage < 0 || currentLanguage >= MAX_LANGUAGES || !strlen( string ) ) { + strcpy( buf, string ); + return; + } +#if !defined( __MACOS__ ) + // ignore newlines + if ( string[strlen( string ) - 1] == '\n' ) { + newline = qtrue; + } + + for ( i = 0, count = 0; string[i] != '\0'; i++ ) { + if ( string[i] != '\n' ) { + buf[count++] = string[i]; + } + } + buf[count] = '\0'; + + t = LookupTrans( buf, NULL, qfalse ); + + if ( t && strlen( t->translated[currentLanguage] ) ) { + int offset = 0; + + if ( cl_debugTranslation->integer >= 1 ) { + buf[0] = '^'; + buf[1] = '1'; + buf[2] = '['; + offset = 3; + } + + strcpy( buf + offset, t->translated[currentLanguage] ); + + if ( cl_debugTranslation->integer >= 1 ) { + int len2 = strlen( buf ); + + buf[len2] = ']'; + buf[len2 + 1] = '^'; + buf[len2 + 2] = '7'; + buf[len2 + 3] = '\0'; + } + + if ( newline ) { + int len2 = strlen( buf ); + + buf[len2] = '\n'; + buf[len2 + 1] = '\0'; + } + } else { + int offset = 0; + + if ( cl_debugTranslation->integer >= 1 ) { + buf[0] = '^'; + buf[1] = '1'; + buf[2] = '['; + offset = 3; + } + + strcpy( buf + offset, string ); + + if ( cl_debugTranslation->integer >= 1 ) { + int len2 = strlen( buf ); + qboolean addnewline = qfalse; + + if ( buf[len2 - 1] == '\n' ) { + len2--; + addnewline = qtrue; + } + + buf[len2] = ']'; + buf[len2 + 1] = '^'; + buf[len2 + 2] = '7'; + buf[len2 + 3] = '\0'; + + if ( addnewline ) { + buf[len2 + 3] = '\n'; + buf[len2 + 4] = '\0'; + } + } + } +#endif //DAJ USA +} + +/* +======================= +CL_TranslateStringBuf +TTimo - handy, stores in a static buf, converts \n to chr(13) +======================= +*/ +const char* CL_TranslateStringBuf( const char *string ) { + char *p; + int i,l; + static char buf[MAX_VA_STRING]; + CL_TranslateString( string, buf ); + while ( ( p = strstr( buf, "\\n" ) ) != NULL ) + { + *p = '\n'; + p++; + // Com_Memcpy(p, p+1, strlen(p) ); b0rks on win32 + l = strlen( p ); + for ( i = 0; i < l; i++ ) + { + *p = *( p + 1 ); + p++; + } + } + return buf; +} + +/* +======================= +CL_OpenURLForCvar +======================= +*/ +void CL_OpenURL( const char *url ) { + if ( !url || !strlen( url ) ) { + Com_Printf( CL_TranslateStringBuf( "invalid/empty URL\n" ) ); + return; + } + Sys_OpenURL( url, qtrue ); +} + +// Gordon: TEST TEST TEST +/* +================== +BotImport_DrawPolygon +================== +*/ +void BotImport_DrawPolygon( int color, int numpoints, float* points ) { + re.DrawDebugPolygon( color, numpoints, points ); +} diff --git a/src/client/cl_net_chan.c b/src/client/cl_net_chan.c new file mode 100644 index 0000000..61eefb9 --- /dev/null +++ b/src/client/cl_net_chan.c @@ -0,0 +1,204 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "../game/q_shared.h" +#include "../qcommon/qcommon.h" +#include "client.h" + +/* +============== +CL_Netchan_Encode + + // first 12 bytes of the data are always: + long serverId; + long messageAcknowledge; + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Encode( msg_t *msg ) { + int serverId, messageAcknowledge, reliableAcknowledge; + int i, index, srdc, sbit, soob; + byte key, *string; + + if ( msg->cursize <= CL_ENCODE_START ) { + return; + } + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->bit = 0; + msg->readcount = 0; + msg->oob = 0; + + serverId = MSG_ReadLong( msg ); + messageAcknowledge = MSG_ReadLong( msg ); + reliableAcknowledge = MSG_ReadLong( msg ); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = (byte *)clc.serverCommands[ reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 ) ]; + index = 0; + // + key = clc.challenge ^ serverId ^ messageAcknowledge; + for ( i = CL_ENCODE_START; i < msg->cursize; i++ ) { + // modify the key with the last received now acknowledged server command + if ( !string[index] ) { + index = 0; + } + if ( string[index] > 127 || string[index] == '%' ) { + key ^= '.' << ( i & 1 ); + } else { + key ^= string[index] << ( i & 1 ); + } + index++; + // encode the data with this key + *( msg->data + i ) = ( *( msg->data + i ) ) ^ key; + } +} + +/* +============== +CL_Netchan_Decode + + // first four bytes of the data are always: + long reliableAcknowledge; + +============== +*/ +static void CL_Netchan_Decode( msg_t *msg ) { + long reliableAcknowledge, i, index; + byte key, *string; + int srdc, sbit, soob; + + srdc = msg->readcount; + sbit = msg->bit; + soob = msg->oob; + + msg->oob = 0; + + reliableAcknowledge = MSG_ReadLong( msg ); + + msg->oob = soob; + msg->bit = sbit; + msg->readcount = srdc; + + string = clc.reliableCommands[ reliableAcknowledge & ( MAX_RELIABLE_COMMANDS - 1 ) ]; + index = 0; + // xor the client challenge with the netchan sequence number (need something that changes every message) + key = clc.challenge ^ LittleLong( *(unsigned *)msg->data ); + for ( i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++ ) { + // modify the key with the last sent and with this message acknowledged client command + if ( !string[index] ) { + index = 0; + } + if ( string[index] > 127 || string[index] == '%' ) { + key ^= '.' << ( i & 1 ); + } else { + key ^= string[index] << ( i & 1 ); + } + index++; + // decode the data with this key + *( msg->data + i ) = *( msg->data + i ) ^ key; + } +} + +/* +================= +CL_Netchan_TransmitNextFragment +================= +*/ +void CL_Netchan_TransmitNextFragment( netchan_t *chan ) { + Netchan_TransmitNextFragment( chan ); +} + +extern qboolean SV_GameIsSinglePlayer( void ); + +/* +================ +CL_WriteBinaryMessage +================ +*/ +static void CL_WriteBinaryMessage( msg_t *msg ) { + if ( !clc.binaryMessageLength ) { + return; + } + + MSG_Uncompressed( msg ); + + if ( ( msg->cursize + clc.binaryMessageLength ) >= msg->maxsize ) { + clc.binaryMessageOverflowed = qtrue; + return; + } + + MSG_WriteData( msg, clc.binaryMessage, clc.binaryMessageLength ); + clc.binaryMessageLength = 0; + clc.binaryMessageOverflowed = qfalse; +} + +/* +================ +CL_Netchan_Transmit +================ +*/ +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { + MSG_WriteByte( msg, clc_EOF ); + CL_WriteBinaryMessage( msg ); + + if ( !SV_GameIsSinglePlayer() ) { + CL_Netchan_Encode( msg ); + } + Netchan_Transmit( chan, msg->cursize, msg->data ); +} + +extern int oldsize; +int newsize = 0; + +/* +================= +CL_Netchan_Process +================= +*/ +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { + int ret; + + ret = Netchan_Process( chan, msg ); + if ( !ret ) { + return qfalse; + } + if ( !SV_GameIsSinglePlayer() ) { + CL_Netchan_Decode( msg ); + } + newsize += msg->cursize; + return qtrue; +} diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c new file mode 100644 index 0000000..1737866 --- /dev/null +++ b/src/client/cl_parse.c @@ -0,0 +1,923 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cl_parse.c -- parse a message received from the server + +#include "client.h" + +char *svc_strings[256] = { + "svc_bad", + + "svc_nop", + "svc_gamestate", + "svc_configstring", + "svc_baseline", + "svc_serverCommand", + "svc_download", + "svc_snapshot", + "svc_EOF" +}; + +void SHOWNET( msg_t *msg, char *s ) { + if ( cl_shownet->integer >= 2 ) { + Com_Printf( "%3i:%s\n", msg->readcount - 1, s ); + } +} + + +/* +========================================================================= + +MESSAGE PARSING + +========================================================================= +*/ +#if 1 + +int entLastVisible[MAX_CLIENTS]; + +qboolean isEntVisible( entityState_t *ent ) { + trace_t tr; + vec3_t start, end, temp; + vec3_t forward, up, right, right2; + float view_height; + + VectorCopy( cl.cgameClientLerpOrigin, start ); + start[2] += ( cl.snap.ps.viewheight - 1 ); + if ( cl.snap.ps.leanf != 0 ) { + vec3_t lright, v3ViewAngles; + VectorCopy( cl.snap.ps.viewangles, v3ViewAngles ); + v3ViewAngles[2] += cl.snap.ps.leanf / 2.0f; + AngleVectors( v3ViewAngles, NULL, lright, NULL ); + VectorMA( start, cl.snap.ps.leanf, lright, start ); + } + + VectorCopy( ent->pos.trBase, end ); + + // Compute vector perpindicular to view to ent + VectorSubtract( end, start, forward ); + VectorNormalizeFast( forward ); + VectorSet( up, 0, 0, 1 ); + CrossProduct( forward, up, right ); + VectorNormalizeFast( right ); + VectorScale( right, 10, right2 ); + VectorScale( right, 18, right ); + + // Set viewheight + if ( ent->animMovetype ) { + view_height = 16; + } else { + view_height = 40; + } + + // First, viewpoint to viewpoint + end[2] += view_height; + CM_BoxTrace( &tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + // First-b, viewpoint to top of head + end[2] += 16; + CM_BoxTrace( &tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + end[2] -= 16; + + // Second, viewpoint to ent's origin + end[2] -= view_height; + CM_BoxTrace( &tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + // Third, to ent's right knee + VectorAdd( end, right, temp ); + temp[2] += 8; + CM_BoxTrace( &tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + // Fourth, to ent's right shoulder + VectorAdd( end, right2, temp ); + if ( ent->animMovetype ) { + temp[2] += 28; + } else { + temp[2] += 52; + } + CM_BoxTrace( &tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + // Fifth, to ent's left knee + VectorScale( right, -1, right ); + VectorScale( right2, -1, right2 ); + VectorAdd( end, right2, temp ); + temp[2] += 2; + CM_BoxTrace( &tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + // Sixth, to ent's left shoulder + VectorAdd( end, right, temp ); + if ( ent->animMovetype ) { + temp[2] += 16; + } else { + temp[2] += 36; + } + CM_BoxTrace( &tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse ); + if ( tr.fraction == 1.f ) { + return qtrue; + } + + return qfalse; +} + +#endif + +/* +================== +CL_DeltaEntity + +Parses deltas from the given base and adds the resulting entity +to the current frame +================== +*/ +void CL_DeltaEntity( msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, + qboolean unchanged ) { + entityState_t *state; + + // save the parsed entity state into the big circular buffer so + // it can be used as the source for a later delta + state = &cl.parseEntities[cl.parseEntitiesNum & ( MAX_PARSE_ENTITIES - 1 )]; + + if ( unchanged ) { + *state = *old; + } else { + MSG_ReadDeltaEntity( msg, old, state, newnum ); + } + + if ( state->number == ( MAX_GENTITIES - 1 ) ) { + return; // entity was delta removed + } + +#if 1 + // DHM - Nerve :: Only draw clients if visible + if ( clc.onlyVisibleClients ) { + if ( state->number < MAX_CLIENTS ) { + if ( isEntVisible( state ) ) { + entLastVisible[state->number] = frame->serverTime; + state->eFlags &= ~EF_NODRAW; + } else { + if ( entLastVisible[state->number] < ( frame->serverTime - 600 ) ) { + state->eFlags |= EF_NODRAW; + } + } + } + } +#endif + + cl.parseEntitiesNum++; + frame->numEntities++; +} + +/* +================== +CL_ParsePacketEntities + +================== +*/ +void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe ) { + int newnum; + entityState_t *oldstate; + int oldindex, oldnum; + + newframe->parseEntitiesNum = cl.parseEntitiesNum; + newframe->numEntities = 0; + + // delta from the entities present in oldframe + oldindex = 0; + oldstate = NULL; + if ( !oldframe ) { + oldnum = 99999; + } else { + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 )]; + oldnum = oldstate->number; + } + } + + while ( 1 ) { + // read the entity index number + newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); + + if ( newnum == ( MAX_GENTITIES - 1 ) ) { + break; + } + + if ( msg->readcount > msg->cursize ) { + Com_Error( ERR_DROP,"CL_ParsePacketEntities: end of message" ); + } + + while ( oldnum < newnum ) { + // one or more entities from the old packet are unchanged + if ( cl_shownet->integer == 3 ) { + Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); + } + CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 )]; + oldnum = oldstate->number; + } + } + if ( oldnum == newnum ) { + // delta from previous state + if ( cl_shownet->integer == 3 ) { + Com_Printf( "%3i: delta: %i\n", msg->readcount, newnum ); + } + CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 )]; + oldnum = oldstate->number; + } + continue; + } + + if ( oldnum > newnum ) { + // delta from baseline + if ( cl_shownet->integer == 3 ) { + Com_Printf( "%3i: baseline: %i\n", msg->readcount, newnum ); + } + CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse ); + continue; + } + + } + + // any remaining entities in the old frame are copied over + while ( oldnum != 99999 ) { + // one or more entities from the old packet are unchanged + if ( cl_shownet->integer == 3 ) { + Com_Printf( "%3i: unchanged: %i\n", msg->readcount, oldnum ); + } + CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); + + oldindex++; + + if ( oldindex >= oldframe->numEntities ) { + oldnum = 99999; + } else { + oldstate = &cl.parseEntities[ + ( oldframe->parseEntitiesNum + oldindex ) & ( MAX_PARSE_ENTITIES - 1 )]; + oldnum = oldstate->number; + } + } + + if ( cl_shownuments->integer ) { + Com_Printf( "Entities in packet: %i\n", newframe->numEntities ); + } +} + + +/* +================ +CL_ParseSnapshot + +If the snapshot is parsed properly, it will be copied to +cl.snap and saved in cl.snapshots[]. If the snapshot is invalid +for any reason, no changes to the state will be made at all. +================ +*/ +void CL_ParseSnapshot( msg_t *msg ) { + int len; + clSnapshot_t *old; + clSnapshot_t newSnap; + int deltaNum; + int oldMessageNum; + int i, packetNum; + + // get the reliable sequence acknowledge number + // NOTE: now sent with all server to client messages + //clc.reliableAcknowledge = MSG_ReadLong( msg ); + + // read in the new snapshot to a temporary buffer + // we will only copy to cl.snap if it is valid + memset( &newSnap, 0, sizeof( newSnap ) ); + + // we will have read any new server commands in this + // message before we got to svc_snapshot + newSnap.serverCommandNum = clc.serverCommandSequence; + + newSnap.serverTime = MSG_ReadLong( msg ); + + newSnap.messageNum = clc.serverMessageSequence; + + deltaNum = MSG_ReadByte( msg ); + if ( !deltaNum ) { + newSnap.deltaNum = -1; + } else { + newSnap.deltaNum = newSnap.messageNum - deltaNum; + } + newSnap.snapFlags = MSG_ReadByte( msg ); + + // If the frame is delta compressed from data that we + // no longer have available, we must suck up the rest of + // the frame, but not use it, then ask for a non-compressed + // message + if ( newSnap.deltaNum <= 0 ) { + newSnap.valid = qtrue; // uncompressed frame + old = NULL; + if ( clc.demorecording ) { + clc.demowaiting = qfalse; // we can start recording now +// if(cl_autorecord->integer) { +// Cvar_Set( "g_synchronousClients", "0" ); +// } + } else { + if ( cl_autorecord->integer /*&& Cvar_VariableValue( "g_synchronousClients")*/ ) { + char name[256]; + char mapname[MAX_QPATH]; + char* period; + qtime_t time; + + Com_RealTime( &time ); + + Q_strncpyz( mapname, cl.mapname, MAX_QPATH ); + for ( period = mapname; *period; period++ ) { + if ( *period == '.' ) { + *period = '\0'; + break; + } + } + + for ( period = mapname; *period; period++ ) { + if ( *period == '/' ) { + break; + } + } + if ( *period ) { + period++; + } + + Com_sprintf( name, sizeof( name ), "demos/%s_%i_%i.dm_%d", period, time.tm_mday, time.tm_mon + 1, PROTOCOL_VERSION ); + + CL_Record( name ); + } + } + } else { + old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; + if ( !old->valid ) { + // should never happen + Com_Printf( "Delta from invalid frame (not supposed to happen!).\n" ); + } else if ( old->messageNum != newSnap.deltaNum ) { + // The frame that the server did the delta from + // is too old, so we can't reconstruct it properly. + Com_DPrintf( "Delta frame too old.\n" ); + } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES - 128 ) { + Com_DPrintf( "Delta parseEntitiesNum too old.\n" ); + } else { + newSnap.valid = qtrue; // valid delta parse + } + } + + // read areamask + len = MSG_ReadByte( msg ); + + if ( len > sizeof( newSnap.areamask ) ) { + Com_Error( ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask.", len ); + return; + } + + MSG_ReadData( msg, &newSnap.areamask, len ); + + // read playerinfo + SHOWNET( msg, "playerstate" ); + if ( old ) { + MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); + } else { + MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); + } + + // read packet entities + SHOWNET( msg, "packet entities" ); + CL_ParsePacketEntities( msg, old, &newSnap ); + + // if not valid, dump the entire thing now that it has + // been properly read + if ( !newSnap.valid ) { + return; + } + + // clear the valid flags of any snapshots between the last + // received and this one, so if there was a dropped packet + // it won't look like something valid to delta from next + // time we wrap around in the buffer + oldMessageNum = cl.snap.messageNum + 1; + + if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { + oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); + } + for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { + cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; + } + + // copy to the current good spot + cl.snap = newSnap; + cl.snap.ping = 999; + // calculate ping time + for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { + packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; + if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { + cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; + break; + } + } + // save the frame off in the backup array for later delta comparisons + cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; + + if ( cl_shownet->integer == 3 ) { + Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, + cl.snap.deltaNum, cl.snap.ping ); + } + + cl.newSnapshots = qtrue; +} + + +//===================================================================== + +int cl_connectedToPureServer; + +/* +================== +CL_SystemInfoChanged + +The systeminfo configstring has been changed, so parse +new information out of it. This will happen at every +gamestate, and possibly during gameplay. +================== +*/ +void CL_PurgeCache( void ); +void CL_SystemInfoChanged( void ) { + char *systemInfo; + const char *s, *t; + char key[BIG_INFO_KEY]; + char value[BIG_INFO_VALUE]; + + systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; + // NOTE TTimo: + // when the serverId changes, any further messages we send to the server will use this new serverId + // show_bug.cgi?id=475 + // in some cases, outdated cp commands might get sent with this news serverId + cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); + + memset( &entLastVisible, 0, sizeof( entLastVisible ) ); + + // don't set any vars when playing a demo + if ( clc.demoplaying ) { + return; + } + + s = Info_ValueForKey( systemInfo, "sv_cheats" ); + sv_cheats = atoi( s ); //bani + if ( atoi( s ) == 0 ) { + Cvar_SetCheatState(); + } + + // check pure server string + s = Info_ValueForKey( systemInfo, "sv_paks" ); + t = Info_ValueForKey( systemInfo, "sv_pakNames" ); + FS_PureServerSetLoadedPaks( s, t ); + + s = Info_ValueForKey( systemInfo, "sv_referencedPaks" ); + t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); + FS_PureServerSetReferencedPaks( s, t ); + + // scan through all the variables in the systeminfo and locally set cvars to match + s = systemInfo; + while ( s ) { + Info_NextPair( &s, key, value ); + if ( !key[0] ) { + break; + } + + Cvar_Set( key, value ); + } + + // Arnout: big hack to clear the image cache on a pure change + //cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" ); + if ( Cvar_VariableValue( "sv_pure" ) ) { + if ( !cl_connectedToPureServer && cls.state <= CA_CONNECTED ) { + CL_PurgeCache(); + } + cl_connectedToPureServer = qtrue; + } else { + if ( cl_connectedToPureServer && cls.state <= CA_CONNECTED ) { + CL_PurgeCache(); + } + cl_connectedToPureServer = qfalse; + } +} + +/* +================== +CL_ParseGamestate +================== +*/ +void CL_ParseGamestate( msg_t *msg ) { + int i; + entityState_t *es; + int newnum; + entityState_t nullstate; + int cmd; + char *s; + + Con_Close(); + + clc.connectPacketCount = 0; + + // wipe local client state + CL_ClearState(); + + // a gamestate always marks a server command sequence + clc.serverCommandSequence = MSG_ReadLong( msg ); + + // parse all the configstrings and baselines + cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings + while ( 1 ) { + cmd = MSG_ReadByte( msg ); + + if ( cmd == svc_EOF ) { + break; + } + + if ( cmd == svc_configstring ) { + int len; + + i = MSG_ReadShort( msg ); + if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { + Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); + } + s = MSG_ReadBigString( msg ); + len = strlen( s ); + + if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { + Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); + } + + // append it to the gameState string buffer + cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; + memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); + cl.gameState.dataCount += len + 1; + } else if ( cmd == svc_baseline ) { + newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); + if ( newnum < 0 || newnum >= MAX_GENTITIES ) { + Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); + } + memset( &nullstate, 0, sizeof( nullstate ) ); + es = &cl.entityBaselines[ newnum ]; + MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); + } else { + Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); + } + } + + clc.clientNum = MSG_ReadLong( msg ); + // read the checksum feed + clc.checksumFeed = MSG_ReadLong( msg ); + + // parse serverId and other cvars + CL_SystemInfoChanged(); + + // Arnout: verify if we have all official pakfiles. As we won't + // be downloading them, we should be kicked for not having them. + if ( cl_connectedToPureServer && !FS_VerifyOfficialPaks() ) { + Com_Error( ERR_DROP, "Couldn't load an official pak file; verify your installation and make sure it has been updated to the latest version." ); + } + + // reinitialize the filesystem if the game directory has changed + FS_ConditionalRestart( clc.checksumFeed ); + + // This used to call CL_StartHunkUsers, but now we enter the download state before loading the + // cgame + CL_InitDownloads(); + + // make sure the game starts + Cvar_Set( "cl_paused", "0" ); +} + + +//===================================================================== + +/* +===================== +CL_ParseDownload + +A download message has been received from the server +===================== +*/ +void CL_ParseDownload( msg_t *msg ) { + int size; + unsigned char data[MAX_MSGLEN]; + int block; + + if ( !*cls.downloadTempName ) { + Com_Printf( "Server sending download, but no download was requested\n" ); + CL_AddReliableCommand( "stopdl" ); + return; + } + + // read the data + block = MSG_ReadShort( msg ); + + // TTimo - www dl + // if we haven't acked the download redirect yet + if ( block == -1 ) { + if ( !clc.bWWWDl ) { + // server is sending us a www download + Q_strncpyz( cls.originalDownloadName, cls.downloadName, sizeof( cls.originalDownloadName ) ); + Q_strncpyz( cls.downloadName, MSG_ReadString( msg ), sizeof( cls.downloadName ) ); + clc.downloadSize = MSG_ReadLong( msg ); + clc.downloadFlags = MSG_ReadLong( msg ); + if ( clc.downloadFlags & ( 1 << DL_FLAG_URL ) ) { + Sys_OpenURL( cls.downloadName, qtrue ); + Cbuf_ExecuteText( EXEC_APPEND, "quit\n" ); + CL_AddReliableCommand( "wwwdl bbl8r" ); // not sure if that's the right msg + clc.bWWWDlAborting = qtrue; + return; + } + Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); + Com_DPrintf( "Server redirected download: %s\n", cls.downloadName ); + clc.bWWWDl = qtrue; // activate wwwdl client loop + CL_AddReliableCommand( "wwwdl ack" ); + // make sure the server is not trying to redirect us again on a bad checksum + if ( strstr( clc.badChecksumList, va( "@%s", cls.originalDownloadName ) ) ) { + Com_Printf( "refusing redirect to %s by server (bad checksum)\n", cls.downloadName ); + CL_AddReliableCommand( "wwwdl fail" ); + clc.bWWWDlAborting = qtrue; + return; + } + // make downloadTempName an OS path + Q_strncpyz( cls.downloadTempName, FS_BuildOSPath( Cvar_VariableString( "fs_homepath" ), cls.downloadTempName, "" ), sizeof( cls.downloadTempName ) ); + cls.downloadTempName[strlen( cls.downloadTempName ) - 1] = '\0'; + if ( !DL_BeginDownload( cls.downloadTempName, cls.downloadName, com_developer->integer ) ) { + // setting bWWWDl to false after sending the wwwdl fail doesn't work + // not sure why, but I suspect we have to eat all remaining block -1 that the server has sent us + // still leave a flag so that CL_WWWDownload is inactive + // we count on server sending us a gamestate to start up clean again + CL_AddReliableCommand( "wwwdl fail" ); + clc.bWWWDlAborting = qtrue; + Com_Printf( "Failed to initialize download for '%s'\n", cls.downloadName ); + } + // Check for a disconnected download + // we'll let the server disconnect us when it gets the bbl8r message + if ( clc.downloadFlags & ( 1 << DL_FLAG_DISCON ) ) { + CL_AddReliableCommand( "wwwdl bbl8r" ); + cls.bWWWDlDisconnected = qtrue; + } + return; + } else + { + // server keeps sending that message till we ack it, eat and ignore + //MSG_ReadLong( msg ); + MSG_ReadString( msg ); + MSG_ReadLong( msg ); + MSG_ReadLong( msg ); + return; + } + } + + if ( !block ) { + // block zero is special, contains file size + clc.downloadSize = MSG_ReadLong( msg ); + + Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); + + if ( clc.downloadSize < 0 ) { + Com_Error( ERR_DROP, MSG_ReadString( msg ) ); + return; + } + } + + size = MSG_ReadShort( msg ); + if ( size < 0 || size > sizeof( data ) ) { + Com_Error( ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size ); + return; + } + + MSG_ReadData( msg, data, size ); + + if ( clc.downloadBlock != block ) { + Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block ); + return; + } + + // open the file if not opened yet + if ( !clc.download ) { + clc.download = FS_SV_FOpenFileWrite( cls.downloadTempName ); + + if ( !clc.download ) { + Com_Printf( "Could not create %s\n", cls.downloadTempName ); + CL_AddReliableCommand( "stopdl" ); + CL_NextDownload(); + return; + } + } + + if ( size ) { + FS_Write( data, size, clc.download ); + } + + CL_AddReliableCommand( va( "nextdl %d", clc.downloadBlock ) ); + clc.downloadBlock++; + + clc.downloadCount += size; + + // So UI gets access to it + Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); + + if ( !size ) { // A zero length block means EOF + if ( clc.download ) { + FS_FCloseFile( clc.download ); + clc.download = 0; + + // rename the file + FS_SV_Rename( cls.downloadTempName, cls.downloadName ); + } + *cls.downloadTempName = *cls.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + + // send intentions now + // We need this because without it, we would hold the last nextdl and then start + // loading right away. If we take a while to load, the server is happily trying + // to send us that last block over and over. + // Write it twice to help make sure we acknowledge the download + CL_WritePacket(); + CL_WritePacket(); + + // get another file if needed + CL_NextDownload(); + } +} + +/* +===================== +CL_ParseCommandString + +Command strings are just saved off until cgame asks for them +when it transitions a snapshot +===================== +*/ +void CL_ParseCommandString( msg_t *msg ) { + char *s; + int seq; + int index; + + seq = MSG_ReadLong( msg ); + s = MSG_ReadString( msg ); + + // see if we have already executed stored it off + if ( clc.serverCommandSequence >= seq ) { + return; + } + clc.serverCommandSequence = seq; + + index = seq & ( MAX_RELIABLE_COMMANDS - 1 ); + Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) ); +} + +/* +===================== +CL_ParseBinaryMessage +===================== +*/ +void CL_ParseBinaryMessage( msg_t *msg ) { + int size; + + MSG_BeginReadingUncompressed( msg ); + + size = msg->cursize - msg->readcount; + if ( size <= 0 || size > MAX_BINARY_MESSAGE ) { + return; + } + + CL_CGameBinaryMessageReceived( &msg->data[msg->readcount], size, cl.snap.serverTime ); +} + +/* +===================== +CL_ParseServerMessage +===================== +*/ +void CL_ParseServerMessage( msg_t *msg ) { + int cmd; + msg_t msgback; + + msgback = *msg; + + if ( cl_shownet->integer == 1 ) { + Com_Printf( "%i ",msg->cursize ); + } else if ( cl_shownet->integer >= 2 ) { + Com_Printf( "------------------\n" ); + } + + MSG_Bitstream( msg ); + + // get the reliable sequence acknowledge number + clc.reliableAcknowledge = MSG_ReadLong( msg ); + // + if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { + clc.reliableAcknowledge = clc.reliableSequence; + } + + // + // parse the message + // + while ( 1 ) { + if ( msg->readcount > msg->cursize ) { + Com_Error( ERR_DROP,"CL_ParseServerMessage: read past end of server message" ); + break; + } + + cmd = MSG_ReadByte( msg ); + + if ( cmd == svc_EOF ) { + SHOWNET( msg, "END OF MESSAGE" ); + break; + } + + if ( cl_shownet->integer >= 2 ) { + if ( !svc_strings[cmd] ) { + Com_Printf( "%3i:BAD CMD %i\n", msg->readcount - 1, cmd ); + } else { + SHOWNET( msg, svc_strings[cmd] ); + } + } + + // other commands + switch ( cmd ) { + default: + Com_Error( ERR_DROP,"CL_ParseServerMessage: Illegible server message %d\n", cmd ); + break; + case svc_nop: + break; + case svc_serverCommand: + CL_ParseCommandString( msg ); + break; + case svc_gamestate: + CL_ParseGamestate( msg ); + break; + case svc_snapshot: + CL_ParseSnapshot( msg ); + break; + case svc_download: + CL_ParseDownload( msg ); + break; + } + } + + CL_ParseBinaryMessage( msg ); +} diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c new file mode 100644 index 0000000..2588aeb --- /dev/null +++ b/src/client/cl_scrn.c @@ -0,0 +1,562 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc + +#include "client.h" + +qboolean scr_initialized; // ready to draw + +cvar_t *cl_timegraph; +cvar_t *cl_debuggraph; +cvar_t *cl_graphheight; +cvar_t *cl_graphscale; +cvar_t *cl_graphshift; + +/* +================ +SCR_DrawNamedPic + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { + qhandle_t hShader; + + assert( width != 0 ); + + hShader = re.RegisterShader( picname ); + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); +} + + +/* +================ +SCR_AdjustFrom640 + +Adjusted for resolution and screen aspect ratio +================ +*/ +void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) { + float xscale; + float yscale; + +#if 0 + // adjust for wide screens + if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { + *x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) ); + } +#endif + + // scale for screen sizes + xscale = cls.glconfig.vidWidth / 640.0; + yscale = cls.glconfig.vidHeight / 480.0; + if ( x ) { + *x *= xscale; + } + if ( y ) { + *y *= yscale; + } + if ( w ) { + *w *= xscale; + } + if ( h ) { + *h *= yscale; + } +} + +/* +================ +SCR_FillRect + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_FillRect( float x, float y, float width, float height, const float *color ) { + re.SetColor( color ); + + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader ); + + re.SetColor( NULL ); +} + + +/* +================ +SCR_DrawPic + +Coordinates are 640*480 virtual values +================= +*/ +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) { + SCR_AdjustFrom640( &x, &y, &width, &height ); + re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader ); +} + + + +/* +** SCR_DrawChar +** chars are drawn at 640*480 virtual screen size +*/ +static void SCR_DrawChar( int x, int y, float size, int ch ) { + int row, col; + float frow, fcol; + float ax, ay, aw, ah; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + if ( y < -size ) { + return; + } + + ax = x; + ay = y; + aw = size; + ah = size; + SCR_AdjustFrom640( &ax, &ay, &aw, &ah ); + + row = ch >> 4; + col = ch & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + re.DrawStretchPic( ax, ay, aw, ah, + fcol, frow, + fcol + size, frow + size, + cls.charSetShader ); +} + +/* +** SCR_DrawSmallChar +** small chars are drawn at native screen resolution +*/ +void SCR_DrawSmallChar( int x, int y, int ch ) { + int row, col; + float frow, fcol; + float size; + + ch &= 255; + + if ( ch == ' ' ) { + return; + } + + if ( y < -SMALLCHAR_HEIGHT ) { + return; + } + + row = ch >> 4; + col = ch & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, + fcol, frow, + fcol + size, frow + size, + cls.charSetShader ); +} + + +/* +================== +SCR_DrawBigString[Color] + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor ) { + vec4_t color; + const char *s; + int xx; + + // draw the drop shadow + color[0] = color[1] = color[2] = 0; + color[3] = setColor[3]; + re.SetColor( color ); + s = string; + xx = x; + while ( *s ) { + if ( Q_IsColorString( s ) ) { + s += 2; + continue; + } + SCR_DrawChar( xx + 2, y + 2, size, *s ); + xx += size; + s++; + } + + + // draw the colored text + s = string; + xx = x; + re.SetColor( setColor ); + while ( *s ) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( color, setColor, sizeof( color ) ); + } else { + memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) ); + color[3] = setColor[3]; + } + color[3] = setColor[3]; + re.SetColor( color ); + } + s += 2; + continue; + } + SCR_DrawChar( xx, y, size, *s ); + xx += size; + s++; + } + re.SetColor( NULL ); +} + + +void SCR_DrawBigString( int x, int y, const char *s, float alpha ) { + float color[4]; + + color[0] = color[1] = color[2] = 1.0; + color[3] = alpha; + SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse ); +} + +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) { + SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue ); +} + + +/* +================== +SCR_DrawSmallString[Color] + +Draws a multi-colored string with a drop shadow, optionally forcing +to a fixed color. + +Coordinates are at 640 by 480 virtual resolution +================== +*/ +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ) { + vec4_t color; + const char *s; + int xx; + + // draw the colored text + s = string; + xx = x; + re.SetColor( setColor ); + while ( *s ) { + if ( Q_IsColorString( s ) ) { + if ( !forceColor ) { + if ( *( s + 1 ) == COLOR_NULL ) { + memcpy( color, setColor, sizeof( color ) ); + } else { + memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) ); + color[3] = setColor[3]; + } + re.SetColor( color ); + } + s += 2; + continue; + } + SCR_DrawSmallChar( xx, y, *s ); + xx += SMALLCHAR_WIDTH; + s++; + } + re.SetColor( NULL ); +} + + + +/* +** SCR_Strlen -- skips color escape codes +*/ +static int SCR_Strlen( const char *str ) { + const char *s = str; + int count = 0; + + while ( *s ) { + if ( Q_IsColorString( s ) ) { + s += 2; + } else { + count++; + s++; + } + } + + return count; +} + +/* +** SCR_GetBigStringWidth +*/ +int SCR_GetBigStringWidth( const char *str ) { + return SCR_Strlen( str ) * 16; +} + + +//=============================================================================== + +/* +================= +SCR_DrawDemoRecording +================= +*/ +void SCR_DrawDemoRecording( void ) { + if ( !clc.demorecording ) { + return; + } + + //bani + Cvar_Set( "cl_demooffset", va( "%d", FS_FTell( clc.demofile ) ) ); +} + + +/* +=============================================================================== + +DEBUG GRAPH + +=============================================================================== +*/ + +typedef struct +{ + float value; + int color; +} graphsamp_t; + +static int current; +static graphsamp_t values[1024]; + +/* +============== +SCR_DebugGraph +============== +*/ +void SCR_DebugGraph( float value, int color ) { + values[current & 1023].value = value; + values[current & 1023].color = color; + current++; +} + +/* +============== +SCR_DrawDebugGraph +============== +*/ +void SCR_DrawDebugGraph( void ) { + int a, x, y, w, i, h; + float v; + int color; + + // + // draw the graph + // + w = cls.glconfig.vidWidth; + x = 0; + y = cls.glconfig.vidHeight; + re.SetColor( g_color_table[0] ); + re.DrawStretchPic( x, y - cl_graphheight->integer, + w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader ); + re.SetColor( NULL ); + + for ( a = 0 ; a < w ; a++ ) + { + i = ( current - 1 - a + 1024 ) & 1023; + v = values[i].value; + color = values[i].color; + v = v * cl_graphscale->integer + cl_graphshift->integer; + + if ( v < 0 ) { + v += cl_graphheight->integer * ( 1 + (int)( -v / cl_graphheight->integer ) ); + } + h = (int)v % cl_graphheight->integer; + re.DrawStretchPic( x + w - 1 - a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader ); + } +} + +//============================================================================= + +/* +================== +SCR_Init +================== +*/ +void SCR_Init( void ) { + cl_timegraph = Cvar_Get( "timegraph", "0", CVAR_CHEAT ); + cl_debuggraph = Cvar_Get( "debuggraph", "0", CVAR_CHEAT ); + cl_graphheight = Cvar_Get( "graphheight", "32", CVAR_CHEAT ); + cl_graphscale = Cvar_Get( "graphscale", "1", CVAR_CHEAT ); + cl_graphshift = Cvar_Get( "graphshift", "0", CVAR_CHEAT ); + + scr_initialized = qtrue; +} + + +//======================================================= + +/* +================== +SCR_DrawScreenField + +This will be called twice if rendering in stereo mode +================== +*/ +void SCR_DrawScreenField( stereoFrame_t stereoFrame ) { + re.BeginFrame( stereoFrame ); + + // wide aspect ratio screens need to have the sides cleared + // unless they are displaying game renderings +/* if ( cls.state != CA_ACTIVE ) { + if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) { + re.SetColor( g_color_table[0] ); + re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader ); + re.SetColor( NULL ); + } + }*/ + + if ( !uivm ) { + Com_DPrintf( "draw screen without UI loaded\n" ); + return; + } + + // if the menu is going to cover the entire screen, we + // don't need to render anything under it + if ( !VM_Call( uivm, UI_IS_FULLSCREEN ) ) { + switch ( cls.state ) { + default: + Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" ); + break; + case CA_CINEMATIC: + SCR_DrawCinematic(); + break; + case CA_DISCONNECTED: + // force menu up + S_StopAllSounds(); + VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); + break; + case CA_CONNECTING: + case CA_CHALLENGING: + case CA_CONNECTED: + // connecting clients will only show the connection dialog + // refresh to update the time + VM_Call( uivm, UI_REFRESH, cls.realtime ); + VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse ); + break; +// // Ridah, if the cgame is valid, fall through to there +// if (!cls.cgameStarted || !com_sv_running->integer) { +// // connecting clients will only show the connection dialog +// VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse ); +// break; +// } + case CA_LOADING: + case CA_PRIMED: + // draw the game information screen and loading progress + CL_CGameRendering( stereoFrame ); + + // also draw the connection information, so it doesn't + // flash away too briefly on local or lan games + //if (!com_sv_running->value || Cvar_VariableIntegerValue("sv_cheats")) // Ridah, don't draw useless text if not in dev mode + VM_Call( uivm, UI_REFRESH, cls.realtime ); + VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue ); + break; + case CA_ACTIVE: + CL_CGameRendering( stereoFrame ); + SCR_DrawDemoRecording(); + break; + } + } + + // the menu draws next + if ( cls.keyCatchers & KEYCATCH_UI && uivm ) { + VM_Call( uivm, UI_REFRESH, cls.realtime ); + } + + // console draws next + Con_DrawConsole(); + + // debug graph can be drawn on top of anything + if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) { + SCR_DrawDebugGraph(); + } +} + +/* +================== +SCR_UpdateScreen + +This is called every frame, and can also be called explicitly to flush +text to the screen. +================== +*/ +void SCR_UpdateScreen( void ) { + static int recursive = 0; + + if ( !scr_initialized ) { + return; // not initialized yet + } + + if ( ++recursive >= 2 ) { + recursive = 0; + // Gordon: i'm breaking this again, because we've removed most of our cases but still have one which will not fix easily + return; +// Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" ); + } + recursive = 1; + + // if running in stereo, we need to draw the frame twice + if ( cls.glconfig.stereoEnabled ) { + SCR_DrawScreenField( STEREO_LEFT ); + SCR_DrawScreenField( STEREO_RIGHT ); + } else { + SCR_DrawScreenField( STEREO_CENTER ); + } + + if ( com_speeds->integer ) { + re.EndFrame( &time_frontend, &time_backend ); + } else { + re.EndFrame( NULL, NULL ); + } + + recursive = 0; +} diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c new file mode 100644 index 0000000..7165f1b --- /dev/null +++ b/src/client/cl_ui.c @@ -0,0 +1,1310 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#include "client.h" + +#include "../game/botlib.h" + +extern botlib_export_t *botlib_export; + +vm_t *uivm; + + +// ydnar: can we put this in a header, pls? +void Key_GetBindingByString( const char* binding, int* key1, int* key2 ); + + +/* +==================== +GetClientState +==================== +*/ +static void GetClientState( uiClientState_t *state ) { + state->connectPacketCount = clc.connectPacketCount; + state->connState = cls.state; + Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) ); + Q_strncpyz( state->updateInfoString, cls.updateInfoString, sizeof( state->updateInfoString ) ); + Q_strncpyz( state->messageString, clc.serverMessage, sizeof( state->messageString ) ); + state->clientNum = cl.snap.ps.clientNum; +} + +/* +==================== +LAN_LoadCachedServers +==================== +*/ +void LAN_LoadCachedServers() { + int size; + fileHandle_t fileIn; + char filename[MAX_QPATH]; + + cls.numglobalservers = cls.numfavoriteservers = 0; + cls.numGlobalServerAddresses = 0; + + if ( com_gameInfo.usesProfiles && cl_profile->string[0] ) { + Com_sprintf( filename, sizeof( filename ), "profiles/%s/servercache.dat", cl_profile->string ); + } else { + Q_strncpyz( filename, "servercache.dat", sizeof( filename ) ); + } + + // Arnout: moved to mod/profiles dir + //if (FS_SV_FOpenFileRead(filename, &fileIn)) { + if ( FS_FOpenFileRead( filename, &fileIn, qtrue ) ) { + FS_Read( &cls.numglobalservers, sizeof( int ), fileIn ); + FS_Read( &cls.numfavoriteservers, sizeof( int ), fileIn ); + FS_Read( &size, sizeof( int ), fileIn ); + if ( size == sizeof( cls.globalServers ) + sizeof( cls.favoriteServers ) ) { + FS_Read( &cls.globalServers, sizeof( cls.globalServers ), fileIn ); + FS_Read( &cls.favoriteServers, sizeof( cls.favoriteServers ), fileIn ); + } else { + cls.numglobalservers = cls.numfavoriteservers = 0; + cls.numGlobalServerAddresses = 0; + } + FS_FCloseFile( fileIn ); + } +} + +/* +==================== +LAN_SaveServersToCache +==================== +*/ +void LAN_SaveServersToCache() { + int size; + fileHandle_t fileOut; + char filename[MAX_QPATH]; + + if ( com_gameInfo.usesProfiles && cl_profile->string[0] ) { + Com_sprintf( filename, sizeof( filename ), "profiles/%s/servercache.dat", cl_profile->string ); + } else { + Q_strncpyz( filename, "servercache.dat", sizeof( filename ) ); + } + + // Arnout: moved to mod/profiles dir + //fileOut = FS_SV_FOpenFileWrite(filename); + fileOut = FS_FOpenFileWrite( filename ); + FS_Write( &cls.numglobalservers, sizeof( int ), fileOut ); + FS_Write( &cls.numfavoriteservers, sizeof( int ), fileOut ); + size = sizeof( cls.globalServers ) + sizeof( cls.favoriteServers ); + FS_Write( &size, sizeof( int ), fileOut ); + FS_Write( &cls.globalServers, sizeof( cls.globalServers ), fileOut ); + FS_Write( &cls.favoriteServers, sizeof( cls.favoriteServers ), fileOut ); + FS_FCloseFile( fileOut ); +} + + +/* +==================== +LAN_ResetPings +==================== +*/ +static void LAN_ResetPings( int source ) { + int count,i; + serverInfo_t *servers = NULL; + count = 0; + + switch ( source ) { + case AS_LOCAL: + servers = &cls.localServers[0]; + count = MAX_OTHER_SERVERS; + break; + case AS_GLOBAL: + servers = &cls.globalServers[0]; + count = MAX_GLOBAL_SERVERS; + break; + case AS_FAVORITES: + servers = &cls.favoriteServers[0]; + count = MAX_OTHER_SERVERS; + break; + } + if ( servers ) { + for ( i = 0; i < count; i++ ) { + servers[i].ping = -1; + } + } +} + +/* +==================== +LAN_AddServer +==================== +*/ +static int LAN_AddServer( int source, const char *name, const char *address ) { + int max, *count, i; + netadr_t adr; + serverInfo_t *servers = NULL; + max = MAX_OTHER_SERVERS; + count = 0; + + switch ( source ) { + case AS_LOCAL: + count = &cls.numlocalservers; + servers = &cls.localServers[0]; + break; + case AS_GLOBAL: + max = MAX_GLOBAL_SERVERS; + count = &cls.numglobalservers; + servers = &cls.globalServers[0]; + break; + case AS_FAVORITES: + count = &cls.numfavoriteservers; + servers = &cls.favoriteServers[0]; + break; + } + if ( servers && *count < max ) { + NET_StringToAdr( address, &adr ); + for ( i = 0; i < *count; i++ ) { + if ( NET_CompareAdr( servers[i].adr, adr ) ) { + break; + } + } + if ( i >= *count ) { + servers[*count].adr = adr; + Q_strncpyz( servers[*count].hostName, name, sizeof( servers[*count].hostName ) ); + servers[*count].visible = qtrue; + ( *count )++; + return 1; + } + return 0; + } + return -1; +} + +/* +==================== +LAN_RemoveServer +==================== +*/ +static void LAN_RemoveServer( int source, const char *addr ) { + int *count, i; + serverInfo_t *servers = NULL; + count = 0; + switch ( source ) { + case AS_LOCAL: + count = &cls.numlocalservers; + servers = &cls.localServers[0]; + break; + case AS_GLOBAL: + count = &cls.numglobalservers; + servers = &cls.globalServers[0]; + break; + case AS_FAVORITES: + count = &cls.numfavoriteservers; + servers = &cls.favoriteServers[0]; + break; + } + if ( servers ) { + netadr_t comp; + NET_StringToAdr( addr, &comp ); + for ( i = 0; i < *count; i++ ) { + if ( NET_CompareAdr( comp, servers[i].adr ) ) { + int j = i; + while ( j < *count - 1 ) { + Com_Memcpy( &servers[j], &servers[j + 1], sizeof( servers[j] ) ); + j++; + } + ( *count )--; + break; + } + } + } +} + + +/* +==================== +LAN_GetServerCount +==================== +*/ +static int LAN_GetServerCount( int source ) { + switch ( source ) { + case AS_LOCAL: + return cls.numlocalservers; + break; + case AS_GLOBAL: + return cls.numglobalservers; + break; + case AS_FAVORITES: + return cls.numfavoriteservers; + break; + } + return 0; +} + +/* +==================== +LAN_GetLocalServerAddressString +==================== +*/ +static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) { + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + Q_strncpyz( buf, NET_AdrToString( cls.localServers[n].adr ), buflen ); + return; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + Q_strncpyz( buf, NET_AdrToString( cls.globalServers[n].adr ), buflen ); + return; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + Q_strncpyz( buf, NET_AdrToString( cls.favoriteServers[n].adr ), buflen ); + return; + } + break; + } + buf[0] = '\0'; +} + +/* +==================== +LAN_GetServerInfo +==================== +*/ +static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { + char info[MAX_STRING_CHARS]; + serverInfo_t *server = NULL; + info[0] = '\0'; + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + server = &cls.localServers[n]; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + server = &cls.globalServers[n]; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + server = &cls.favoriteServers[n]; + } + break; + } + if ( server && buf ) { + buf[0] = '\0'; + Info_SetValueForKey( info, "hostname", server->hostName ); + Info_SetValueForKey( info, "serverload", va( "%i", server->load ) ); + Info_SetValueForKey( info, "mapname", server->mapName ); + Info_SetValueForKey( info, "clients", va( "%i",server->clients ) ); + Info_SetValueForKey( info, "sv_maxclients", va( "%i",server->maxClients ) ); + Info_SetValueForKey( info, "ping", va( "%i",server->ping ) ); + Info_SetValueForKey( info, "minping", va( "%i",server->minPing ) ); + Info_SetValueForKey( info, "maxping", va( "%i",server->maxPing ) ); + Info_SetValueForKey( info, "game", server->game ); + Info_SetValueForKey( info, "gametype", va( "%i",server->gameType ) ); + Info_SetValueForKey( info, "nettype", va( "%i",server->netType ) ); + Info_SetValueForKey( info, "addr", NET_AdrToString( server->adr ) ); + Info_SetValueForKey( info, "sv_allowAnonymous", va( "%i", server->allowAnonymous ) ); + Info_SetValueForKey( info, "friendlyFire", va( "%i", server->friendlyFire ) ); // NERVE - SMF + Info_SetValueForKey( info, "maxlives", va( "%i", server->maxlives ) ); // NERVE - SMF + Info_SetValueForKey( info, "needpass", va( "%i", server->needpass ) ); // NERVE - SMF + Info_SetValueForKey( info, "punkbuster", va( "%i", server->punkbuster ) ); // DHM - Nerve + Info_SetValueForKey( info, "gamename", server->gameName ); // Arnout + Info_SetValueForKey( info, "g_antilag", va( "%i", server->antilag ) ); // TTimo + Info_SetValueForKey( info, "weaprestrict", va( "%i", server->weaprestrict ) ); + Info_SetValueForKey( info, "balancedteams", va( "%i", server->balancedteams ) ); + Q_strncpyz( buf, info, buflen ); + } else { + if ( buf ) { + buf[0] = '\0'; + } + } +} + +/* +==================== +LAN_GetServerPing +==================== +*/ +static int LAN_GetServerPing( int source, int n ) { + serverInfo_t *server = NULL; + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + server = &cls.localServers[n]; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + server = &cls.globalServers[n]; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + server = &cls.favoriteServers[n]; + } + break; + } + if ( server ) { + return server->ping; + } + return -1; +} + +/* +==================== +LAN_GetServerPtr +==================== +*/ +static serverInfo_t *LAN_GetServerPtr( int source, int n ) { + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + return &cls.localServers[n]; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + return &cls.globalServers[n]; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + return &cls.favoriteServers[n]; + } + break; + } + return NULL; +} + +/* +==================== +LAN_CompareServers +==================== +*/ +static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) { + int res; + serverInfo_t *server1, *server2; + char name1[ MAX_NAME_LENGTH ], name2[ MAX_NAME_LENGTH ]; + + server1 = LAN_GetServerPtr( source, s1 ); + server2 = LAN_GetServerPtr( source, s2 ); + if ( !server1 || !server2 ) { + return 0; + } + + res = 0; + switch ( sortKey ) { + case SORT_HOST: + //% res = Q_stricmp( server1->hostName, server2->hostName ); + Q_strncpyz( name1, server1->hostName, sizeof( name1 ) ); + Q_CleanStr( name1 ); + Q_strncpyz( name2, server2->hostName, sizeof( name2 ) ); + Q_CleanStr( name2 ); + res = Q_stricmp( name1, name2 ); + break; + + case SORT_MAP: + res = Q_stricmp( server1->mapName, server2->mapName ); + break; + case SORT_CLIENTS: + if ( server1->clients < server2->clients ) { + res = -1; + } else if ( server1->clients > server2->clients ) { + res = 1; + } else { + res = 0; + } + break; + case SORT_GAME: + if ( server1->gameType < server2->gameType ) { + res = -1; + } else if ( server1->gameType > server2->gameType ) { + res = 1; + } else { + res = 0; + } + break; + case SORT_PING: + if ( server1->ping < server2->ping ) { + res = -1; + } else if ( server1->ping > server2->ping ) { + res = 1; + } else { + res = 0; + } + break; + } + + if ( sortDir ) { + if ( res < 0 ) { + return 1; + } + if ( res > 0 ) { + return -1; + } + return 0; + } + return res; +} + +/* +==================== +LAN_GetPingQueueCount +==================== +*/ +static int LAN_GetPingQueueCount( void ) { + return ( CL_GetPingQueueCount() ); +} + +/* +==================== +LAN_ClearPing +==================== +*/ +static void LAN_ClearPing( int n ) { + CL_ClearPing( n ); +} + +/* +==================== +LAN_GetPing +==================== +*/ +static void LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) { + CL_GetPing( n, buf, buflen, pingtime ); +} + +/* +==================== +LAN_GetPingInfo +==================== +*/ +static void LAN_GetPingInfo( int n, char *buf, int buflen ) { + CL_GetPingInfo( n, buf, buflen ); +} + +/* +==================== +LAN_MarkServerVisible +==================== +*/ +static void LAN_MarkServerVisible( int source, int n, qboolean visible ) { + if ( n == -1 ) { + int count = MAX_OTHER_SERVERS; + serverInfo_t *server = NULL; + switch ( source ) { + case AS_LOCAL: + server = &cls.localServers[0]; + break; + case AS_GLOBAL: + server = &cls.globalServers[0]; + count = MAX_GLOBAL_SERVERS; + break; + case AS_FAVORITES: + server = &cls.favoriteServers[0]; + break; + } + if ( server ) { + for ( n = 0; n < count; n++ ) { + server[n].visible = visible; + } + } + + } else { + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + cls.localServers[n].visible = visible; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + cls.globalServers[n].visible = visible; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + cls.favoriteServers[n].visible = visible; + } + break; + } + } +} + + +/* +======================= +LAN_ServerIsVisible +======================= +*/ +static int LAN_ServerIsVisible( int source, int n ) { + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + return cls.localServers[n].visible; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + return cls.globalServers[n].visible; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + return cls.favoriteServers[n].visible; + } + break; + } + return qfalse; +} + +/* +======================= +LAN_UpdateVisiblePings +======================= +*/ +qboolean LAN_UpdateVisiblePings( int source ) { + return CL_UpdateVisiblePings_f( source ); +} + +/* +==================== +LAN_GetServerStatus +==================== +*/ +int LAN_GetServerStatus( char *serverAddress, char *serverStatus, int maxLen ) { + return CL_ServerStatus( serverAddress, serverStatus, maxLen ); +} + +/* +======================= +LAN_ServerIsInFavoriteList +======================= +*/ +qboolean LAN_ServerIsInFavoriteList( int source, int n ) { + int i; + serverInfo_t *server = NULL; + + switch ( source ) { + case AS_LOCAL: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + server = &cls.localServers[n]; + } + break; + case AS_GLOBAL: + if ( n >= 0 && n < MAX_GLOBAL_SERVERS ) { + server = &cls.globalServers[n]; + } + break; + case AS_FAVORITES: + if ( n >= 0 && n < MAX_OTHER_SERVERS ) { + return qtrue; + } + break; + } + + if ( !server ) { + return qfalse; + } + + for ( i = 0; i < cls.numfavoriteservers; i++ ) { + if ( NET_CompareAdr( cls.favoriteServers[i].adr, server->adr ) ) { + return qtrue; + } + } + + return qfalse; +} + +/* +==================== +CL_GetGlConfig +==================== +*/ +static void CL_GetGlconfig( glconfig_t *config ) { + *config = cls.glconfig; +} + +/* +==================== +GetClipboardData +==================== +*/ +static void GetClipboardData( char *buf, int buflen ) { + char *cbd; + + cbd = Sys_GetClipboardData(); + + if ( !cbd ) { + *buf = 0; + return; + } + + Q_strncpyz( buf, cbd, buflen ); + + Z_Free( cbd ); +} + +/* +==================== +Key_KeynumToStringBuf +==================== +*/ +void Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) { + Q_strncpyz( buf, Key_KeynumToString( keynum, qtrue ), buflen ); +} + +/* +==================== +Key_GetBindingBuf +==================== +*/ +void Key_GetBindingBuf( int keynum, char *buf, int buflen ) { + char *value; + + value = Key_GetBinding( keynum ); + if ( value ) { + Q_strncpyz( buf, value, buflen ); + } else { + *buf = 0; + } +} + +/* +==================== +Key_GetCatcher +==================== +*/ +int Key_GetCatcher( void ) { + return cls.keyCatchers; +} + +/* +==================== +Ket_SetCatcher +==================== +*/ +void Key_SetCatcher( int catcher ) { + // NERVE - SMF - console overrides everything + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) { + cls.keyCatchers = catcher | KEYCATCH_CONSOLE; + } else { + cls.keyCatchers = catcher; + } + +} + + +/* +==================== +CLUI_GetCDKey +==================== +*/ +static void CLUI_GetCDKey( char *buf, int buflen ) { + cvar_t *fs; + fs = Cvar_Get( "fs_game", "", CVAR_INIT | CVAR_SYSTEMINFO ); + if ( UI_usesUniqueCDKey() && fs && fs->string[0] != 0 ) { + memcpy( buf, &cl_cdkey[16], 16 ); + buf[16] = 0; + } else { + memcpy( buf, cl_cdkey, 16 ); + buf[16] = 0; + } +} + + +/* +==================== +CLUI_SetCDKey +==================== +*/ +static void CLUI_SetCDKey( char *buf ) { + cvar_t *fs; + fs = Cvar_Get( "fs_game", "", CVAR_INIT | CVAR_SYSTEMINFO ); + if ( UI_usesUniqueCDKey() && fs && fs->string[0] != 0 ) { + memcpy( &cl_cdkey[16], buf, 16 ); + cl_cdkey[32] = 0; + // set the flag so the fle will be written at the next opportunity + cvar_modifiedFlags |= CVAR_ARCHIVE; + } else { + memcpy( cl_cdkey, buf, 16 ); + // set the flag so the fle will be written at the next opportunity + cvar_modifiedFlags |= CVAR_ARCHIVE; + } +} + + +/* +==================== +GetConfigString +==================== +*/ +static int GetConfigString( int index, char *buf, int size ) { + int offset; + + if ( index < 0 || index >= MAX_CONFIGSTRINGS ) { + return qfalse; + } + + offset = cl.gameState.stringOffsets[index]; + if ( !offset ) { + if ( size ) { + buf[0] = 0; + } + return qfalse; + } + + Q_strncpyz( buf, cl.gameState.stringData + offset, size ); + + return qtrue; +} + +/* +==================== +FloatAsInt +==================== +*/ +static int FloatAsInt( float f ) { + int temp; + + *(float *)&temp = f; + + return temp; +} + +void *VM_ArgPtr( int intValue ); +#define VMA( x ) VM_ArgPtr( args[x] ) +#define VMF( x ) ( (float *)args )[x] + +/* +==================== +CL_UISystemCalls + +The ui module is making a system call +==================== +*/ +int CL_UISystemCalls( int *args ) { + switch ( args[0] ) { + case UI_ERROR: + Com_Error( ERR_DROP, "%s", (char *)VMA( 1 ) ); + return 0; + + case UI_PRINT: + Com_Printf( "%s", (char *)VMA( 1 ) ); + return 0; + + case UI_MILLISECONDS: + return Sys_Milliseconds(); + + case UI_CVAR_REGISTER: + Cvar_Register( VMA( 1 ), VMA( 2 ), VMA( 3 ), args[4] ); + return 0; + + case UI_CVAR_UPDATE: + Cvar_Update( VMA( 1 ) ); + return 0; + + case UI_CVAR_SET: + Cvar_Set( VMA( 1 ), VMA( 2 ) ); + return 0; + + case UI_CVAR_VARIABLEVALUE: + return FloatAsInt( Cvar_VariableValue( VMA( 1 ) ) ); + + case UI_CVAR_VARIABLESTRINGBUFFER: + Cvar_VariableStringBuffer( VMA( 1 ), VMA( 2 ), args[3] ); + return 0; + + case UI_CVAR_LATCHEDVARIABLESTRINGBUFFER: + Cvar_LatchedVariableStringBuffer( VMA( 1 ), VMA( 2 ), args[3] ); + return 0; + + case UI_CVAR_SETVALUE: + Cvar_SetValue( VMA( 1 ), VMF( 2 ) ); + return 0; + + case UI_CVAR_RESET: + Cvar_Reset( VMA( 1 ) ); + return 0; + + case UI_CVAR_CREATE: + Cvar_Get( VMA( 1 ), VMA( 2 ), args[3] ); + return 0; + + case UI_CVAR_INFOSTRINGBUFFER: + Cvar_InfoStringBuffer( args[1], VMA( 2 ), args[3] ); + return 0; + + case UI_ARGC: + return Cmd_Argc(); + + case UI_ARGV: + Cmd_ArgvBuffer( args[1], VMA( 2 ), args[3] ); + return 0; + + case UI_CMD_EXECUTETEXT: + Cbuf_ExecuteText( args[1], VMA( 2 ) ); + return 0; + + case UI_ADDCOMMAND: + Cmd_AddCommand( VMA( 1 ), NULL ); + return 0; + + case UI_FS_FOPENFILE: + return FS_FOpenFileByMode( VMA( 1 ), VMA( 2 ), args[3] ); + + case UI_FS_READ: + FS_Read( VMA( 1 ), args[2], args[3] ); + return 0; + + case UI_FS_WRITE: + FS_Write( VMA( 1 ), args[2], args[3] ); + return 0; + + case UI_FS_FCLOSEFILE: + FS_FCloseFile( args[1] ); + return 0; + + case UI_FS_DELETEFILE: + return FS_Delete( VMA( 1 ) ); + + case UI_FS_GETFILELIST: + return FS_GetFileList( VMA( 1 ), VMA( 2 ), VMA( 3 ), args[4] ); + + case UI_R_REGISTERMODEL: + return re.RegisterModel( VMA( 1 ) ); + + case UI_R_REGISTERSKIN: + return re.RegisterSkin( VMA( 1 ) ); + + case UI_R_REGISTERSHADERNOMIP: + return re.RegisterShaderNoMip( VMA( 1 ) ); + + case UI_R_CLEARSCENE: + re.ClearScene(); + return 0; + + case UI_R_ADDREFENTITYTOSCENE: + re.AddRefEntityToScene( VMA( 1 ) ); + return 0; + + case UI_R_ADDPOLYTOSCENE: + re.AddPolyToScene( args[1], args[2], VMA( 3 ) ); + return 0; + + // Ridah + case UI_R_ADDPOLYSTOSCENE: + re.AddPolysToScene( args[1], args[2], VMA( 3 ), args[4] ); + return 0; + // done. + + case UI_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 UI_R_ADDCORONATOSCENE: + re.AddCoronaToScene( VMA( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), args[6], args[7] ); + return 0; + + case UI_R_RENDERSCENE: + re.RenderScene( VMA( 1 ) ); + return 0; + + case UI_R_SETCOLOR: + re.SetColor( VMA( 1 ) ); + return 0; + + case UI_R_DRAW2DPOLYS: + re.Add2dPolys( VMA( 1 ), args[2], args[3] ); + return 0; + + case UI_R_DRAWSTRETCHPIC: + re.DrawStretchPic( VMF( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), VMF( 6 ), VMF( 7 ), VMF( 8 ), args[9] ); + return 0; + + case UI_R_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 UI_R_MODELBOUNDS: + re.ModelBounds( args[1], VMA( 2 ), VMA( 3 ) ); + return 0; + + case UI_UPDATESCREEN: + SCR_UpdateScreen(); + return 0; + + case UI_CM_LERPTAG: + return re.LerpTag( VMA( 1 ), VMA( 2 ), VMA( 3 ), args[4] ); + + case UI_S_REGISTERSOUND: +#ifdef DOOMSOUND ///// (SA) DOOMSOUND + return S_RegisterSound( VMA( 1 ) ); +#else + return S_RegisterSound( VMA( 1 ), args[2] ); +#endif ///// (SA) DOOMSOUND + + case UI_S_STARTLOCALSOUND: + S_StartLocalSound( args[1], args[2], args[3] ); + return 0; + + case UI_S_FADESTREAMINGSOUND: + S_FadeStreamingSound( VMF( 1 ), args[2], args[3] ); + return 0; + + case UI_S_FADEALLSOUNDS: + S_FadeAllSounds( VMF( 1 ), args[2], args[3] ); + return 0; + + case UI_KEY_KEYNUMTOSTRINGBUF: + Key_KeynumToStringBuf( args[1], VMA( 2 ), args[3] ); + return 0; + + case UI_KEY_GETBINDINGBUF: + Key_GetBindingBuf( args[1], VMA( 2 ), args[3] ); + return 0; + + case UI_KEY_SETBINDING: + Key_SetBinding( args[1], VMA( 2 ) ); + return 0; + + case UI_KEY_BINDINGTOKEYS: + Key_GetBindingByString( VMA( 1 ), VMA( 2 ), VMA( 3 ) ); + return 0; + + + case UI_KEY_ISDOWN: + return Key_IsDown( args[1] ); + + case UI_KEY_GETOVERSTRIKEMODE: + return Key_GetOverstrikeMode(); + + case UI_KEY_SETOVERSTRIKEMODE: + Key_SetOverstrikeMode( args[1] ); + return 0; + + case UI_KEY_CLEARSTATES: + Key_ClearStates(); + return 0; + + case UI_KEY_GETCATCHER: + return Key_GetCatcher(); + + case UI_KEY_SETCATCHER: + Key_SetCatcher( args[1] ); + return 0; + + case UI_GETCLIPBOARDDATA: + GetClipboardData( VMA( 1 ), args[2] ); + return 0; + + case UI_GETCLIENTSTATE: + GetClientState( VMA( 1 ) ); + return 0; + + case UI_GETGLCONFIG: + CL_GetGlconfig( VMA( 1 ) ); + return 0; + + case UI_GETCONFIGSTRING: + return GetConfigString( args[1], VMA( 2 ), args[3] ); + + case UI_LAN_LOADCACHEDSERVERS: + LAN_LoadCachedServers(); + return 0; + + case UI_LAN_SAVECACHEDSERVERS: + LAN_SaveServersToCache(); + return 0; + + case UI_LAN_ADDSERVER: + return LAN_AddServer( args[1], VMA( 2 ), VMA( 3 ) ); + + case UI_LAN_REMOVESERVER: + LAN_RemoveServer( args[1], VMA( 2 ) ); + return 0; + + case UI_LAN_GETPINGQUEUECOUNT: + return LAN_GetPingQueueCount(); + + case UI_LAN_CLEARPING: + LAN_ClearPing( args[1] ); + return 0; + + case UI_LAN_GETPING: + LAN_GetPing( args[1], VMA( 2 ), args[3], VMA( 4 ) ); + return 0; + + case UI_LAN_GETPINGINFO: + LAN_GetPingInfo( args[1], VMA( 2 ), args[3] ); + return 0; + + case UI_LAN_GETSERVERCOUNT: + return LAN_GetServerCount( args[1] ); + + case UI_LAN_GETSERVERADDRESSSTRING: + LAN_GetServerAddressString( args[1], args[2], VMA( 3 ), args[4] ); + return 0; + + case UI_LAN_GETSERVERINFO: + LAN_GetServerInfo( args[1], args[2], VMA( 3 ), args[4] ); + return 0; + + case UI_LAN_GETSERVERPING: + return LAN_GetServerPing( args[1], args[2] ); + + case UI_LAN_MARKSERVERVISIBLE: + LAN_MarkServerVisible( args[1], args[2], args[3] ); + return 0; + + case UI_LAN_SERVERISVISIBLE: + return LAN_ServerIsVisible( args[1], args[2] ); + + case UI_LAN_UPDATEVISIBLEPINGS: + return LAN_UpdateVisiblePings( args[1] ); + + case UI_LAN_RESETPINGS: + LAN_ResetPings( args[1] ); + return 0; + + case UI_LAN_SERVERSTATUS: + return LAN_GetServerStatus( VMA( 1 ), VMA( 2 ), args[3] ); + + case UI_LAN_SERVERISINFAVORITELIST: + return LAN_ServerIsInFavoriteList( args[1], args[2] ); + + case UI_SET_PBCLSTATUS: + return 0; + + case UI_SET_PBSVSTATUS: + return 0; + + case UI_LAN_COMPARESERVERS: + return LAN_CompareServers( args[1], args[2], args[3], args[4], args[5] ); + + case UI_MEMORY_REMAINING: + return Hunk_MemoryRemaining(); + + case UI_GET_CDKEY: + CLUI_GetCDKey( VMA( 1 ), args[2] ); + return 0; + + case UI_SET_CDKEY: + CLUI_SetCDKey( VMA( 1 ) ); + return 0; + + case UI_R_REGISTERFONT: + re.RegisterFont( VMA( 1 ), args[2], VMA( 3 ) ); + return 0; + + case UI_MEMSET: + return (int)memset( VMA( 1 ), args[2], args[3] ); + + case UI_MEMCPY: + return (int)memcpy( VMA( 1 ), VMA( 2 ), args[3] ); + + case UI_STRNCPY: + return (int)strncpy( VMA( 1 ), VMA( 2 ), args[3] ); + + case UI_SIN: + return FloatAsInt( sin( VMF( 1 ) ) ); + + case UI_COS: + return FloatAsInt( cos( VMF( 1 ) ) ); + + case UI_ATAN2: + return FloatAsInt( atan2( VMF( 1 ), VMF( 2 ) ) ); + + case UI_SQRT: + return FloatAsInt( sqrt( VMF( 1 ) ) ); + + case UI_FLOOR: + return FloatAsInt( floor( VMF( 1 ) ) ); + + case UI_CEIL: + return FloatAsInt( ceil( VMF( 1 ) ) ); + + case UI_PC_ADD_GLOBAL_DEFINE: + return botlib_export->PC_AddGlobalDefine( VMA( 1 ) ); + case UI_PC_REMOVE_ALL_GLOBAL_DEFINES: + botlib_export->PC_RemoveAllGlobalDefines(); + return 0; + case UI_PC_LOAD_SOURCE: + return botlib_export->PC_LoadSourceHandle( VMA( 1 ) ); + case UI_PC_FREE_SOURCE: + return botlib_export->PC_FreeSourceHandle( args[1] ); + case UI_PC_READ_TOKEN: + return botlib_export->PC_ReadTokenHandle( args[1], VMA( 2 ) ); + case UI_PC_SOURCE_FILE_AND_LINE: + return botlib_export->PC_SourceFileAndLine( args[1], VMA( 2 ), VMA( 3 ) ); + case UI_PC_UNREAD_TOKEN: + botlib_export->PC_UnreadLastTokenHandle( args[1] ); + return 0; + + case UI_S_STOPBACKGROUNDTRACK: + S_StopBackgroundTrack(); + return 0; + case UI_S_STARTBACKGROUNDTRACK: + S_StartBackgroundTrack( VMA( 1 ), VMA( 2 ), args[3] ); //----(SA) added fadeup time + return 0; + + case UI_REAL_TIME: + return Com_RealTime( VMA( 1 ) ); + + case UI_CIN_PLAYCINEMATIC: + Com_DPrintf( "UI_CIN_PlayCinematic\n" ); + return CIN_PlayCinematic( VMA( 1 ), args[2], args[3], args[4], args[5], args[6] ); + + case UI_CIN_STOPCINEMATIC: + return CIN_StopCinematic( args[1] ); + + case UI_CIN_RUNCINEMATIC: + return CIN_RunCinematic( args[1] ); + + case UI_CIN_DRAWCINEMATIC: + CIN_DrawCinematic( args[1] ); + return 0; + + case UI_CIN_SETEXTENTS: + CIN_SetExtents( args[1], args[2], args[3], args[4], args[5] ); + return 0; + + case UI_R_REMAP_SHADER: + re.RemapShader( VMA( 1 ), VMA( 2 ), VMA( 3 ) ); + return 0; + + case UI_VERIFY_CDKEY: + return CL_CDKeyValidate( VMA( 1 ), VMA( 2 ) ); + + // NERVE - SMF + case UI_CL_GETLIMBOSTRING: + return CL_GetLimboString( args[1], VMA( 2 ) ); + + case UI_CL_TRANSLATE_STRING: + CL_TranslateString( VMA( 1 ), VMA( 2 ) ); + return 0; + // -NERVE - SMF + + // DHM - Nerve + case UI_CHECKAUTOUPDATE: + CL_CheckAutoUpdate(); + return 0; + + case UI_GET_AUTOUPDATE: + CL_GetAutoUpdate(); + return 0; + // DHM - Nerve + + case UI_OPENURL: + CL_OpenURL( (const char *)VMA( 1 ) ); + return 0; + + case UI_GETHUNKDATA: + Com_GetHunkInfo( VMA( 1 ), VMA( 2 ) ); + return 0; + + default: + Com_Error( ERR_DROP, "Bad UI system trap: %i", args[0] ); + + } + + return 0; +} + +/* +==================== +CL_ShutdownUI +==================== +*/ +void CL_ShutdownUI( void ) { + cls.keyCatchers &= ~KEYCATCH_UI; + cls.uiStarted = qfalse; + if ( !uivm ) { + return; + } + VM_Call( uivm, UI_SHUTDOWN ); + VM_Free( uivm ); + uivm = NULL; +} + +/* +==================== +CL_InitUI +==================== +*/ + +void CL_InitUI( void ) { + int v; + + uivm = VM_Create( "ui", CL_UISystemCalls, VMI_NATIVE ); + if ( !uivm ) { + Com_Error( ERR_FATAL, "VM_Create on UI failed" ); + } + + // sanity check + v = VM_Call( uivm, UI_GETAPIVERSION ); + if ( v != UI_API_VERSION ) { + Com_Error( ERR_FATAL, "User Interface is version %d, expected %d", v, UI_API_VERSION ); + cls.uiStarted = qfalse; + } + + // init for this gamestate + VM_Call( uivm, UI_INIT, ( cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE ) ); +} + + +qboolean UI_usesUniqueCDKey() { + if ( uivm ) { + return ( VM_Call( uivm, UI_HASUNIQUECDKEY ) == qtrue ); + } else { + return qfalse; + } +} + +qboolean UI_checkKeyExec( int key ) { + if ( uivm ) { + return VM_Call( uivm, UI_CHECKEXECKEY, key ); + } else { + return qfalse; + } +} + +/* +==================== +UI_GameCommand + +See if the current console command is claimed by the ui +==================== +*/ +qboolean UI_GameCommand( void ) { + if ( !uivm ) { + return qfalse; + } + + return VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime ); +} diff --git a/src/client/client.h b/src/client/client.h new file mode 100644 index 0000000..7fc21d0 --- /dev/null +++ b/src/client/client.h @@ -0,0 +1,706 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// client.h -- primary header for client + +#include "../game/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../renderer/tr_public.h" +#include "../ui/ui_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" +#include "../game/bg_public.h" + +#define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits + +#define LIMBOCHAT_WIDTH 140 // NERVE - SMF - NOTE TTimo buffer size indicator, not related to screen bbox +#define LIMBOCHAT_HEIGHT 7 // NERVE - SMF + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + +// Arnout: for double tapping +typedef struct { + int pressedTime[DT_NUM]; + int releasedTime[DT_NUM]; + + int lastdoubleTap; +} doubleTap_t; + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +typedef struct { + int p_cmdNumber; // cl.cmdNumber when packet was sent + int p_serverTime; // usercmd->serverTime when packet was sent + int p_realtime; // cls.realtime when packet was sent +} outPacket_t; + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 2048 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; // it requres several frames in a timeout condition + // to disconnect, preventing debugging breaks from + // causing immediate disconnects on continue + clSnapshot_t snap; // latest received from server + + int serverTime; // may be paused during play + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse of any valid packet + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + // cgame communicates a few values to the client system + int cgameUserCmdValue; // current weapon to add to usercmd_t + int cgameFlags; // flags that can be set by the gamecode + float cgameSensitivity; + int cgameMpIdentClient; // NERVE - SMF + vec3_t cgameClientLerpOrigin; // DHM - Nerve + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + // Arnout: double tapping + doubleTap_t doubleTap; + + outPacket_t outPackets[PACKET_BACKUP]; // information about each packet we have sent out + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + int serverId; // included in each client message so the server + // can tell if it is for a prior map_restart + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t snapshots[PACKET_BACKUP]; + + entityState_t entityBaselines[MAX_GENTITIES]; // for delta compression when not in previous frame + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + // NERVE - SMF + // NOTE TTimo - UI uses LIMBOCHAT_WIDTH strings (140), + // but for the processing in CL_AddToLimboChat we need some safe room + char limboChatMsgs[LIMBOCHAT_HEIGHT][LIMBOCHAT_WIDTH * 3 + 1]; + int limboChatPos; + + qboolean corruptedTranslationFile; + char translationVersion[MAX_STRING_TOKENS]; + // -NERVE - SMF + + qboolean cameraMode; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, play a demo, or connect to a different server + +A connection can be to either a server through the network layer or a +demo through a file. + +============================================================================= +*/ + + +typedef struct { + + int clientNum; + int lastPacketSentTime; // for retransmits during connection + int lastPacketTime; // for timeouts + + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + char serverMessage[MAX_STRING_TOKENS]; // for display on connection dialog + + int challenge; // from the server to use for connecting + int checksumFeed; // from the server for checksum calculations + + int onlyVisibleClients; // DHM - Nerve + + // these are our reliable messages that go to the server + int reliableSequence; + int reliableAcknowledge; // the last one the server has executed + // TTimo - NOTE: incidentally, reliableCommands[0] is never used (always start at reliableAcknowledge+1) + char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_TOKEN_CHARS]; + + // unreliable binary data to send to server + int binaryMessageLength; + char binaryMessage[MAX_BINARY_MESSAGE]; + qboolean binaryMessageOverflowed; + + // server message (unreliable) and command (reliable) sequence + // numbers are NOT cleared at level changes, but continue to + // increase as long as the connection is valid + + // message sequence is used by both the network layer and the + // delta compression layer + int serverMessageSequence; + + // reliable messages received from server + int serverCommandSequence; + int lastExecutedServerCommand; // last server command grabbed or executed with CL_GetServerCommand + char serverCommands[MAX_RELIABLE_COMMANDS][MAX_TOKEN_CHARS]; + + // file transfer from server + fileHandle_t download; + int downloadNumber; + int downloadBlock; // block we are waiting for + int downloadCount; // how many bytes we got + int downloadSize; // how many bytes we got + int downloadFlags; // misc download behaviour flags sent by the server + char downloadList[MAX_INFO_STRING]; // list of paks we need to download + + // www downloading + qboolean bWWWDl; // we have a www download going + qboolean bWWWDlAborting; // disable the CL_WWWDownload until server gets us a gamestate (used for aborts) + char redirectedList[MAX_INFO_STRING]; // list of files that we downloaded through a redirect since last FS_ComparePaks + char badChecksumList[MAX_INFO_STRING]; // list of files for which wwwdl redirect is broken (wrong checksum) + + // demo information + char demoName[MAX_QPATH]; + qboolean demorecording; + qboolean demoplaying; + qboolean demowaiting; // don't record until a non-delta message is received + qboolean firstDemoFrameSkipped; + fileHandle_t demofile; + + qboolean waverecording; + fileHandle_t wavefile; + int wavetime; + + int timeDemoFrames; // counter of rendered frames + int timeDemoStart; // cls.realtime before first frame + int timeDemoBaseTime; // each frame will be at this time + frameNum * 50 + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + netadr_t adr; + int start; + int time; + char info[MAX_INFO_STRING]; +} ping_t; + +typedef struct { + netadr_t adr; + char hostName[MAX_NAME_LENGTH]; + int load; + char mapName[MAX_NAME_LENGTH]; + char game[MAX_NAME_LENGTH]; + int netType; + int gameType; + int clients; + int maxClients; + int minPing; + int maxPing; + int ping; + qboolean visible; + int allowAnonymous; + int friendlyFire; // NERVE - SMF + int maxlives; // NERVE - SMF + int needpass; + int punkbuster; // DHM - Nerve + int antilag; // TTimo + int weaprestrict; + int balancedteams; + char gameName[MAX_NAME_LENGTH]; // Arnout +} serverInfo_t; + +typedef struct { + byte ip[4]; + unsigned short port; +} serverAddress_t; + +typedef struct { + connstate_t state; // connection status + int keyCatchers; // bit flags + + qboolean cddialog; // bring up the cd needed dialog next frame + + qboolean doCachePurge; // Arnout: empty the renderer cache as soon as possible + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + + int realtime; // ignores pause + int realFrametime; // ignoring pause, so console always works + + int numlocalservers; + serverInfo_t localServers[MAX_OTHER_SERVERS]; + + int numglobalservers; + serverInfo_t globalServers[MAX_GLOBAL_SERVERS]; + // additional global servers + int numGlobalServerAddresses; + serverAddress_t globalServerAddresses[MAX_GLOBAL_SERVERS]; + + int numfavoriteservers; + serverInfo_t favoriteServers[MAX_OTHER_SERVERS]; + + int pingUpdateSource; // source currently pinging or updating + + int masterNum; + + // update server info + netadr_t updateServer; + char updateChallenge[MAX_TOKEN_CHARS]; + char updateInfoString[MAX_INFO_STRING]; + + netadr_t authorizeServer; + + // DHM - Nerve :: Auto-update Info + char autoupdateServerNames[MAX_AUTOUPDATE_SERVERS][MAX_QPATH]; + netadr_t autoupdateServer; + qboolean autoUpdateServerChecked[MAX_AUTOUPDATE_SERVERS]; + int autoupdatServerFirstIndex; // to know when we went through all of them + int autoupdatServerIndex; // to cycle through them + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; + qhandle_t consoleShader2; // NERVE - SMF - merged from WolfSP + + // www downloading + // in the static stuff since this may have to survive server disconnects + // if new stuff gets added, CL_ClearStaticDownload code needs to be updated for clear up + qboolean bWWWDlDisconnected; // keep going with the download after server disconnect + char downloadName[MAX_OSPATH]; + char downloadTempName[MAX_OSPATH]; // in wwwdl mode, this is OS path (it's a qpath otherwise) + char originalDownloadName[MAX_QPATH]; // if we get a redirect, keep a copy of the original file path + qboolean downloadRestart; // if true, we need to do another FS_Restart because we downloaded a pak +} clientStatic_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern vm_t *cgvm; // interface to cgame dll or vm +extern vm_t *uivm; // interface to ui dll or vm +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_maxpackets; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_shownuments; // DHM - Nerve +extern cvar_t *cl_visibleClients; // DHM - Nerve +extern cvar_t *cl_showSend; +extern cvar_t *cl_showServerCommands; // NERVE - SMF +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; +extern cvar_t *cl_freezeDemo; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_recoilPitch; // RF + +extern cvar_t *cl_bypassMouseInput; // NERVE - SMF + +extern cvar_t *cl_doubletapdelay; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_timedemo; + +extern cvar_t *cl_activeAction; +extern cvar_t *cl_autorecord; + +extern cvar_t *cl_allowDownload; +extern cvar_t *cl_conXOffset; +extern cvar_t *cl_inGameVideo; + +extern cvar_t *cl_missionStats; +extern cvar_t *cl_waitForFire; + +// NERVE - SMF - localization +extern cvar_t *cl_language; +// -NERVE - SMF + +extern cvar_t *cl_profile; +extern cvar_t *cl_defaultProfile; + +//bani +extern qboolean sv_cheats; + +//================================================= + +// +// cl_main +// + +void CL_Init( void ); +void CL_FlushMemory( void ); +void CL_ShutdownAll( void ); +void CL_AddReliableCommand( const char *cmd ); + +void CL_StartHunkUsers( void ); + +void CL_CheckAutoUpdate( void ); +qboolean CL_NextUpdateServer( void ); +void CL_GetAutoUpdate( void ); + +void CL_Disconnect_f( void ); +void CL_GetChallengePacket( void ); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f( void ); +void CL_NextDemo( void ); +void CL_ReadDemoMessage( void ); + +void CL_InitDownloads( void ); +void CL_NextDownload( void ); + +void CL_GetPing( int n, char *buf, int buflen, int *pingtime ); +void CL_GetPingInfo( int n, char *buf, int buflen ); +void CL_ClearPing( int n ); +int CL_GetPingQueueCount( void ); + +void CL_ShutdownRef( void ); +void CL_InitRef( void ); +qboolean CL_CDKeyValidate( const char *key, const char *checksum ); +int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ); + +void CL_AddToLimboChat( const char *str ); // NERVE - SMF +qboolean CL_GetLimboString( int index, char *buf ); // NERVE - SMF + +// NERVE - SMF - localization +void CL_InitTranslation(); +void CL_SaveTransTable(); +void CL_ReloadTranslation(); +void CL_TranslateString( const char *string, char *dest_buffer ); +const char* CL_TranslateStringBuf( const char *string ); // TTimo +// -NERVE - SMF + +void CL_OpenURL( const char *url ); // TTimo + +void CL_Record( const char* name ); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +typedef enum { + KB_LEFT, + KB_RIGHT, + KB_FORWARD, + KB_BACK, + KB_LOOKUP, + KB_LOOKDOWN, + KB_MOVELEFT, + KB_MOVERIGHT, + KB_STRAFE, + KB_SPEED, + KB_UP, + KB_DOWN, + KB_BUTTONS0, + KB_BUTTONS1, + KB_BUTTONS2, + KB_BUTTONS3, + KB_BUTTONS4, + KB_BUTTONS5, + KB_BUTTONS6, + KB_BUTTONS7, + KB_WBUTTONS0, + KB_WBUTTONS1, + KB_WBUTTONS2, + KB_WBUTTONS3, + KB_WBUTTONS4, + KB_WBUTTONS5, + KB_WBUTTONS6, + KB_WBUTTONS7, + KB_MLOOK, + NUM_BUTTONS +} kbuttons_t; + + +void CL_ClearKeys( void ); + +void CL_InitInput( void ); +void CL_SendCmd( void ); +void CL_ClearState( void ); +void CL_ReadPackets( void ); + +void CL_WritePacket( void ); +//void IN_CenterView (void); +void IN_Notebook( void ); +void IN_Help( void ); + +//----(SA) salute +void IN_Salute( void ); +//----(SA) + +void CL_VerifyCode( void ); + +float CL_KeyState( kbutton_t *key ); +char *Key_KeynumToString( int keynum, qboolean bTranslate ); + +// +// cl_parse.c +// +extern int cl_connectedToPureServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +void CL_UpdateInfoPacket( netadr_t from ); // DHM - Nerve + +void CL_ServerInfoPacket( netadr_t from, msg_t *msg ); +void CL_LocalServers_f( void ); +void CL_GlobalServers_f( void ); +void CL_FavoriteServers_f( void ); +void CL_Ping_f( void ); +qboolean CL_UpdateVisiblePings_f( int source ); + + +// +// console +// +#define NUM_CON_TIMES 4 + +//#define CON_TEXTSIZE 32768 +#define CON_TEXTSIZE 65536 // (SA) DM want's more console... + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + float desiredFrac; // ydnar: for variable console heights + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; + + int acLength; // Arnout: autocomplete buffer length +} console_t; + +extern console_t con; + +void Con_DrawCharacter( int cx, int line, int num ); + +void Con_CheckResize( void ); +void Con_Init( void ); +void Con_Clear_f( void ); +void Con_ToggleConsole_f( void ); +void Con_DrawNotify( void ); +void Con_ClearNotify( void ); +void Con_RunConsole( void ); +void Con_DrawConsole( void ); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + +// +// cl_scrn.c +// +void SCR_Init( void ); +void SCR_UpdateScreen( void ); + +void SCR_DebugGraph( float value, int color ); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ); +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ); +void SCR_DrawSmallChar( int x, int y, int ch ); + + +// +// cl_cin.c +// + +void CL_PlayCinematic_f( void ); +void SCR_DrawCinematic( void ); +void SCR_RunCinematic( void ); +void SCR_StopCinematic( void ); +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits ); +e_status CIN_StopCinematic( int handle ); +e_status CIN_RunCinematic( int handle ); +void CIN_DrawCinematic( int handle ); +void CIN_SetExtents( int handle, int x, int y, int w, int h ); +void CIN_SetLooping( int handle, qboolean loop ); +void CIN_UploadCinematic( int handle ); +void CIN_CloseAllVideos( void ); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); +void CL_ShaderStateChanged( void ); +void CL_UpdateLevelHunkUsage( void ); +void CL_CGameBinaryMessageReceived( const char *buf, int buflen, int serverTime ); + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); +void LAN_LoadCachedServers(); +void LAN_SaveServersToCache(); + + +// +// cl_net_chan.c +// +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ); //int length, const byte *data ); +void CL_Netchan_TransmitNextFragment( netchan_t *chan ); +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ); diff --git a/src/client/keys.h b/src/client/keys.h new file mode 100644 index 0000000..cf07a39 --- /dev/null +++ b/src/client/keys.h @@ -0,0 +1,67 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "../ui/keycodes.h" + +#define MAX_KEYS 256 + +typedef struct { + qboolean down; + int repeats; // if > 1, it is autorepeating + char *binding; + int hash; +} qkey_t; + +extern qboolean key_overstrikeMode; +extern qkey_t keys[MAX_KEYS]; + +// NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h + +void Field_KeyDownEvent( field_t *edit, int key ); +void Field_CharEvent( field_t *edit, int ch ); +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ); +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ); + +#define COMMAND_HISTORY 32 +extern field_t historyEditLines[COMMAND_HISTORY]; + +extern field_t g_consoleField; +extern field_t chatField; +extern qboolean anykeydown; +extern qboolean chat_team; +extern qboolean chat_buddy; + +void Key_WriteBindings( fileHandle_t f ); +void Key_SetBinding( int keynum, const char *binding ); +void Key_GetBindingByString( const char* binding, int* key1, int* key2 ); +char *Key_GetBinding( int keynum ); +qboolean Key_IsDown( int keynum ); +qboolean Key_GetOverstrikeMode( void ); +void Key_SetOverstrikeMode( qboolean state ); +void Key_ClearStates( void ); +int Key_GetKey( const char *binding ); diff --git a/src/client/snd_adpcm.c b/src/client/snd_adpcm.c new file mode 100644 index 0000000..fafe1f3 --- /dev/null +++ b/src/client/snd_adpcm.c @@ -0,0 +1,141 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +#include "snd_local.h" + +void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) { + // LordHavoc: removed 4-clause BSD code for Intel ADPCM codec +} + + +void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) { + // LordHavoc: removed 4-clause BSD code for Intel ADPCM codec +} + + +/* +==================== +S_AdpcmMemoryNeeded + +Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format +==================== +*/ +int S_AdpcmMemoryNeeded( const wavinfo_t *info ) { + float scale; + int scaledSampleCount; + int sampleMemory; + int blockCount; + int headerMemory; + + // determine scale to convert from input sampling rate to desired sampling rate + scale = (float)info->rate / dma.speed; + + // calc number of samples at playback sampling rate + scaledSampleCount = info->samples / scale; + + // calc memory need to store those samples using ADPCM at 4 bits per sample + sampleMemory = scaledSampleCount / 2; + + // calc number of sample blocks needed of PAINTBUFFER_SIZE + blockCount = scaledSampleCount / PAINTBUFFER_SIZE; + if ( scaledSampleCount % PAINTBUFFER_SIZE ) { + blockCount++; + } + + // calc memory needed to store the block headers + headerMemory = blockCount * sizeof( adpcm_state_t ); + + return sampleMemory + headerMemory; +} + + +/* +==================== +S_AdpcmGetSamples +==================== +*/ +void S_AdpcmGetSamples( sndBuffer *chunk, short *to ) { + adpcm_state_t state; + byte *out; + + // get the starting state from the block header + state.index = chunk->adpcm.index; + state.sample = chunk->adpcm.sample; + + out = (byte *)chunk->sndChunk; + // get samples + S_AdpcmDecode( (const char*)out, to, SND_CHUNK_SIZE_BYTE * 2, &state ); +} + + +/* +==================== +S_AdpcmEncodeSound +==================== +*/ +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) { + adpcm_state_t state; + int inOffset; + int count; + int n; + sndBuffer *newchunk, *chunk; + byte *out; + + inOffset = 0; + count = sfx->soundLength; + state.index = 0; + state.sample = samples[0]; + + chunk = NULL; + while ( count ) { + n = count; + if ( n > SND_CHUNK_SIZE_BYTE * 2 ) { + n = SND_CHUNK_SIZE_BYTE * 2; + } + + newchunk = SND_malloc(); + if ( sfx->soundData == NULL ) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + + // output the header + chunk->adpcm.index = state.index; + chunk->adpcm.sample = state.sample; + + out = (byte *)chunk->sndChunk; + + // encode the samples + S_AdpcmEncode( samples + inOffset, (char *)out, n, &state ); + + inOffset += n; + count -= n; + } +} diff --git a/src/client/snd_dma.c b/src/client/snd_dma.c new file mode 100644 index 0000000..3a3a933 --- /dev/null +++ b/src/client/snd_dma.c @@ -0,0 +1,2824 @@ +/* +=========================================================================== + +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: snd_dma.c + * + * desc: main control for any streaming sound output device + * + * $Archive: /Wolfenstein MP/src/client/snd_dma.c $ + * + *****************************************************************************/ + +#include "snd_local.h" +#include "client.h" + +void S_Play_f( void ); +void S_SoundList_f( void ); +void S_Music_f( void ); +void S_QueueMusic_f( void ); +void S_StreamingSound_f( void ); +void S_ClearSounds( qboolean clearStreaming, qboolean clearMusic ); //----(SA) modified + +void S_Update_Mix(); +void S_StopAllSounds( void ); +void S_UpdateStreamingSounds( void ); + +snd_t snd; // globals for sound + +// Ridah, streaming sounds +// !! NOTE: the first streaming sound is always the music +streamingSound_t streamingSounds[MAX_STREAMING_SOUNDS]; +int numStreamingSounds = 0; + +void *crit; + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +// only begin attenuating sound volumes when outside the FULLVOLUME range +#define SOUND_FULLVOLUME 80 + +#define SOUND_ATTENUATE 0.0008f +#define SOUND_RANGE_DEFAULT 1250 + +channel_t s_channels[MAX_CHANNELS]; +channel_t loop_channels[MAX_CHANNELS]; +int numLoopChannels; + +dma_t dma; + +static int listener_number; +static vec3_t listener_origin; +static vec3_t listener_axis[3]; + +int s_soundtime; // sample PAIRS +int s_paintedtime; // sample PAIRS + +// MAX_SFX may be larger than MAX_SOUNDS because +// of custom player sounds +#define MAX_SFX 4096 +sfx_t s_knownSfx[MAX_SFX]; + +cvar_t *s_volume; +cvar_t *s_testsound; +cvar_t *s_khz; +cvar_t *s_show; +cvar_t *s_mixahead; +cvar_t *s_mixPreStep; +cvar_t *s_musicVolume; +cvar_t *s_currentMusic; //----(SA) added +cvar_t *s_separation; +cvar_t *s_doppler; +cvar_t *s_mute; // (SA) for DM so he can 'toggle' sound on/off without disturbing volume levels +cvar_t *s_defaultsound; // (SA) added to silence the default beep sound if desired +cvar_t *cl_cacheGathering; // Ridah +cvar_t *s_wavonly; +cvar_t *s_debugMusic; //----(SA) added + +// Rafael +cvar_t *s_nocompressed; + +// fretn +cvar_t *s_bits; +cvar_t *s_numchannels; + +// for streaming sounds +int s_rawend[MAX_STREAMING_SOUNDS]; +int s_rawpainted[MAX_STREAMING_SOUNDS]; +portable_samplepair_t s_rawsamples[MAX_STREAMING_SOUNDS][MAX_RAW_SAMPLES]; +// RF, store the volumes, since now they get adjusted at time of painting, so we can extract talking data first +portable_samplepair_t s_rawVolume[MAX_STREAMING_SOUNDS]; + +/* +================ +S_SoundInfo_f +================ +*/ +void S_SoundInfo_f( void ) { + Com_Printf( "----- Sound Info -----\n" ); + if ( !snd.s_soundStarted ) { + Com_Printf( "sound system not started\n" ); + } else { + if ( snd.s_soundMute ) { + Com_Printf( "sound system is muted\n" ); + } + + Com_Printf( "%5d stereo\n", dma.channels - 1 ); + Com_Printf( "%5d samples\n", dma.samples ); + Com_Printf( "%5d samplebits\n", dma.samplebits ); + Com_Printf( "%5d submission_chunk\n", dma.submission_chunk ); + Com_Printf( "%5d speed\n", dma.speed ); + Com_Printf( "0x%p dma buffer\n", dma.buffer ); + if ( streamingSounds[0].file ) { + Com_Printf( "Background file: %s\n", streamingSounds[0].loop ); + } else { + Com_Printf( "No background file.\n" ); + } + + } + Com_Printf( "----------------------\n" ); +} + +void S_ChannelSetup(); + +/* +================ +S_Init +================ +*/ +void S_Init( void ) { + cvar_t *cv; + qboolean r; + + Com_Printf( "\n------- sound initialization -------\n" ); + + s_mute = Cvar_Get( "s_mute", "0", CVAR_TEMP ); //----(SA) added + s_volume = Cvar_Get( "s_volume", "0.8", CVAR_ARCHIVE ); + s_musicVolume = Cvar_Get( "s_musicvolume", "0.25", CVAR_ARCHIVE ); + s_currentMusic = Cvar_Get( "s_currentMusic", "", CVAR_ROM ); + s_separation = Cvar_Get( "s_separation", "0.5", CVAR_ARCHIVE ); + s_doppler = Cvar_Get( "s_doppler", "1", CVAR_ARCHIVE ); + s_khz = Cvar_Get( "s_khz", "22", CVAR_ARCHIVE | CVAR_LATCH ); + s_mixahead = Cvar_Get( "s_mixahead", "0.2", CVAR_ARCHIVE ); + s_debugMusic = Cvar_Get( "s_debugMusic", "0", CVAR_TEMP ); + + s_mixPreStep = Cvar_Get( "s_mixPreStep", "0.05", CVAR_ARCHIVE ); + s_show = Cvar_Get( "s_show", "0", CVAR_CHEAT ); + s_testsound = Cvar_Get( "s_testsound", "0", CVAR_CHEAT ); + s_defaultsound = Cvar_Get( "s_defaultsound", "0", CVAR_ARCHIVE ); + s_wavonly = Cvar_Get( "s_wavonly", "0", CVAR_ARCHIVE | CVAR_LATCH ); + // Ridah + cl_cacheGathering = Cvar_Get( "cl_cacheGathering", "0", 0 ); + + // Rafael + s_nocompressed = Cvar_Get( "s_nocompressed", "0", CVAR_INIT ); + + // fretn + s_bits = Cvar_Get( "s_bits", "16", CVAR_LATCH | CVAR_ARCHIVE ); + s_numchannels = Cvar_Get( "s_channels", "2", CVAR_LATCH | CVAR_ARCHIVE ); + + + cv = Cvar_Get( "s_initsound", "1", 0 ); + if ( !cv->integer ) { + Com_Printf( "not initializing.\n" ); + Com_Printf( "------------------------------------\n" ); + return; + } + + crit = Sys_InitializeCriticalSection(); + + Cmd_AddCommand( "play", S_Play_f ); + Cmd_AddCommand( "music", S_Music_f ); + Cmd_AddCommand( "music_queue", S_QueueMusic_f ); + Cmd_AddCommand( "streamingsound", S_StreamingSound_f ); + Cmd_AddCommand( "s_list", S_SoundList_f ); + Cmd_AddCommand( "s_info", S_SoundInfo_f ); + Cmd_AddCommand( "s_stop", S_StopAllSounds ); + + r = SNDDMA_Init(); + Com_Printf( "------------------------------------\n" ); + + if ( r ) { + Sys_EnterCriticalSection( crit ); + + Com_Memset( &snd, 0, sizeof( snd ) ); +// Com_Memset(snd.sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH); + + snd.s_soundStarted = 1; + snd.s_soundMute = 1; +// snd.s_numSfx = 0; + + snd.volTarget = 0.0f; + //snd.volTarget = 1.0f; // full volume + + s_soundtime = 0; + s_paintedtime = 0; + + S_StopAllSounds(); + + S_SoundInfo_f(); + S_ChannelSetup(); + + Sys_LeaveCriticalSection( crit ); + } + +} + +/* +================ +S_ChannelFree +================ +*/ +void S_ChannelFree( channel_t *v ) { + v->thesfx = NULL; + v->threadReady = qfalse; +#ifdef _DEBUG + if ( v > &s_channels[MAX_CHANNELS] || v < &s_channels[0] ) { + Com_DPrintf( "s_channel OUT OF BOUNDS\n" ); + return; + } +#endif + *(channel_t **)snd.endflist = v; + snd.endflist = v; + *(channel_t **)v = NULL; +} + +/* +================ +S_ChannelMalloc +================ +*/ +channel_t* S_ChannelMalloc() { + channel_t *v; + if ( snd.freelist == NULL ) { + return NULL; + } + // RF, be careful not to lose our freelist + if ( *(channel_t **)snd.freelist == NULL ) { + return NULL; + } +#ifdef _DEBUG + if ( *(channel_t **)snd.freelist > &s_channels[MAX_CHANNELS] || *(channel_t **)snd.freelist < &s_channels[0] ) { //DAJ extra check + Com_DPrintf( "s_channel OUT OF BOUNDS\n" ); + return NULL; + } +#endif + v = snd.freelist; + snd.freelist = *(channel_t **)snd.freelist; + v->allocTime = Sys_Milliseconds(); + return v; +} + +/* +================ +S_ChannelSetup +================ +*/ +void S_ChannelSetup() { + channel_t *p, *q; + + // clear all the sounds so they don't + Com_Memset( s_channels, 0, sizeof( s_channels ) ); + + p = s_channels;; + q = p + MAX_CHANNELS; + while ( --q > p ) { + *(channel_t **)q = q - 1; + } + + snd.endflist = q; + *(channel_t **)q = NULL; + snd.freelist = p + MAX_CHANNELS - 1; + Com_DPrintf( "Channel memory manager started\n" ); +} + +/* +================ +S_Shutdown +================ +*/ +void S_Shutdown( void ) { + if ( !snd.s_soundStarted ) { + return; + } + + Sys_EnterCriticalSection( crit ); + + SNDDMA_Shutdown(); + snd.s_soundStarted = 0; + snd.s_soundMute = 1; + + Sys_LeaveCriticalSection( crit ); + + Cmd_RemoveCommand( "play" ); + Cmd_RemoveCommand( "music" ); + Cmd_RemoveCommand( "s_stop" ); + Cmd_RemoveCommand( "s_list" ); + Cmd_RemoveCommand( "s_info" ); +} + +/* +================ +S_HashSFXName + +return a hash value for the sfx name +================ +*/ +static long S_HashSFXName( const char *name ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while ( name[i] != '\0' ) { + letter = tolower( name[i] ); + if ( letter == '.' ) { + break; // don't include extension + } + if ( letter == '\\' ) { + letter = '/'; // damn path names + } + hash += (long)( letter ) * ( i + 119 ); + i++; + } + hash &= ( LOOP_HASH - 1 ); + return hash; +} + +/* +================== +S_FindName + +Will allocate a new sfx if it isn't found +================== +*/ +static sfx_t *S_FindName( const char *name ) { + int i; + int hash; + + sfx_t *sfx; + + if ( !name ) { + //Com_Error (ERR_FATAL, "S_FindName: NULL\n"); + name = "*default*"; + } + if ( !name[0] ) { + //Com_Error (ERR_FATAL, "S_FindName: empty name\n"); + name = "*default*"; + } + + if ( strlen( name ) >= MAX_QPATH ) { + Com_Error( ERR_FATAL, "Sound name too long: %s", name ); + } + + // Ridah, caching + if ( cl_cacheGathering->integer ) { + Cbuf_ExecuteText( EXEC_NOW, va( "cache_usedfile sound %s\n", name ) ); + } + + hash = S_HashSFXName( name ); + + sfx = snd.sfxHash[hash]; + // see if already loaded + while ( sfx ) { + if ( !Q_stricmp( sfx->soundName, name ) ) { + return sfx; + } + sfx = sfx->next; + } + + // find a free sfx + for ( i = 0 ; i < snd.s_numSfx ; i++ ) { + if ( !s_knownSfx[i].soundName[0] ) { + break; + } + } + + if ( i == snd.s_numSfx ) { + if ( snd.s_numSfx == MAX_SFX ) { + Com_Error( ERR_FATAL, "S_FindName: out of sfx_t" ); + } + snd.s_numSfx++; + } + + sfx = &s_knownSfx[i]; + Com_Memset( sfx, 0, sizeof( *sfx ) ); + strcpy( sfx->soundName, name ); + + sfx->next = snd.sfxHash[hash]; + snd.sfxHash[hash] = sfx; + + return sfx; +} + +/* +================= +S_DefaultSound +================= +*/ +void S_DefaultSound( sfx_t *sfx ) { + int i; + + if ( s_defaultsound->integer ) { + sfx->soundLength = 512; + } else { + sfx->soundLength = 8; + } + + sfx->soundData = SND_malloc(); + sfx->soundData->next = NULL; + + if ( s_defaultsound->integer ) { + for ( i = 0 ; i < sfx->soundLength ; i++ ) { + sfx->soundData->sndChunk[i] = i; + } + } else { + for ( i = 0 ; i < sfx->soundLength ; i++ ) { + sfx->soundData->sndChunk[i] = 0; + } + } +} + +/* +================ +S_Reload +================ +*/ +void S_Reload( void ) { + sfx_t *sfx; + int i; + + if ( !snd.s_soundStarted ) { + return; + } + + Com_Printf( "reloading sounds...\n" ); + + Sys_EnterCriticalSection( crit ); + + S_StopAllSounds(); + + for ( sfx = s_knownSfx, i = 0; i < snd.s_numSfx; i++, sfx++ ) { + sfx->inMemory = qfalse; + S_memoryLoad( sfx ); + } + + Sys_LeaveCriticalSection( crit ); +} + +/* +=================== +S_DisableSounds + +Disables sounds until the next S_BeginRegistration. +This is called when the hunk is cleared and the sounds +are no longer valid. +=================== +*/ +void S_DisableSounds( void ) { + S_StopAllSounds(); + snd.s_soundMute = 1; +} + +/* +===================== +S_BeginRegistration +===================== +*/ +void S_BeginRegistration( void ) { + sfx_t *sfx; + + snd.s_soundMute = 0; // we can play again + + if ( snd.s_numSfx == 0 ) { + SND_setup(); + + snd.s_numSfx = 0; + Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) ); + Com_Memset( snd.sfxHash, 0, sizeof( sfx_t * ) * LOOP_HASH ); + + sfx = S_FindName( "***DEFAULT***" ); + S_DefaultSound( sfx ); + } +} + +/* +================== +S_RegisterSound + +Creates a default buzz sound if the file can't be loaded +================== +*/ +sfxHandle_t S_RegisterSound( const char *name, qboolean compressed ) { + sfx_t *sfx; + + //compressed = qfalse; // Arnout: memory corruption with compressed sounds? + + if ( !snd.s_soundStarted ) { + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + Com_Printf( "Sound name exceeds MAX_QPATH\n" ); + return 0; + } + + sfx = S_FindName( name ); + if ( sfx->soundData ) { + if ( sfx->defaultSound ) { + if ( com_developer->integer ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + } + return 0; + } + return sfx - s_knownSfx; + } + + Sys_EnterCriticalSection( crit ); + + sfx->inMemory = qfalse; + sfx->soundCompressed = compressed; + +// if (!compressed) { + S_memoryLoad( sfx ); +// } + + Sys_LeaveCriticalSection( crit ); + + if ( sfx->defaultSound ) { + if ( com_developer->integer ) { + Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName ); + } + return 0; + } + + return sfx - s_knownSfx; +} + +/* +================= +S_memoryLoad +================= +*/ +void S_memoryLoad( sfx_t *sfx ) { + // load the sound file + if ( !S_LoadSound( sfx ) ) { +// Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName ); + sfx->defaultSound = qtrue; + } + sfx->inMemory = qtrue; +} + +//============================================================================= + +/* +================= +S_SpatializeOrigin + +Used for spatializing s_channels +================= +*/ +// xkan, 11/04/2002 - added noAttenuation arg. +void S_SpatializeOrigin( vec3_t origin, int master_vol, int *left_vol, int *right_vol, float range, int noAttenuation ) { +// vec_t dot; + vec_t dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + vec3_t vec; + +// const float dist_mult = SOUND_ATTENUATE; + float dist_mult, dist_fullvol; + + dist_fullvol = range * 0.064f; // default range of 1250 gives 80 + dist_mult = dist_fullvol * 0.00001f; // default range of 1250 gives .0008 +// dist_mult = range*0.00000064f; // default range of 1250 gives .0008 + + // calculate stereo seperation and distance attenuation + VectorSubtract( origin, listener_origin, source_vec ); + + dist = VectorNormalize( source_vec ); +// dist -= SOUND_FULLVOLUME; + dist -= dist_fullvol; + if ( dist < 0 || noAttenuation ) { + dist = 0; // close enough to be at full volume + + } + if ( dist ) { + dist = dist / range; // FIXME: lose the divide again + } +// dist *= dist_mult; // different attenuation levels + + VectorRotate( source_vec, listener_axis, vec ); + +// dot = -vec[1]; + + if ( dma.channels == 1 || noAttenuation ) { // no attenuation = no spatialization + rscale = 1.0; + lscale = 1.0; + } else + { + // xkan 11/22/2002 - the total energy of left + right should stay constant + // and the energy is proportional to the square of sound volume. + // + // therefore lscale and rscale should satisfy the following 2 conditions: + // lscale^2 + rscale^2 = 2 + // lscale^2 - rscale^2 = 2*vec[1] + // (the second condition here is more experimental than physical). + // These 2 conditions together give us the following solution. +// rscale = 0.5 * (1.0 + dot); +// lscale = 0.5 * (1.0 - dot); + rscale = sqrt( 1.0 - vec[1] ); + lscale = sqrt( 1.0 + vec[1] ); + //rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot; + //lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot; +/* if ( rscale < 0 ) { + rscale = 0; + } + if ( lscale < 0 ) { + lscale = 0; + }*/ + } + + // add in distance effect + scale = ( 1.0 - dist ) * rscale; + *right_vol = ( master_vol * scale ); + if ( *right_vol < 0 ) { + *right_vol = 0; + } + + scale = ( 1.0 - dist ) * lscale; + *left_vol = ( master_vol * scale ); + if ( *left_vol < 0 ) { + *left_vol = 0; + } +} + +/* +==================== +S_StartSound + +Validates the parms and queues the sound up +if pos is NULL, the sound will be dynamically sourced from the entity +Entchannel 0 will never override a playing sound + + flags: (currently apply only to non-looping sounds) + SND_NORMAL 0 - (default) allow sound to be cut off only by the same sound on this channel + SND_OKTOCUT 0x001 - allow sound to be cut off by any following sounds on this channel + SND_REQUESTCUT 0x002 - allow sound to be cut off by following sounds on this channel only for sounds who request cutoff + SND_CUTOFF 0x004 - cut off sounds on this channel that are marked 'SND_REQUESTCUT' + SND_CUTOFF_ALL 0x008 - cut off all sounds on this channel +==================== +*/ +void S_ThreadStartSoundEx( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle, int flags, int volume ); + +void S_StartSoundEx( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle, int flags, int volume ) { + if ( !snd.s_soundStarted || snd.s_soundMute || ( cls.state != CA_ACTIVE && cls.state != CA_DISCONNECTED ) ) { + return; + } + + // RF, we have lots of NULL sounds using up valuable channels, so just ignore them +/* if( !sfxHandle && entchannel != CHAN_WEAPON ) { // let null weapon sounds try to play. they kill any weapon sounds playing when a guy dies + Com_Printf( "^1WARNING: NULL sfx handle\n" ); + return; + }*/ + + // RF, make the call now, or else we could override following streaming sounds in the same frame, due to the delay + S_ThreadStartSoundEx( origin, entityNum, entchannel, sfxHandle, flags, volume ); +/* + if (snd.tart < MAX_PUSHSTACK) { + sfx_t *sfx; + if (origin) { + VectorCopy( origin, snd.pushPop[snd.tart].origin ); + snd.pushPop[snd.tart].fixedOrigin = qtrue; + } else { + snd.pushPop[snd.tart].fixedOrigin = qfalse; + } + snd.pushPop[snd.tart].entityNum = entityNum; + snd.pushPop[snd.tart].entityChannel = entchannel; + snd.pushPop[snd.tart].sfx = sfxHandle; + snd.pushPop[snd.tart].flags = flags; + // Gordon: more volume control, so more cookies from Tim! + snd.pushPop[snd.tart].volume = volume; // default was 127 + sfx = &s_knownSfx[ sfxHandle ]; + + if (sfx->inMemory == qfalse) { + S_memoryLoad(sfx); + } + + snd.tart++; + } +*/ +} + +void S_ThreadStartSoundEx( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle, int flags, int volume ) { + channel_t *ch; + sfx_t *sfx; + int i, oldest, chosen; + + chosen = -1; + if ( !snd.s_soundStarted || snd.s_soundMute ) { + return; + } + + if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) { + Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum ); + } + + if ( sfxHandle < 0 || sfxHandle >= snd.s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_StartSound: handle %i out of range\n", sfxHandle ); + return; + } + + Sys_EnterCriticalSection( crit ); + + sfx = &s_knownSfx[ sfxHandle ]; + + if ( s_show->integer == 1 ) { + Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName ); + } + + sfx->lastTimeUsed = Sys_Milliseconds(); + + // check for a streaming sound that this entity is playing in this channel + // kill it if it exists + if ( entityNum >= 0 ) { + for ( i = 1; i < MAX_STREAMING_SOUNDS; i++ ) { // track 0 is music/cinematics + if ( !streamingSounds[i].file ) { + continue; + } + // check to see if this character currently has another sound streaming on the same channel + if ( ( entchannel != CHAN_AUTO ) && ( streamingSounds[i].entnum >= 0 ) && ( streamingSounds[i].channel == entchannel ) && ( streamingSounds[i].entnum == entityNum ) ) { + // found a match, override this channel + streamingSounds[i].kill = 1; + break; + } + } + } + + ch = NULL; + +//----(SA) modified + + // shut off other sounds on this channel if necessary + for ( i = 0 ; i < MAX_CHANNELS ; i++ ) { + if ( s_channels[i].entnum == entityNum && s_channels[i].thesfx && s_channels[i].entchannel == entchannel ) { + + // cutoff all on channel + if ( flags & SND_CUTOFF_ALL ) { + S_ChannelFree( &s_channels[i] ); + continue; + } + + if ( s_channels[i].flags & SND_NOCUT ) { + continue; + } + + // RF, let client voice sounds be overwritten + + // TAT 11/2/2002 - don't do this, then if 2 bots try to talk at the same time, the 1st voice goes away. + // multiple people are allowed to talk at the same time + + // xkan, 11/21/2002 - restore this for all non-zero clients, for client 0, we sometimes do get (and want) + // multiple sounds on him. + // + /*if(entityNum != 0 && + entityNum < MAX_CLIENTS && s_channels[i].entchannel != CHAN_AUTO && s_channels[i].entchannel != CHAN_WEAPON) { + S_ChannelFree(&s_channels[i]); + continue; + }*/ + + + // cutoff sounds that expect to be overwritten + if ( s_channels[i].flags & SND_OKTOCUT ) { + S_ChannelFree( &s_channels[i] ); + continue; + } + + // cutoff 'weak' sounds on channel + if ( flags & SND_CUTOFF ) { + if ( s_channels[i].flags & SND_REQUESTCUT ) { + S_ChannelFree( &s_channels[i] ); + continue; + } + } + + } + } + + // re-use channel if applicable + for ( i = 0 ; i < MAX_CHANNELS ; i++ ) { + if ( s_channels[i].entnum == entityNum && s_channels[i].entchannel == entchannel && entchannel != CHAN_AUTO ) { + if ( !( s_channels[i].flags & SND_NOCUT ) && s_channels[i].thesfx == sfx ) { + ch = &s_channels[i]; + break; + } + } + } + + if ( !ch ) { + ch = S_ChannelMalloc(); + } +//----(SA) end + + if ( !ch ) { + ch = s_channels; + + oldest = sfx->lastTimeUsed; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( ch->entnum == entityNum && ch->thesfx == sfx ) { + chosen = i; + break; + } + if ( ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTime < oldest && ch->entchannel != CHAN_ANNOUNCER ) { + oldest = ch->allocTime; + chosen = i; + } + } + if ( chosen == -1 ) { + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( ch->entnum != listener_number && ch->allocTime < oldest && ch->entchannel != CHAN_ANNOUNCER ) { + oldest = ch->allocTime; + chosen = i; + } + } + if ( chosen == -1 ) { + if ( ch->entnum == listener_number ) { + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( ch->allocTime < oldest ) { + oldest = ch->allocTime; + chosen = i; + } + } + } + if ( chosen == -1 ) { + //Com_Printf("dropping sound\n"); + Sys_LeaveCriticalSection( crit ); + return; + } + } + } + ch = &s_channels[chosen]; + ch->allocTime = sfx->lastTimeUsed; + } + +#ifdef _DEBUG + if ( ch > &s_channels[MAX_CHANNELS] || ch < &s_channels[0] ) { //DAJ extra check + Com_DPrintf( "s_channel OUT OF BOUNDS\n" ); + Sys_LeaveCriticalSection( crit ); + return; + } +#endif + if ( origin ) { + VectorCopy( origin, ch->origin ); + ch->fixed_origin = qtrue; + } else { + ch->fixed_origin = qfalse; + } + + ch->flags = flags; //----(SA) added + ch->master_vol = volume; + ch->entnum = entityNum; + ch->thesfx = sfx; + ch->entchannel = entchannel; + ch->leftvol = ch->master_vol; // these will get calced at next spatialize + ch->rightvol = ch->master_vol; // unless the game isn't running + ch->doppler = qfalse; + + if ( ch->fixed_origin ) { + S_SpatializeOrigin( ch->origin, ch->master_vol, &ch->leftvol, &ch->rightvol, SOUND_RANGE_DEFAULT, flags & SND_NO_ATTENUATION ); + } else { + S_SpatializeOrigin( snd.entityPositions[ch->entnum], ch->master_vol, &ch->leftvol, &ch->rightvol, SOUND_RANGE_DEFAULT, flags & SND_NO_ATTENUATION ); + } + + ch->startSample = START_SAMPLE_IMMEDIATE; + ch->threadReady = qtrue; + + Sys_LeaveCriticalSection( crit ); +} + +/* +============== +S_StartSound +============== +*/ +void S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle, int volume ) { + S_StartSoundEx( origin, entityNum, entchannel, sfxHandle, 0, volume ); +} + + + +/* +================== +S_StartLocalSound +================== +*/ +void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum, int volume ) { + if ( !snd.s_soundStarted || snd.s_soundMute ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= snd.s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_StartLocalSound: handle %i out of range\n", sfxHandle ); + return; + } + + S_StartSound( NULL, listener_number, channelNum, sfxHandle, volume ); +} + + +/* +================== +S_ClearSoundBuffer + +If we are about to perform file access, clear the buffer +so sound doesn't stutter. +================== +*/ +void S_ClearSoundBuffer( qboolean killStreaming ) { + if ( !snd.s_soundStarted ) { + return; + } + + if ( !snd.s_soundPainted ) { // RF, buffers are clear, no point clearing again + return; + } + + snd.s_soundPainted = qfalse; + +// snd.s_clearSoundBuffer = 4; + snd.s_clearSoundBuffer = 3; + + S_ClearSounds( killStreaming, qtrue ); // do this now since you might not be allowed to in a sec (no multi-threaeded) +} + +/* +================== +S_StopAllSounds +================== +*/ +void S_StopAllSounds( void ) { + int i; + if ( !snd.s_soundStarted ) { + return; + } + + Sys_EnterCriticalSection( crit ); + +//DAJ BUGFIX for(i=0;i= MAX_LOOP_SOUNDS ) { + return; + } + + if ( !volume ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= snd.s_numSfx ) { + Com_Error( ERR_DROP, "S_AddLoopingSound: handle %i out of range", sfxHandle ); + return; + } + + Sys_EnterCriticalSection( crit ); + + sfx = &s_knownSfx[ sfxHandle ]; + + if ( sfx->inMemory == qfalse ) { + S_memoryLoad( sfx ); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + + // ydnar: allow looped sounds to start when initially triggered, rather than in the middle of the sample + snd.loopSounds[ snd.numLoopSounds ].startSample = sfx->soundLength + ? ( ( s_khz->integer * soundTime ) - s_paintedtime ) % sfx->soundLength + : 0; + + snd.loopSounds[ snd.numLoopSounds ].startSample = sfx->soundLength + ? s_paintedtime - ( (int) ( dma.speed * ( cl.serverTime - soundTime ) / 1000.0f ) ) + : 0; + + snd.loopSounds[ snd.numLoopSounds ].startSample = soundTime % sfx->soundLength; + + VectorCopy( origin, snd.loopSounds[snd.numLoopSounds].origin ); + VectorCopy( velocity, snd.loopSounds[snd.numLoopSounds].velocity ); + snd.loopSounds[snd.numLoopSounds].active = qtrue; + snd.loopSounds[snd.numLoopSounds].doppler = qfalse; + snd.loopSounds[snd.numLoopSounds].oldDopplerScale = 1.0; + snd.loopSounds[snd.numLoopSounds].dopplerScale = 1.0; + snd.loopSounds[snd.numLoopSounds].sfx = sfx; + if ( range ) { + snd.loopSounds[snd.numLoopSounds].range = range; + } else { + snd.loopSounds[snd.numLoopSounds].range = SOUND_RANGE_DEFAULT; + } + + if ( volume & 1 << UNDERWATER_BIT ) { + snd.loopSounds[snd.numLoopSounds].loudUnderWater = qtrue; + } + + if ( volume > 65535 ) { + volume = 65535; + } else if ( volume < 0 ) { + volume = 0; + } + + snd.loopSounds[snd.numLoopSounds].vol = (int)( (float)volume * snd.volCurrent ); //----(SA) modified + + if ( s_doppler->integer && VectorLengthSquared( velocity ) > 0.0 ) { + vec3_t out; + float lena, lenb; + + snd.loopSounds[snd.numLoopSounds].doppler = qtrue; + lena = DistanceSquared( snd.entityPositions[listener_number], snd.loopSounds[snd.numLoopSounds].origin ); + VectorAdd( snd.loopSounds[snd.numLoopSounds].origin, snd.loopSounds[snd.numLoopSounds].velocity, out ); + lenb = DistanceSquared( snd.entityPositions[listener_number], out ); + if ( ( snd.loopSounds[snd.numLoopSounds].framenum + 1 ) != cls.framecount ) { + snd.loopSounds[snd.numLoopSounds].oldDopplerScale = 1.0; + } else { + snd.loopSounds[snd.numLoopSounds].oldDopplerScale = snd.loopSounds[snd.numLoopSounds].dopplerScale; + } + snd.loopSounds[snd.numLoopSounds].dopplerScale = lenb / ( lena * 100 ); + if ( snd.loopSounds[snd.numLoopSounds].dopplerScale <= 1.0 ) { + snd.loopSounds[snd.numLoopSounds].doppler = qfalse; // don't bother doing the math + } + } + + snd.loopSounds[snd.numLoopSounds].framenum = cls.framecount; + snd.numLoopSounds++; + + Sys_LeaveCriticalSection( crit ); +} + +/* +================== +S_AddLoopingSound + +Called during entity generation for a frame +Include velocity in case I get around to doing doppler... +================== +*/ +void S_AddRealLoopingSound( const vec3_t origin, const vec3_t velocity, const int range, sfxHandle_t sfxHandle, int volume, int soundTime ) { + sfx_t *sfx; + + if ( !snd.s_soundStarted || snd.s_soundMute ) { + return; + } + + if ( snd.numLoopSounds >= MAX_LOOP_SOUNDS ) { + return; + } + + if ( !volume ) { + return; + } + + if ( sfxHandle < 0 || sfxHandle >= snd.s_numSfx ) { + Com_Printf( S_COLOR_YELLOW "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle ); + return; + } + + Sys_EnterCriticalSection( crit ); + + sfx = &s_knownSfx[ sfxHandle ]; + + if ( sfx->inMemory == qfalse ) { + S_memoryLoad( sfx ); + } + + if ( !sfx->soundLength ) { + Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); + } + + // ydnar: allow looped sounds to start when initially triggered, rather than in the middle of the sample + /*snd.loopSounds[ snd.numLoopSounds ].startSample = sfx->soundLength + ? ((s_khz->integer * soundTime) - s_paintedtime) % sfx->soundLength + : 0; + + snd.loopSounds[ snd.numLoopSounds ].startSample = sfx->soundLength + ? s_paintedtime - ((int) (dma.speed * (cl.serverTime - soundTime) / 1000.0f)) + : 0; + + snd.loopSounds[ snd.numLoopSounds ].startSample = soundTime % sfx->soundLength;*/ + + VectorCopy( origin, snd.loopSounds[snd.numLoopSounds].origin ); + VectorCopy( velocity, snd.loopSounds[snd.numLoopSounds].velocity ); + snd.loopSounds[snd.numLoopSounds].sfx = sfx; + snd.loopSounds[snd.numLoopSounds].active = qtrue; + snd.loopSounds[snd.numLoopSounds].doppler = qfalse; + + if ( range ) { + snd.loopSounds[snd.numLoopSounds].range = range; + } else { + snd.loopSounds[snd.numLoopSounds].range = SOUND_RANGE_DEFAULT; + } + + if ( volume & 1 << UNDERWATER_BIT ) { + snd.loopSounds[snd.numLoopSounds].loudUnderWater = qtrue; + } + + if ( volume > 65535 ) { + volume = 65535; + } else if ( volume < 0 ) { + volume = 0; + } + + snd.loopSounds[snd.numLoopSounds].vol = (int)( (float)volume * snd.volCurrent ); //----(SA) modified + snd.numLoopSounds++; + + Sys_LeaveCriticalSection( crit ); +} + +/* +================== +S_AddLoopSounds + +Spatialize all of the looping sounds. +All sounds are on the same cycle, so any duplicates can just +sum up the channel multipliers. +================== +*/ +void S_AddLoopSounds( void ) { + int i, j, time; + int left_total, right_total, left, right; + channel_t *ch; + loopSound_t *loop, *loop2; + static int loopFrame; + + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { +// Com_DPrintf ("not started or muted\n"); + return; + } + + Sys_EnterCriticalSection( crit ); + + numLoopChannels = 0; + + time = Sys_Milliseconds(); + + loopFrame++; + for ( i = 0 ; i < snd.numLoopSounds ; i++ ) { + loop = &snd.loopSounds[i]; + if ( !loop->active || loop->mergeFrame == loopFrame ) { + continue; // already merged into an earlier sound + } + + if ( loop->kill ) { + S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total, loop->range, qfalse ); // 3d + } else { + S_SpatializeOrigin( loop->origin, 90, &left_total, &right_total, loop->range, qfalse ); // sphere + } + + // adjust according to volume + left_total = (int)( (float)loop->vol * (float)left_total / 256.0 ); + right_total = (int)( (float)loop->vol * (float)right_total / 256.0 ); + + loop->sfx->lastTimeUsed = time; + + for ( j = ( i + 1 ); j < MAX_GENTITIES ; j++ ) { + loop2 = &snd.loopSounds[j]; + if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx || + loop2->startSample != loop->startSample ) { // ydnar + continue; + } + loop2->mergeFrame = loopFrame; + + if ( loop2->kill ) { + S_SpatializeOrigin( loop2->origin, 127, &left, &right, loop2->range, qfalse ); // 3d + } else { + S_SpatializeOrigin( loop2->origin, 90, &left, &right, loop2->range, qfalse ); // sphere + } + + // adjust according to volume + left = (int)( (float)loop2->vol * (float)left / 256.0 ); + right = (int)( (float)loop2->vol * (float)right / 256.0 ); + + loop2->sfx->lastTimeUsed = time; + left_total += left; + right_total += right; + } + if ( left_total == 0 && right_total == 0 ) { + continue; // not audible + } + + // allocate a channel + ch = &loop_channels[numLoopChannels]; + + if ( left_total > 255 ) { + left_total = 255; + } + if ( right_total > 255 ) { + right_total = 255; + } + + ch->master_vol = 127; + ch->leftvol = left_total; + ch->rightvol = right_total; + ch->thesfx = loop->sfx; + ch->doppler = loop->doppler; + ch->dopplerScale = loop->dopplerScale; + ch->oldDopplerScale = loop->oldDopplerScale; + + // ydnar: allow offsetting of sound samples + ch->startSample = loop->startSample; + + numLoopChannels++; + if ( numLoopChannels == MAX_CHANNELS ) { + i = snd.numLoopSounds + 1; + } + } + Sys_LeaveCriticalSection( crit ); +} + +//============================================================================= + +/* +================= +S_ByteSwapRawSamples + +If raw data has been loaded in little endien binary form, this must be done. +If raw data was calculated, as with ADPCM, this should not be called. +================= +*/ +//DAJ void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) { +void S_ByteSwapRawSamples( int samples, int width, int s_channels, short *data ) { + int i; + + if ( width != 2 ) { + return; + } +#ifndef __MACOS__ //DAJ save this test + if ( LittleShort( 256 ) == 256 ) { + return; + } +#endif + //DAJ use a faster loop technique + if ( s_channels == 2 ) { + i = samples << 1; + } else { + i = samples; + } + + do { + *data = LittleShort( *data ); + data++; +//DAJ ((short *)data)[i] = LittleShort( ((short *)data)[i] ); + } while ( --i ); +} + +/* +============ +S_GetRawSamplePointer +============ +*/ +portable_samplepair_t *S_GetRawSamplePointer() { + return s_rawsamples[0]; +} + +/* +============ +S_RawSamples + +Music streaming +============ +*/ +void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float lvol, float rvol, int streamingIndex ) { + int i; + int src, dst; + float scale; + int intVolumeL, intVolumeR; + + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { + return; + } + + // volume taken into account when mixed + s_rawVolume[streamingIndex].left = 256 * lvol; + s_rawVolume[streamingIndex].right = 256 * rvol; + + intVolumeL = 256; + intVolumeR = 256; + + if ( s_rawend[streamingIndex] < s_soundtime ) { + Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i (%i)\n", s_rawend[streamingIndex], s_soundtime, s_soundtime - s_rawend[streamingIndex] ); + s_rawend[streamingIndex] = s_soundtime; + } + + scale = (float)rate / dma.speed; + + //Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend); + if ( s_channels == 2 && width == 2 ) { + if ( scale == 1.0 ) { // optimized case + for ( i = 0; i < samples; i++ ) + { + dst = s_rawend[streamingIndex] & ( MAX_RAW_SAMPLES - 1 ); + s_rawend[streamingIndex]++; + s_rawsamples[streamingIndex][dst].left = ( (short *)data )[i * 2] * intVolumeL; + s_rawsamples[streamingIndex][dst].right = ( (short *)data )[i * 2 + 1] * intVolumeR; + } + } else + { + for ( i = 0; ; i++ ) + { + src = i * scale; + if ( src >= samples ) { + break; + } + dst = s_rawend[streamingIndex] & ( MAX_RAW_SAMPLES - 1 ); + s_rawend[streamingIndex]++; + s_rawsamples[streamingIndex][dst].left = ( (short *)data )[src * 2] * intVolumeL; + s_rawsamples[streamingIndex][dst].right = ( (short *)data )[src * 2 + 1] * intVolumeR; + } + } + } else if ( s_channels == 1 && width == 2 ) { + for ( i = 0; ; i++ ) + { + src = i * scale; + if ( src >= samples ) { + break; + } + dst = s_rawend[streamingIndex] & ( MAX_RAW_SAMPLES - 1 ); + s_rawend[streamingIndex]++; + s_rawsamples[streamingIndex][dst].left = ( (short *)data )[src] * intVolumeL; + s_rawsamples[streamingIndex][dst].right = ( (short *)data )[src] * intVolumeR; + } + } else if ( s_channels == 2 && width == 1 ) { + intVolumeL *= 256; + intVolumeR *= 256; + + for ( i = 0 ; ; i++ ) + { + src = i * scale; + if ( src >= samples ) { + break; + } + dst = s_rawend[streamingIndex] & ( MAX_RAW_SAMPLES - 1 ); + s_rawend[streamingIndex]++; + s_rawsamples[streamingIndex][dst].left = ( (char *)data )[src * 2] * intVolumeL; + s_rawsamples[streamingIndex][dst].right = ( (char *)data )[src * 2 + 1] * intVolumeR; + } + } else if ( s_channels == 1 && width == 1 ) { + intVolumeL *= 256; + intVolumeR *= 256; + + for ( i = 0; ; i++ ) + { + src = i * scale; + if ( src >= samples ) { + break; + } + dst = s_rawend[streamingIndex] & ( MAX_RAW_SAMPLES - 1 ); + s_rawend[streamingIndex]++; + s_rawsamples[streamingIndex][dst].left = ( ( (byte *)data )[src] - 128 ) * intVolumeL; + s_rawsamples[streamingIndex][dst].right = ( ( (byte *)data )[src] - 128 ) * intVolumeR; + } + } + + if ( s_rawend[streamingIndex] > ( s_soundtime + MAX_RAW_SAMPLES ) ) { + Com_DPrintf( "S_RawSamples: overflowed %i > %i (%i)\n", s_rawend[streamingIndex], s_soundtime + MAX_RAW_SAMPLES, s_rawend[streamingIndex] - ( s_soundtime + MAX_RAW_SAMPLES ) ); + } +} + +//============================================================================= + +/* +===================== +S_UpdateEntityPosition + +let the sound system know where an entity currently is +====================== +*/ +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) { + if ( entityNum < 0 || entityNum > MAX_GENTITIES ) { + Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum ); + } + VectorCopy( origin, snd.entityPositions[entityNum] ); +} + + +/* +============ +S_Respatialize + +Change the volumes of all the playing sounds for changes in their positions +============ +*/ +void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) { + + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { + return; + } + + listener_number = entityNum; + VectorCopy( head, listener_origin ); + VectorCopy( axis[0], listener_axis[0] ); + VectorCopy( axis[1], listener_axis[1] ); + VectorCopy( axis[2], listener_axis[2] ); +} + +void S_ThreadRespatialize() { + int i; + channel_t *ch; + vec3_t origin; + // update spatialization for dynamic sounds + ch = s_channels; + for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) { + if ( !ch->thesfx ) { + continue; + } + // anything coming from the view entity will always be full volume + if ( ch->entnum == listener_number ) { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + } else { + if ( ch->fixed_origin ) { + VectorCopy( ch->origin, origin ); + } else { + VectorCopy( snd.entityPositions[ ch->entnum ], origin ); + } + + S_SpatializeOrigin( origin, ch->master_vol, &ch->leftvol, &ch->rightvol, SOUND_RANGE_DEFAULT, ch->flags & SND_NO_ATTENUATION ); + } + } +} + +/* +======================== +S_ScanChannelStarts + +Returns qtrue if any new sounds were started since the last mix +======================== +*/ +qboolean S_ScanChannelStarts( void ) { + channel_t *ch; + int i; + qboolean newSamples; + + newSamples = qfalse; + ch = s_channels; + + for ( i = 0; i < MAX_CHANNELS; i++, ch++ ) { + if ( !ch->thesfx ) { + continue; + } + // if this channel was just started this frame, + // set the sample count to it begins mixing + // into the very first sample + if ( ch->startSample == START_SAMPLE_IMMEDIATE && ch->threadReady == qtrue ) { + ch->startSample = s_paintedtime; + newSamples = qtrue; + continue; + } + + // if it is completely finished by now, clear it + if ( ch->startSample + ( ch->thesfx->soundLength ) <= s_paintedtime ) { +//----(SA) got from TA sound. correct? +// Com_Memset(ch, 0, sizeof(*ch)); + S_ChannelFree( ch ); + } + } + + return newSamples; +} + +/* +============== +S_CheckForQueuedMusic +============== +*/ +int S_CheckForQueuedMusic( void ) { + streamingSound_t *ss; + char *nextMusicVA; + + if ( !snd.nextMusicTrack[0] ) { // we didn't actually care about the length + return 0; + } + + nextMusicVA = va( "%s", snd.nextMusicTrack ); + + ss = &streamingSounds[0]; + + if ( snd.nextMusicTrackType == QUEUED_PLAY_ONCE_SILENT ) { + // do nothing. current music is dead, don't start another + } else if ( snd.nextMusicTrackType == QUEUED_PLAY_ONCE ) { + S_StartBackgroundTrack( nextMusicVA, ss->name, 0 ); // play once, then go back to looping what's currently playing + } else { // QUEUED_PLAY_LOOPED + S_StartBackgroundTrack( nextMusicVA, nextMusicVA, 0 ); // take over + } + + snd.nextMusicTrackType = 0; // clear out music queue + snd.nextMusicTrack[0] = 0; // clear out music queue + + return 1; +} + +/* +============ +S_Update + +Called once each time through the main loop +============ +*/ + +void S_Update_Debug( void ) { + int i; + int total; + channel_t *ch; + + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { +// Com_DPrintf ("not started or muted\n"); + return; + } + + if ( s_show->integer == 2 ) { + total = 0; + ch = s_channels; + for ( i = 0; i < MAX_CHANNELS; i++, ch++ ) { + if ( ch->thesfx && ( ch->leftvol || ch->rightvol ) ) { + Com_Printf( "%i %i %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName ); // <- this is not thread safe + total++; + } + } + + Com_Printf( "----(%i)---- painted: %i\n", total, s_paintedtime ); + } +} + +void S_Update( void ) { + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { +// Com_DPrintf ("not started or muted\n"); + return; + } + + // add loopsounds + S_AddLoopSounds(); + // do all the rest + S_UpdateThread(); +} + +/* +============== +S_ClearSounds +============== +*/ +void S_ClearSounds( qboolean clearStreaming, qboolean clearMusic ) { + int clear; + int i; + channel_t *ch; + streamingSound_t *ss; + + Sys_EnterCriticalSection( crit ); + + // stop looping sounds + Com_Memset( snd.loopSounds, 0, MAX_GENTITIES * sizeof( loopSound_t ) ); + Com_Memset( loop_channels, 0, MAX_CHANNELS * sizeof( channel_t ) ); + numLoopChannels = 0; + + // RF, moved this up so streaming sounds dont get updated with the music, below, and leave us with a snippet off streaming sounds after we reload + if ( clearStreaming ) { // we don't want to stop guys with long dialogue from getting cut off by a file read + // RF, clear talking amplitudes + Com_Memset( s_entityTalkAmplitude, 0, sizeof( s_entityTalkAmplitude ) ); + + for ( i = 0, ss = streamingSounds; i < MAX_STREAMING_SOUNDS; i++, ss++ ) { + if ( i > 0 || clearMusic ) { + s_rawend[i] = 0; + ss->kill = 2; // get rid of it next sound update + } + } + + // RF, we should also kill all channels, since we are killing streaming sounds anyway (fixes siren in forest playing after a map_restart/loadgame + ch = s_channels; + for ( i = 0; i < MAX_CHANNELS; i++, ch++ ) { + if ( ch->thesfx ) { + S_ChannelFree( ch ); + } + } + + } + + if ( !clearMusic ) { + S_UpdateStreamingSounds(); //----(SA) added so music will get updated if not cleared + } else { + // music cleanup + snd.nextMusicTrack[0] = 0; + snd.nextMusicTrackType = 0; + } + + if ( clearStreaming && clearMusic ) { + if ( dma.samplebits == 8 ) { + clear = 0x80; + } else { + clear = 0; + } + + SNDDMA_BeginPainting(); + if ( dma.buffer ) { + // TTimo: due to a particular bug workaround in linux sound code, + // have to optionally use a custom C implementation of Com_Memset + // not affecting win32, we have #define Snd_Memset Com_Memset + // show_bug.cgi?id=371 + Snd_Memset( dma.buffer, clear, dma.samples * dma.samplebits / 8 ); + } + SNDDMA_Submit(); + + Sys_LeaveCriticalSection( crit ); + + // NERVE - SMF - clear out channels so they don't finish playing when audio restarts + S_ChannelSetup(); + } else { + Sys_LeaveCriticalSection( crit ); + } +} + +/* +============== +S_UpdateThread +============== +*/ +void S_UpdateThread( void ) { + + if ( !snd.s_soundStarted || ( snd.s_soundMute == 1 ) ) { +// Com_DPrintf ("not started or muted\n"); + return; + } + +#ifdef TALKANIM + // default to ZERO amplitude, overwrite if sound is playing + memset( s_entityTalkAmplitude, 0, sizeof( s_entityTalkAmplitude ) ); +#endif + + if ( snd.s_clearSoundBuffer ) { + S_ClearSounds( qtrue, (qboolean)( snd.s_clearSoundBuffer >= 4 ) ); //----(SA) modified + snd.s_clearSoundBuffer = 0; + } else { + Sys_EnterCriticalSection( crit ); + + S_ThreadRespatialize(); + // add raw data from streamed samples + S_UpdateStreamingSounds(); + // mix some sound + S_Update_Mix(); + + Sys_LeaveCriticalSection( crit ); + } +} + +/* +============ +S_GetSoundtime +============ +*/ +void S_GetSoundtime( void ) { + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = dma.samples / dma.channels; + + // it is possible to miscount buffers if it has wrapped twice between + // calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos(); + if ( samplepos < oldsamplepos ) { + buffers++; // buffer wrapped + + if ( s_paintedtime > 0x40000000 ) { // time to chop things off to avoid 32 bit limits + buffers = 0; + s_paintedtime = fullsamples; + S_StopAllSounds(); + } + } + oldsamplepos = samplepos; + + s_soundtime = buffers * fullsamples + samplepos / dma.channels; + +#if 0 +// check to make sure that we haven't overshot + if ( s_paintedtime < s_soundtime ) { + Com_DPrintf( "S_GetSoundtime : overflow\n" ); + s_paintedtime = s_soundtime; + } +#endif + + if ( dma.submission_chunk < 256 ) { + s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed; + } else { + s_paintedtime = s_soundtime + dma.submission_chunk; + } +} + +/* +============ +S_Update_Mix +============ +*/ +void S_Update_Mix( void ) { + unsigned endtime; + int samps; //, i; + static float lastTime = 0.0f; + float ma, op; + float thisTime, sane; + static int ot = -1; + + if ( !snd.s_soundStarted || snd.s_soundMute ) { + return; + } + + // RF, this isn't used anymore, since it was causing timing problems with streaming sounds, since the + // starting of the sound is delayed, it could cause streaming sounds to be cutoff, when the steaming sound was issued after + // this sound +/* + for(i=0;ivalue * dma.speed; + op = s_mixPreStep->value + sane * dma.speed * 0.01; + + if ( op < ma ) { + ma = op; + } + + // mix ahead of current position + endtime = s_soundtime + ma; + + // mix to an even submission block size + endtime = ( endtime + dma.submission_chunk - 1 ) + & ~( dma.submission_chunk - 1 ); + + // never mix more than the complete buffer + samps = dma.samples >> ( dma.channels - 1 ); + if ( endtime - s_soundtime > samps ) { + endtime = s_soundtime + samps; + } + + // global volume fading + + // endtime or s_paintedtime or s_soundtime... + if ( s_soundtime < snd.volTime2 ) { // still has fading to do + if ( s_soundtime > snd.volTime1 ) { // has started fading + snd.volFadeFrac = ( (float)( s_soundtime - snd.volTime1 ) / (float)( snd.volTime2 - snd.volTime1 ) ); + snd.volCurrent = ( ( 1.0 - snd.volFadeFrac ) * snd.volStart + snd.volFadeFrac * snd.volTarget ); + +//DAJ Com_DPrintf( "master vol: %f\n", snd.volCurrent ); + + } else { + snd.volCurrent = snd.volStart; + } + } else { + snd.volCurrent = snd.volTarget; + + if ( snd.stopSounds ) { + S_StopAllSounds(); // faded out, stop playing + snd.stopSounds = qfalse; + } + } + + SNDDMA_BeginPainting(); + S_PaintChannels( endtime ); + SNDDMA_Submit(); + + lastTime = thisTime; +} + +/* +=============================================================================== + +console functions + +=============================================================================== +*/ + +void S_Play_f( void ) { + int i; + sfxHandle_t h; + char name[256]; + + i = 1; + while ( i < Cmd_Argc() ) { + if ( !Q_strrchr( Cmd_Argv( i ), '.' ) ) { + Com_sprintf( name, sizeof( name ), "%s.wav", Cmd_Argv( 1 ) ); + } else { + Q_strncpyz( name, Cmd_Argv( i ), sizeof( name ) ); + } + h = S_RegisterSound( name, qfalse ); + if ( h ) { + S_StartLocalSound( h, CHAN_LOCAL_SOUND, 127 ); + } + i++; + } +} + +/* +============== +S_QueueMusic_f + console interface really just for testing +============== +*/ +void S_QueueMusic_f( void ) { + int type = -2; // default to setting this as the next continual loop + int c; + + c = Cmd_Argc(); + + if ( c == 3 ) { + type = atoi( Cmd_Argv( 2 ) ); + } + + if ( type != -1 ) { // clamp to valid values (-1, -2) + type = -2; + } + + // NOTE: could actually use this to touch the file now so there's not a hit when the queue'd music is played? + S_StartBackgroundTrack( Cmd_Argv( 1 ), Cmd_Argv( 1 ), type ); +} + +void S_Music_f( void ) { + int c; + + c = Cmd_Argc(); + + if ( c == 2 ) { + S_StartBackgroundTrack( Cmd_Argv( 1 ), Cmd_Argv( 1 ), 0 ); + } else if ( c == 3 ) { + S_StartBackgroundTrack( Cmd_Argv( 1 ), Cmd_Argv( 2 ), 0 ); + Q_strncpyz( streamingSounds[0].loop, Cmd_Argv( 2 ), sizeof( streamingSounds[0].loop ) ); + } else { + Com_Printf( "music [loopfile]\n" ); + return; + } + +} + +// Ridah, just for testing the streaming sounds +void S_StreamingSound_f( void ) { + int c; + + c = Cmd_Argc(); + + if ( c == 2 ) { + S_StartStreamingSound( Cmd_Argv( 1 ), 0, -1, 0, 0 ); + } else if ( c == 5 ) { + S_StartStreamingSound( Cmd_Argv( 1 ), 0, atoi( Cmd_Argv( 2 ) ), atoi( Cmd_Argv( 3 ) ), atoi( Cmd_Argv( 4 ) ) ); + } else { + Com_Printf( "streamingsound [entnum channel attenuation]\n" ); + return; + } + +} + +void S_SoundList_f( void ) { + int i; + sfx_t *sfx; + int size, total; + char type[4][16]; + char mem[2][16]; + + strcpy( type[0], "16bit" ); + strcpy( type[1], "adpcm" ); + strcpy( type[2], "daub4" ); + strcpy( type[3], "mulaw" ); + strcpy( mem[0], "paged out" ); + strcpy( mem[1], "resident " ); + total = 0; + for ( sfx = s_knownSfx, i = 0 ; i < snd.s_numSfx ; i++, sfx++ ) { + size = sfx->soundLength; + total += size; + Com_Printf( "%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] ); + } + Com_Printf( "Total resident: %i\n", total ); + S_DisplayFreeMemory(); +} + + +/* +=============================================================================== + +STREAMING SOUND + +=============================================================================== +*/ + +int FGetLittleLong( const fileHandle_t f ) { + int v; + + FS_Read( &v, sizeof( v ), f ); + + return LittleLong( v ); +} + +int FGetLittleShort( const fileHandle_t f ) { + short v; + + FS_Read( &v, sizeof( v ), f ); + + return LittleShort( v ); +} + +// returns the length of the data in the chunk, or 0 if not found +int S_FindWavChunk( const fileHandle_t f, const char *chunk ) { + char name[5]; + int len; + int r; + + name[4] = 0; + len = 0; + r = FS_Read( name, 4, f ); + if ( r != 4 ) { + return 0; + } + len = FGetLittleLong( f ); + if ( len < 0 || len > 0xfffffff ) { + len = 0; + return 0; + } + len = ( len + 1 ) & ~1; // pad to word boundary +// s_nextWavChunk += len + 8; + + if ( strcmp( name, chunk ) ) { + return 0; + } + + return len; +} + +/* +====================== +S_StartBackgroundTrack +====================== +*/ +void S_StartBackgroundTrack( const char *intro, const char *loop, int fadeupTime ) { + int len; + char dump[16]; +// char name[MAX_QPATH]; + char loopMusic[MAX_QPATH]; + streamingSound_t *ss; + fileHandle_t fh; + + // music is always track 0 + ss = &streamingSounds[0]; + +//----(SA) added + if ( fadeupTime < 0 ) { // queue, don't play until music fades to 0 or is stopped + // -1 - queue to play once then return to music + // -2 - queue to set as new looping music + + if ( intro && strlen( intro ) ) { + strcpy( snd.nextMusicTrack, intro ); + snd.nextMusicTrackType = fadeupTime; + if ( fadeupTime == -2 ) { + Cvar_Set( "s_currentMusic", intro ); //----(SA) so the savegame will have the right music + + } + if ( s_debugMusic->integer ) { + if ( fadeupTime == -1 ) { + Com_Printf( "MUSIC: StartBgTrack: queueing '%s' for play once\n", intro ); + } else if ( fadeupTime == -2 ) { + Com_Printf( "MUSIC: StartBgTrack: queueing '%s' as new loop\n", intro ); + } + } + + } else { + snd.nextMusicTrack[0] = 0; // clear out the next track so things go on as they are + snd.nextMusicTrackType = 0; // be quiet at the next opportunity + + // clear out looping sound in current music so that it'll stop when it's done + if ( ss && ss->loop ) { + ss->loop[0] = 0; // clear loop + } + + if ( s_debugMusic->integer ) { + Com_Printf( "S_StartBgTrack(): queue cleared\n" ); + } + } + + return; // don't actually start any queued sounds + } + + // clear out nextMusic +// snd.nextMusicTrack[0] = 0; +//----(SA) end + + if ( !snd.s_soundStarted || !crit ) { + return; + } + + Sys_EnterCriticalSection( crit ); + + if ( !intro ) { + intro = ""; + } + if ( !loop || !loop[0] ) { + Q_strncpyz( loopMusic, intro, sizeof( loopMusic ) ); + } else { + Q_strncpyz( loopMusic, loop, sizeof( loopMusic ) ); + } + + Cvar_Set( "s_currentMusic", "" ); //----(SA) so the savegame will have the right music + + if ( !Q_stricmp( loop, "onetimeonly" ) ) { // don't change the loop if you're playing a single hit + Q_strncpyz( loopMusic, ss->loop, sizeof( loopMusic ) ); + } + + Q_strncpyz( ss->loop, loopMusic, sizeof( ss->loop ) - 4 ); + + Q_strncpyz( ss->name, intro, sizeof( ss->name ) - 4 ); + COM_DefaultExtension( ss->name, sizeof( ss->name ), ".wav" ); + + // close the current sound if present, but DON'T reset s_rawend + if ( ss->file ) { + Sys_EndStreamedFile( ss->file ); + FS_FCloseFile( ss->file ); + ss->file = 0; + } + + if ( !intro[0] ) { + Com_DPrintf( "Fail to start: %s\n", ss->name ); // (SA) TEMP + Sys_LeaveCriticalSection( crit ); + return; + } + + ss->channel = 0; + ss->entnum = -1; + ss->attenuation = 0; + + fh = 0; + // + // open up a wav file and get all the info + // + FS_FOpenFileRead( ss->name, &fh, qtrue ); + if ( !fh ) { + Com_Printf( "Couldn't open streaming sound file %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return; + } + + // skip the riff wav header + + FS_Read( dump, 12, fh ); + + if ( !S_FindWavChunk( fh, "fmt " ) ) { + Com_Printf( "No fmt chunk in %s\n", ss->name ); + FS_FCloseFile( fh ); + Sys_LeaveCriticalSection( crit ); + return; + } + + // save name for soundinfo + ss->info.format = FGetLittleShort( fh ); + ss->info.channels = FGetLittleShort( fh ); + ss->info.rate = FGetLittleLong( fh ); + FGetLittleLong( fh ); + FGetLittleShort( fh ); + ss->info.width = FGetLittleShort( fh ) / 8; + + if ( ss->info.format != WAV_FORMAT_PCM ) { + FS_FCloseFile( fh ); + Com_Printf( "Not a microsoft PCM format wav: %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return; + } + + if ( ss->info.channels != 2 || ss->info.rate != 22050 ) { + Com_Printf( "WARNING: music file %s is not 22k stereo\n", ss->name ); + } + + if ( ( len = S_FindWavChunk( fh, "data" ) ) == 0 ) { + FS_FCloseFile( fh ); + Com_Printf( "No data chunk in %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return; + } + + if ( s_debugMusic->integer ) { + Com_Printf( "MUSIC: StartBgTrack:\n playing %s\n looping %s %d\n", intro, loopMusic, fadeupTime ); + } + + Cvar_Set( "s_currentMusic", loopMusic ); //----(SA) so the savegame will have the right music + + ss->info.samples = len / ( ss->info.width * ss->info.channels ); + + ss->samples = ss->info.samples; + + ss->fadeStartVol = 0; + ss->fadeStart = 0; + ss->fadeEnd = 0; + ss->fadeTargetVol = 0; + + if ( fadeupTime ) { + ss->fadeStart = s_soundtime; + ss->fadeEnd = s_soundtime + ( ( (float)( ss->info.rate ) / 1000.0f ) * fadeupTime ); +// ss->fadeStart = s_paintedtime; +// ss->fadeEnd = s_paintedtime + (((float)(ss->info.rate)/1000.0f ) * fadeupTime); + ss->fadeTargetVol = 1.0; + } + + // + // start the background streaming + // + Sys_BeginStreamedFile( fh, 0x10000 ); + + ss->looped = 0; //----(SA) added + + ss->file = fh; + ss->kill = 0; + numStreamingSounds++; + + Com_DPrintf( "S_StartBackgroundTrack - Success\n" ); + Sys_LeaveCriticalSection( crit ); +} + + +/* +============== +S_FadeAllSounds + +============== +*/ +void S_FadeAllSounds( float targetVol, int time, qboolean stopsounds ) { + // TAT 11/15/2002 + // Because of strange timing issues, sometimes we try to fade up before the fade down completed + // If that's the case, just force an immediate stop to all sounds + if ( s_soundtime < snd.volTime2 && snd.stopSounds ) { + S_StopAllSounds(); + } + + snd.volStart = snd.volCurrent; + snd.volTarget = targetVol; + + snd.volTime1 = s_soundtime; + snd.volTime2 = s_soundtime + ( ( (float)( dma.speed ) / 1000.0f ) * time ); + + snd.stopSounds = stopsounds; + + // instant + if ( !time ) { + snd.volTarget = snd.volStart = snd.volCurrent = targetVol; // set it + snd.volTime1 = snd.volTime2 = 0; // no fading + } +} + + +//----(SA) added +/* +============== +S_FadeStreamingSound +============== +*/ +void S_FadeStreamingSound( float targetVol, int time, int ssNum ) { + streamingSound_t *ss; + + if ( ssNum >= numStreamingSounds ) { // invalid sound + return; + } + + ss = &streamingSounds[ssNum]; + + if ( !ss ) { + return; + } + + if ( ss->kill ) { + return; + } + + ss->fadeStartVol = 1.0f; + + if ( ssNum == 0 ) { + if ( s_debugMusic->integer ) { + Com_Printf( "MUSIC: Fade: %0.2f %d\n", targetVol, time ); + } + } + + // get current fraction if already fading/faded + if ( ss->fadeStart ) { + if ( ss->fadeEnd <= s_soundtime ) { +// if(ss->fadeEnd <= s_paintedtime) + ss->fadeStartVol = ss->fadeTargetVol; + } else { + ss->fadeStartVol = ( (float)( s_soundtime - ss->fadeStart ) / (float)( ss->fadeEnd - ss->fadeStart ) ); + } +// ss->fadeStartVol = ( (float)(s_paintedtime - ss->fadeStart)/(float)(ss->fadeEnd - ss->fadeStart) ); + } + + ss->fadeStart = s_soundtime; + ss->fadeEnd = s_soundtime + ( ( (float)( ss->info.rate ) / 1000.0f ) * time ); +// ss->fadeStart = s_paintedtime; +// ss->fadeEnd = s_paintedtime + (((float)(ss->info.rate)/1000.0f ) * time); + ss->fadeTargetVol = targetVol; +} + + +/* +============== +S_GetStreamingFade +============== +*/ +float S_GetStreamingFade( streamingSound_t *ss ) { + float oldfrac, newfrac; + +// if(ss->kill) +// return 0; + + if ( !ss->fadeStart ) { + return 1.0f; // full volume + + } + if ( ss->fadeEnd <= s_soundtime ) { // it's hit it's target +// if(ss->fadeEnd <= s_paintedtime) { // it's hit it's target + if ( ss->fadeTargetVol <= 0 ) { // faded out. die next update + ss->kill = 1; + } + return ss->fadeTargetVol; + } + + newfrac = (float)( s_soundtime - ss->fadeStart ) / (float)( ss->fadeEnd - ss->fadeStart ); +// newfrac = (float)(s_paintedtime - ss->fadeStart)/(float)(ss->fadeEnd - ss->fadeStart); + oldfrac = 1.0f - newfrac; + + return ( oldfrac * ss->fadeStartVol ) + ( newfrac * ss->fadeTargetVol ); +} + +//----(SA) end + +/* +====================== +S_StartStreamingSound + + FIXME: record the starting cg.time of the sound, so we can determine the + position by looking at the current cg.time, this way pausing or loading a + savegame won't screw up the timing of important sounds +====================== +*/ +float S_StartStreamingSound( const char *intro, const char *loop, int entnum, int channel, int attenuation ) { + int len; + char dump[16]; +// char name[MAX_QPATH]; + int i; + streamingSound_t *ss; + fileHandle_t fh; + + if ( !crit || !snd.s_soundStarted || snd.s_soundMute || cls.state != CA_ACTIVE ) { + return 0; + } + + Sys_EnterCriticalSection( crit ); + if ( !intro || !intro[0] ) { + if ( loop && loop[0] ) { + intro = loop; + } else { + intro = ""; + } + } + Com_DPrintf( "S_StartStreamingSound( %s, %s, %i, %i, %i )\n", intro, loop, entnum, channel, attenuation ); + + // look for a free track, but first check for overriding a currently playing sound for this entity + ss = NULL; + if ( entnum >= 0 ) { + for ( i = 1; i < MAX_STREAMING_SOUNDS; i++ ) { // track 0 is music/cinematics + if ( !streamingSounds[i].file ) { + continue; + } + // check to see if this character currently has another sound streaming on the same channel + if ( ( channel != CHAN_AUTO ) && ( streamingSounds[i].entnum >= 0 ) && ( streamingSounds[i].channel == channel ) && ( streamingSounds[i].entnum == entnum ) ) { + // found a match, override this channel + streamingSounds[i].kill = 1; + ss = &streamingSounds[i]; // use this track to start the new stream + break; + } + } + } + if ( !ss ) { + // no need to override a current stream, so look for a free track + for ( i = 1; i < MAX_STREAMING_SOUNDS; i++ ) { // track 0 is music/cinematics + if ( !streamingSounds[i].file ) { + ss = &streamingSounds[i]; + break; + } + } + } + if ( !ss ) { + if ( !s_mute->integer ) { // don't do the print if you're muted + Com_Printf( "S_StartStreamingSound: No free streaming tracks\n" ); + } + Sys_LeaveCriticalSection( crit ); + return 0; + } + + if ( ss->loop && loop ) { + Q_strncpyz( ss->loop, loop, sizeof( ss->loop ) - 4 ); + } else { + ss->loop[0] = 0; + } + + Q_strncpyz( ss->name, intro, sizeof( ss->name ) - 4 ); + COM_DefaultExtension( ss->name, sizeof( ss->name ), ".wav" ); + + // close the current sound if present, but DON'T reset s_rawend + if ( ss->file ) { + Sys_EndStreamedFile( ss->file ); + FS_FCloseFile( ss->file ); + ss->file = 0; + } + + if ( !intro[0] ) { + Sys_LeaveCriticalSection( crit ); + return 0; + } + + fh = 0; + // + // open up a wav file and get all the info + // + FS_FOpenFileRead( ss->name, &fh, qtrue ); + if ( !fh ) { + Com_Printf( "Couldn't open streaming sound file %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return 0; + } + + // skip the riff wav header + + FS_Read( dump, 12, fh ); + + if ( !S_FindWavChunk( fh, "fmt " ) ) { + Com_Printf( "No fmt chunk in %s\n", ss->name ); + FS_FCloseFile( fh ); + Sys_LeaveCriticalSection( crit ); + return 0; + } + + // save name for soundinfo + ss->info.format = FGetLittleShort( fh ); + ss->info.channels = FGetLittleShort( fh ); + ss->info.rate = FGetLittleLong( fh ); + FGetLittleLong( fh ); + FGetLittleShort( fh ); + ss->info.width = FGetLittleShort( fh ) / 8; + + if ( ss->info.format != WAV_FORMAT_PCM ) { + FS_FCloseFile( fh ); + Com_Printf( "Not a microsoft PCM format wav: %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return 0; + } + + //if ( ss->info.channels != 2 || ss->info.rate != 22050 ) { + // Com_Printf("WARNING: music file %s is not 22k stereo\n", ss->name ); + //} + + if ( ( len = S_FindWavChunk( fh, "data" ) ) == 0 ) { + FS_FCloseFile( fh ); + Com_Printf( "No data chunk in %s\n", ss->name ); + Sys_LeaveCriticalSection( crit ); + return 0; + } + + ss->info.samples = len / ( ss->info.width * ss->info.channels ); + + ss->samples = ss->info.samples; + ss->channel = channel; + ss->attenuation = attenuation; + ss->entnum = entnum; + ss->kill = 0; + + ss->fadeStartVol = 0; + ss->fadeStart = 0; + ss->fadeEnd = 0; + ss->fadeTargetVol = 0; + + // + // start the background streaming + // + Sys_BeginStreamedFile( fh, 0x10000 ); + + ss->file = fh; + numStreamingSounds++; + Sys_LeaveCriticalSection( crit ); + + + return ( ss->samples / (float)ss->info.rate ) * 1000.f; +} + +/* +====================== +S_StopStreamingSound +====================== +*/ +void S_StopStreamingSound( int index ) { + if ( !streamingSounds[index].file ) { + return; + } + Sys_EnterCriticalSection( crit ); + streamingSounds[index].kill = 1; + Sys_LeaveCriticalSection( crit ); +} + +/* +============== +S_StopEntStreamingSound +============== +*/ +void S_StopEntStreamingSound( int entNum ) { + int i; + + for ( i = 1; i < MAX_STREAMING_SOUNDS; i++ ) { // track 0 is music/cinematics + if ( !streamingSounds[i].file ) { + continue; + } + + // Gordon: -1 = ALL now + if ( streamingSounds[i].entnum != entNum && entNum != -1 ) { + continue; + } + + S_StopStreamingSound( i ); + s_rawend[i] = 0; // stop it /now/ + } +} + +/* +====================== +S_StopBackgroundTrack +====================== +*/ +void S_StopBackgroundTrack( void ) { + S_StopStreamingSound( 0 ); +} + +/* +====================== +S_UpdateStreamingSounds +====================== +*/ +void S_UpdateStreamingSounds( void ) { + int bufferSamples; + int fileSamples; + byte raw[30000]; // just enough to fit in a mac stack frame + int fileBytes; + int r, i; + streamingSound_t *ss; + int *re, *rp; +// qboolean looped; + float lvol, rvol; + int soundMixAheadTime; + float streamingVol = 1.0f; + + if ( !snd.s_soundStarted || !crit ) { + return; + } + + // seems like the mute would be better down lower so no timing gets messed up + +// if ( s_mute->value ) { //----(SA) sound is muted, skip everything +// return; +// } + + soundMixAheadTime = s_soundtime; // + (int)(0.35 * dma.speed); // allow for talking animations + + snd.s_soundPainted = qtrue; + + for ( i = 0, ss = streamingSounds, re = s_rawend, rp = s_rawpainted; i < MAX_STREAMING_SOUNDS; i++, ss++, re++, rp++ ) { + if ( ss->kill && ss->file ) { + fileHandle_t file; + file = ss->file; + ss->file = 0; + Sys_EndStreamedFile( file ); + FS_FCloseFile( file ); + numStreamingSounds--; + + if ( i == 0 || ss->kill == 2 ) { // kill whole channel /now/ +// memset( &s_rawsamples[i], 0, MAX_RAW_SAMPLES*sizeof(portable_samplepair_t) ); + *re = 0; // reset rawend + + } + ss->kill = 0; + continue; + } + + *rp = qfalse; + + // don't bother playing anything if musicvolume is 0 + if ( i == 0 && s_musicVolume->value <= 0 ) { + continue; + } + if ( i > 0 && s_volume->value <= 0 ) { + continue; + } + + if ( !ss->file ) { + if ( i == 0 ) { // music + // quiet now, so start up queued music if it exists + S_CheckForQueuedMusic(); + } + continue; // skip until next frame + } + + // see how many samples should be copied into the raw buffer + if ( *re < soundMixAheadTime ) { // RF, read a bit ahead of time to allow for talking animations + *re = soundMixAheadTime; + } + +// looped = qfalse; + + while ( *re < soundMixAheadTime + MAX_RAW_SAMPLES ) { + bufferSamples = MAX_RAW_SAMPLES - ( *re - soundMixAheadTime ); + + // decide how much data needs to be read from the file + fileSamples = bufferSamples * ss->info.rate / dma.speed; + + // if there are no samples due to be read this frame, abort painting + // but keep the streaming going, since it might just need to wait until + // the next frame before it needs to paint some more + if ( !fileSamples ) { + break; + } + + // don't try and read past the end of the file + if ( fileSamples > ss->samples ) { + fileSamples = ss->samples; + } + + // our max buffer size + fileBytes = fileSamples * ( ss->info.width * ss->info.channels ); + if ( fileBytes > sizeof( raw ) ) { + fileBytes = sizeof( raw ); + fileSamples = fileBytes / ( ss->info.width * ss->info.channels ); + } + + r = Sys_StreamedRead( raw, 1, fileBytes, ss->file ); + if ( r != fileBytes ) { + Com_DPrintf( "StreamedRead failure on stream sound\n" ); + ss->kill = 1; + break; + } + + // byte swap if needed + S_ByteSwapRawSamples( fileSamples, ss->info.width, ss->info.channels, (short*)raw ); + + // calculate the volume + streamingVol = S_GetStreamingFade( ss ); + + streamingVol *= snd.volCurrent; // get current global volume level + + if ( s_mute->value ) { //----(SA) sound is muted. process to maintain timing, but play at 0 volume + streamingVol = 0; + } + + if ( i == 0 ) { // music + lvol = rvol = s_musicVolume->value * streamingVol; + } else { // attenuate if required + if ( ss->entnum >= 0 && ss->attenuation ) { + int r, l; + S_SpatializeOrigin( snd.entityPositions[ ss->entnum ], s_volume->value * 255.0f, &l, &r, SOUND_RANGE_DEFAULT, qfalse ); + if ( ( lvol = ( (float)l / 255.0 ) ) > 1.0 ) { + lvol = 1.0; + } + if ( ( rvol = ( (float)r / 255.0 ) ) > 1.0 ) { + rvol = 1.0; + } + lvol *= streamingVol; + rvol *= streamingVol; + } else { + lvol = rvol = s_volume->value * streamingVol; + } + } + + // add to raw buffer + S_RawSamples( fileSamples, ss->info.rate, + ss->info.width, ss->info.channels, raw, lvol, rvol, i ); + + *rp = qtrue; + + ss->samples -= fileSamples; + + if ( !ss->samples ) { // at the end of the sound + + // Queued music will take over as the new loop + // start up queued music if it exists + if ( i == 0 && snd.nextMusicTrackType ) { // queued music is queued + if ( ss->file ) { + fileHandle_t file; + file = ss->file; + ss->file = 0; + Sys_EndStreamedFile( file ); + FS_FCloseFile( file ); + numStreamingSounds--; +// memset( &s_rawsamples[i], 0, MAX_RAW_SAMPLES*sizeof(portable_samplepair_t) ); // really clear it + s_rawend[i] = 0; // reset rawend + } +/* + nextMusicVA = va("%s", snd.nextMusicTrack); + if(snd.nextMusicTrackType == QUEUED_PLAY_ONCE) { + S_StartBackgroundTrack( nextMusicVA, ss->name, 0); // play once, then go back to looping what's currently playing + } else { // QUEUED_PLAY_LOOPED + S_StartBackgroundTrack( nextMusicVA, nextMusicVA, 0); // take over + } + snd.nextMusicTrack[0] = 0; // clear out music queue +*/ + break; // this is now the music ss->file, no need to re-start next time through + } else { + // loop + if ( ss->loop && ss->loop[0] ) { + if ( ss->looped ) { + char dump[16]; + Sys_StreamSeek( ss->file, 0, FS_SEEK_SET ); // just go back to the beginning + FS_Read( dump, 12, ss->file ); + + if ( !S_FindWavChunk( ss->file, "fmt " ) ) { + ss->kill = 1; + break; + } + + // save name for soundinfo + ss->info.format = FGetLittleShort( ss->file ); + ss->info.channels = FGetLittleShort( ss->file ); + ss->info.rate = FGetLittleLong( ss->file ); + FGetLittleLong( ss->file ); + FGetLittleShort( ss->file ); + ss->info.width = FGetLittleShort( ss->file ) / 8; + ss->samples = ss->info.samples; + if ( ( S_FindWavChunk( ss->file, "data" ) ) == 0 ) { + ss->kill = 1; + } + if ( s_debugMusic->integer ) { + Com_Printf( "MUSIC: looping current track\n" ); + } + break; + } else { // start up the sound + S_StartBackgroundTrack( ss->loop, ss->loop, 0 ); + ss->looped = qtrue; // this is now the music ss->file, no need to re-start next time through + break; + } + + // no loop, just stop + } else { + ss->kill = 1; + if ( i == 0 ) { + Cvar_Set( "s_currentMusic", "" ); //----(SA) so the savegame know's it's supposed to be quiet + + if ( s_debugMusic->integer ) { + Com_Printf( "MUSIC: Ending current track-> no loop\n" ); + } + } + break; + } + } + } + } + } +} + +/* +====================== +S_FreeOldestSound +====================== +*/ +void S_FreeOldestSound( void ) { + int i, oldest, used; + sfx_t *sfx; + sndBuffer *buffer, *nbuffer; + + oldest = Sys_Milliseconds(); + used = 0; + + for ( i = 1 ; i < snd.s_numSfx ; i++ ) { + sfx = &s_knownSfx[i]; + if ( sfx->inMemory && sfx->lastTimeUsed < oldest ) { + used = i; + oldest = sfx->lastTimeUsed; + } + } + + sfx = &s_knownSfx[used]; + + Com_DPrintf( "S_FreeOldestSound: freeing sound %s\n", sfx->soundName ); + + buffer = sfx->soundData; + while ( buffer != NULL ) { + nbuffer = buffer->next; + SND_free( buffer ); + buffer = nbuffer; + } + sfx->inMemory = qfalse; + sfx->soundData = NULL; +} + +// START xkan, 9/23/2002 +// returns how long the sound lasts in milliseconds +int S_GetSoundLength( sfxHandle_t sfxHandle ) { + if ( sfxHandle < 0 || sfxHandle >= snd.s_numSfx ) { + Com_DPrintf( S_COLOR_YELLOW "S_StartSound: handle %i out of range\n", sfxHandle ); + return -1; + } + return (int)( (float)s_knownSfx[ sfxHandle ].soundLength / dma.speed * 1000.0 ); +} +// END xkan, 9/23/2002 + +// ydnar: for looped sound synchronization +int S_GetCurrentSoundTime( void ) { + return s_soundtime + dma.speed; +// return s_paintedtime; +} diff --git a/src/client/snd_local.h b/src/client/snd_local.h new file mode 100644 index 0000000..4e0ff13 --- /dev/null +++ b/src/client/snd_local.h @@ -0,0 +1,323 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + +// snd_local.h -- private sound definations + + +#include "../game/q_shared.h" +#include "../qcommon/qcommon.h" +#include "snd_public.h" + +#define PAINTBUFFER_SIZE 4096 // this is in samples + +#define SND_CHUNK_SIZE 1024 // samples +#define SND_CHUNK_SIZE_FLOAT ( SND_CHUNK_SIZE / 2 ) // floats +#define SND_CHUNK_SIZE_BYTE ( SND_CHUNK_SIZE * 2 ) // floats + +#define TALKANIM + +typedef struct { + int left; // the final values will be clamped to +/- 0x00ffff00 and shifted down + int right; +} portable_samplepair_t; + +typedef struct adpcm_state { + short sample; /* Previous output value */ + char index; /* Index into stepsize table */ +#if defined( __MACOS__ ) + char pad; /* //DAJ added pad for alignment */ +#endif +} adpcm_state_t; + +typedef struct sndBuffer_s { + short sndChunk[SND_CHUNK_SIZE]; + struct sndBuffer_s *next; + int size; + adpcm_state_t adpcm; +} sndBuffer; + +typedef struct sfx_s { + sndBuffer *soundData; + qboolean defaultSound; // couldn't be loaded, so use buzz + qboolean inMemory; // not in Memory + qboolean soundCompressed; // not in Memory + int soundCompressionMethod; + int soundLength; + char soundName[MAX_QPATH]; + int lastTimeUsed; + struct sfx_s *next; +} sfx_t; + +typedef struct { + int channels; + int samples; // mono samples in buffer + int submission_chunk; // don't mix less than this # + int samplebits; + int speed; + int samplepos; + byte *buffer; +} dma_t; + +#define START_SAMPLE_IMMEDIATE 0x7fffffff + +typedef struct loopSound_s { + vec3_t origin; + vec3_t velocity; + sfx_t *sfx; + int mergeFrame; + qboolean active; + qboolean kill; + qboolean doppler; + float dopplerScale; + float oldDopplerScale; + int framenum; + float range; //----(SA) added + int vol; + qboolean loudUnderWater; // (SA) set if this sound should be played at full vol even when under water (under water loop sound for ex.) + int startTime, startSample; // ydnar: so looping sounds can be out of phase +} loopSound_t; + +typedef struct +{ + int *prt; //DAJ BUGFIX for freelist/endlist pointer + int allocTime; + int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix + int entnum; // to allow overriding a specific sound + int entchannel; // to allow overriding a specific sound + int leftvol; // 0-255 volume after spatialization + int rightvol; // 0-255 volume after spatialization + int master_vol; // 0-255 volume before spatialization + float dopplerScale; + float oldDopplerScale; + vec3_t origin; // only use if fixed_origin is set + qboolean fixed_origin; // use origin instead of fetching entnum's origin + sfx_t *thesfx; // sfx structure + qboolean doppler; + int flags; //----(SA) added + qboolean threadReady; +} channel_t; + + +#define WAV_FORMAT_PCM 1 + + +typedef struct { + int format; + int rate; + int width; + int channels; + int samples; + int dataofs; // chunk starts this many bytes from file start +} wavinfo_t; + + +/* +==================================================================== + + SYSTEM SPECIFIC FUNCTIONS + +==================================================================== +*/ + +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init( void ); + +// gets the current DMA position +int SNDDMA_GetDMAPos( void ); + +// shutdown the DMA xfer. +void SNDDMA_Shutdown( void ); + +void SNDDMA_BeginPainting( void ); + +void SNDDMA_Submit( void ); + +//==================================================================== + +#if defined( __MACOS__ ) +#define MAX_CHANNELS 64 +#else +#define MAX_CHANNELS 96 +#endif + +extern channel_t s_channels[MAX_CHANNELS]; +extern channel_t loop_channels[MAX_CHANNELS]; +extern int numLoopChannels; + +extern int s_paintedtime; +extern vec3_t listener_forward; +extern vec3_t listener_right; +extern vec3_t listener_up; +extern dma_t dma; + +#ifdef TALKANIM +extern unsigned char s_entityTalkAmplitude[MAX_CLIENTS]; +#endif + +//----(SA) some flags for queued music tracks +#define QUEUED_PLAY_ONCE -1 +#define QUEUED_PLAY_LOOPED -2 +#define QUEUED_PLAY_ONCE_SILENT -3 // when done it goes quiet +//----(SA) end + +// Ridah, streaming sounds +typedef struct { + fileHandle_t file; + wavinfo_t info; + int samples; + char name[MAX_QPATH]; //----(SA) added + char loop[MAX_QPATH]; + int looped; //----(SA) added + int entnum; + int channel; + int attenuation; + int kill; //----(SA) changed + + int fadeStart; //----(SA) added + int fadeEnd; //----(SA) added + float fadeStartVol; //----(SA) added + float fadeTargetVol; //----(SA) added +} streamingSound_t; + +typedef struct { + vec3_t origin; + qboolean fixedOrigin; + int entityNum; + int entityChannel; + sfxHandle_t sfx; + int flags; + int volume; +} s_pushStack; + +#define MAX_PUSHSTACK 64 +#define LOOP_HASH 128 +#define MAX_LOOP_SOUNDS 1024 + +// removed many statics into a common sound struct +typedef struct { + sfx_t *sfxHash[LOOP_HASH]; + int numLoopSounds; + loopSound_t loopSounds[MAX_LOOP_SOUNDS]; + + float volTarget; + float volStart; + int volTime1; + int volTime2; + float volFadeFrac; + float volCurrent; + + qboolean stopSounds; + + channel_t *freelist; + channel_t *endflist; + + int s_numSfx; + + s_pushStack pushPop[MAX_PUSHSTACK]; + int tart; + + qboolean s_soundPainted; + int s_clearSoundBuffer; + + int s_soundStarted; + int s_soundMute; // 0 - not muted, 1 - muted, 2 - no new sounds, but play out remaining sounds (so they can die if necessary) + + vec3_t entityPositions[MAX_GENTITIES]; + + char nextMusicTrack[MAX_QPATH]; // extracted from CS_MUSIC_QUEUE //----(SA) added + int nextMusicTrackType; +} snd_t; + +extern snd_t snd; // globals for sound + +#define MAX_STREAMING_SOUNDS 12 // need to keep it low, or the rawsamples will get too big +#define MAX_RAW_SAMPLES 16384 + +extern streamingSound_t streamingSounds[MAX_STREAMING_SOUNDS]; +extern int s_rawend[MAX_STREAMING_SOUNDS]; +extern portable_samplepair_t s_rawsamples[MAX_STREAMING_SOUNDS][MAX_RAW_SAMPLES]; +extern portable_samplepair_t s_rawVolume[MAX_STREAMING_SOUNDS]; + + +extern cvar_t *s_volume; +extern cvar_t *s_nosound; +extern cvar_t *s_khz; +extern cvar_t *s_show; +extern cvar_t *s_mixahead; +extern cvar_t *s_mute; + +extern cvar_t *s_testsound; +extern cvar_t *s_separation; +extern cvar_t *s_currentMusic; //----(SA) added +extern cvar_t *s_debugMusic; //----(SA) added + +// fretn +extern cvar_t *s_bits; +extern cvar_t *s_numchannels; + +qboolean S_LoadSound( sfx_t *sfx ); + +void SND_free( sndBuffer *v ); +sndBuffer* SND_malloc(); +void SND_setup(); + +void S_PaintChannels( int endtime ); + +void S_memoryLoad( sfx_t *sfx ); +portable_samplepair_t *S_GetRawSamplePointer(); + +// spatializes a channel +void S_Spatialize( channel_t *ch ); + +// adpcm functions +int S_AdpcmMemoryNeeded( const wavinfo_t *info ); +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ); +void S_AdpcmGetSamples( sndBuffer *chunk, short *to ); + +// wavelet function + +#define SENTINEL_MULAW_ZERO_RUN 127 +#define SENTINEL_MULAW_FOUR_BIT_RUN 126 + +void S_FreeOldestSound(); + +#define NXStream byte + +void encodeWavelet( sfx_t *sfx, short *packets ); +void decodeWavelet( sndBuffer *stream, short *packets ); + +void encodeMuLaw( sfx_t *sfx, short *packets ); +extern short mulawToShort[256]; + +extern short *sfxScratchBuffer; +extern const sfx_t *sfxScratchPointer; +extern int sfxScratchIndex; + +extern unsigned char s_entityTalkAmplitude[MAX_CLIENTS]; + +extern float S_GetStreamingFade( streamingSound_t *ss ); //----(SA) added diff --git a/src/client/snd_mem.c b/src/client/snd_mem.c new file mode 100644 index 0000000..14df913 --- /dev/null +++ b/src/client/snd_mem.c @@ -0,0 +1,555 @@ +/* +=========================================================================== + +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: snd_mem.c + * + * desc: sound caching + * + * $Archive: /Wolfenstein MP/src/client/snd_mem.c $ + * + *****************************************************************************/ + +#include "snd_local.h" + +#define DEF_COMSOUNDMEGS "24" // (SA) upped for GD + +/* +=============================================================================== + +SOUND MEMORY MANAGENT + +=============================================================================== +*/ + +static sndBuffer *buffer = NULL; +static sndBuffer *freelist = NULL; +static int inUse = 0; +static int totalInUse = 0; +static int totalAllocated = 0; + +short *sfxScratchBuffer = NULL; +const sfx_t *sfxScratchPointer = NULL; +int sfxScratchIndex = 0; + +extern cvar_t *s_nocompressed; + +/* +================ +SND_free +================ +*/ +void SND_free( sndBuffer *v ) { + *(sndBuffer **)v = freelist; + freelist = (sndBuffer*)v; + inUse += sizeof( sndBuffer ); + totalInUse -= sizeof( sndBuffer ); +} + +/* +================ +SND_malloc +================ +*/ +sndBuffer* SND_malloc() { + sndBuffer *v; + + while ( freelist == NULL ) { + S_FreeOldestSound(); + } + + inUse -= sizeof( sndBuffer ); + totalInUse += sizeof( sndBuffer ); + totalAllocated += sizeof( sndBuffer ); + + v = freelist; + freelist = *(sndBuffer **)freelist; + v->next = NULL; + return v; +} + +/* +================ +SND_setup +================ +*/ +void SND_setup() { + sndBuffer *p, *q; + cvar_t *cv; + int scs; + + cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE ); + + scs = cv->integer * 512; + + buffer = malloc( scs * sizeof( sndBuffer ) ); + // allocate the stack based hunk allocator + sfxScratchBuffer = malloc( SND_CHUNK_SIZE * sizeof( short ) * 4 ); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4); + sfxScratchPointer = NULL; + + inUse = scs * sizeof( sndBuffer ); + totalInUse = 0; + totalAllocated = 0; + p = buffer;; + q = p + scs; + while ( --q > p ) { + *(sndBuffer **)q = q - 1; + } + *(sndBuffer **)q = NULL; + freelist = p + scs - 1; + + Com_Printf( "Sound memory manager started\n" ); +} + +/* +=============================================================================== + +WAV loading + +=============================================================================== +*/ + +static byte *data_p; +static byte *iff_end; +static byte *last_chunk; +static byte *iff_data; +static int iff_chunk_len; + +/* +================ +GetLittleShort +================ +*/ +static short GetLittleShort( void ) { + short val = 0; + val = *data_p; + val = val + ( *( data_p + 1 ) << 8 ); + data_p += 2; + return val; +} + +/* +================ +GetLittleLong +================ +*/ +static int GetLittleLong( void ) { + int val = 0; + val = *data_p; + val = val + ( *( data_p + 1 ) << 8 ); + val = val + ( *( data_p + 2 ) << 16 ); + val = val + ( *( data_p + 3 ) << 24 ); + data_p += 4; + return val; +} + +/* +================ +FindNextChunk +================ +*/ +static void FindNextChunk( char *name ) { + while ( 1 ) + { + data_p = last_chunk; + + if ( data_p >= iff_end ) { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong(); + if ( iff_chunk_len < 0 ) { + data_p = NULL; + return; + } + data_p -= 8; + last_chunk = data_p + 8 + ( ( iff_chunk_len + 1 ) & ~1 ); + if ( !strncmp( (char *)data_p, name, 4 ) ) { + return; + } + } +} + +/* +================ +FindChunk +================ +*/ +static void FindChunk( char *name ) { + last_chunk = iff_data; + FindNextChunk( name ); +} + +typedef struct waveFormat_s { + const char *name; + int format; +} waveFormat_t; + +static waveFormat_t waveFormats[] = { + { "Windows PCM", 1 }, + { "Antex ADPCM", 14 }, + { "Antex ADPCME", 33 }, + { "Antex ADPCM", 40 }, + { "Audio Processing Technology", 25 }, + { "Audiofile, Inc.", 24 }, + { "Audiofile, Inc.", 26 }, + { "Control Resources Limited", 34 }, + { "Control Resources Limited", 37 }, + { "Creative ADPCM", 200 }, + { "Dolby Laboratories", 30 }, + { "DSP Group, Inc", 22 }, + { "DSP Solutions, Inc.", 15 }, + { "DSP Solutions, Inc.", 16 }, + { "DSP Solutions, Inc.", 35 }, + { "DSP Solutions ADPCM", 36 }, + { "Echo Speech Corporation", 23 }, + { "Fujitsu Corp.", 300 }, + { "IBM Corporation", 5 }, + { "Ing C. Olivetti & C., S.p.A.", 1000 }, + { "Ing C. Olivetti & C., S.p.A.", 1001 }, + { "Ing C. Olivetti & C., S.p.A.", 1002 }, + { "Ing C. Olivetti & C., S.p.A.", 1003 }, + { "Ing C. Olivetti & C., S.p.A.", 1004 }, + { "Intel ADPCM", 11 }, + { "Intel ADPCM", 11 }, + { "Unknown", 0 }, + { "Microsoft ADPCM", 2 }, + { "Microsoft Corporation", 6 }, + { "Microsoft Corporation", 7 }, + { "Microsoft Corporation", 31 }, + { "Microsoft Corporation", 50 }, + { "Natural MicroSystems ADPCM", 38 }, + { "OKI ADPCM", 10 }, + { "Sierra ADPCM", 13 }, + { "Speech Compression", 21 }, + { "Videologic ADPCM", 12 }, + { "Yamaha ADPCM", 20 }, + { NULL, 0 } +}; + +static const char *GetWaveFormatName( const int format ) { + int i = 0; + + while ( waveFormats[i].name ) { + if ( format == waveFormats[i].format ) { + return( waveFormats[i].name ); + } + i++; + } + + return( "Unknown" ); + +} + +/* +============ +GetWavinfo +============ +*/ +static wavinfo_t GetWavinfo( char *name, byte *wav, int wavlength ) { + wavinfo_t info; + + Com_Memset( &info, 0, sizeof( info ) ); + + if ( !wav ) { + return info; + } + + iff_data = wav; + iff_end = wav + wavlength; + +// find "RIFF" chunk + FindChunk( "RIFF" ); + if ( !( data_p && !strncmp( (char *)data_p + 8, "WAVE", 4 ) ) ) { + Com_Printf( "Missing RIFF/WAVE chunks\n" ); + return info; + } + +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk( "fmt " ); + if ( !data_p ) { + Com_Printf( "Missing fmt chunk\n" ); + return info; + } + data_p += 8; + info.format = GetLittleShort(); + info.channels = GetLittleShort(); + info.rate = GetLittleLong(); + data_p += 4 + 2; + info.width = GetLittleShort() / 8; + + if ( info.format != 1 ) { + Com_Printf( "Unsupported format: %s\n", GetWaveFormatName( info.format ) ); + Com_Printf( "Microsoft PCM format only\n" ); + return info; + } + +// find data chunk + FindChunk( "data" ); + if ( !data_p ) { + Com_Printf( "Missing data chunk\n" ); + return info; + } + + data_p += 4; + info.samples = GetLittleLong() / info.width; + info.dataofs = data_p - wav; + + return info; +} + +/* +================ +ResampleSfx + +resample / decimate to the current source rate +================ +*/ +static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) { + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + int part; + sndBuffer *chunk; + + stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 + + outcount = sfx->soundLength / stepscale; + sfx->soundLength = outcount; + + samplefrac = 0; + fracstep = stepscale * 256; + chunk = sfx->soundData; + + // Gordon: use the littleshort version only if we need to + if ( LittleShort( 256 ) == 256 ) { + for ( i = 0 ; i < outcount ; i++ ) + { + srcsample = samplefrac >> 8; + samplefrac += fracstep; + if ( inwidth == 2 ) { + sample = ( (short *)data )[srcsample]; + } else { + sample = (int)( ( unsigned char )( data[srcsample] ) - 128 ) << 8; + } + part = ( i & ( SND_CHUNK_SIZE - 1 ) ); + if ( part == 0 ) { + sndBuffer *newchunk; + newchunk = SND_malloc(); + if ( chunk == NULL ) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + } + + chunk->sndChunk[part] = sample; + } + } else { + for ( i = 0 ; i < outcount ; i++ ) + { + srcsample = samplefrac >> 8; + samplefrac += fracstep; + if ( inwidth == 2 ) { + sample = LittleShort( ( (short *)data )[srcsample] ); + } else { + sample = (int)( ( unsigned char )( data[srcsample] ) - 128 ) << 8; + } + part = ( i & ( SND_CHUNK_SIZE - 1 ) ); + if ( part == 0 ) { + sndBuffer *newchunk; + newchunk = SND_malloc(); + if ( chunk == NULL ) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + } + + chunk->sndChunk[part] = sample; + } + } +} + +/* +================ +ResampleSfx + +resample / decimate to the current source rate +================ +*/ +static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) { + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + + stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2 + + outcount = samples / stepscale; + + samplefrac = 0; + fracstep = stepscale * 256; + + // Gordon: use the littleshort version only if we need to + if ( LittleShort( 256 ) == 256 ) { + for ( i = 0 ; i < outcount ; i++ ) + { + srcsample = samplefrac >> 8; + samplefrac += fracstep; + if ( inwidth == 2 ) { + sample = ( (short *)data )[srcsample]; + } else { + sample = (int)( ( unsigned char )( data[srcsample] ) - 128 ) << 8; + } + sfx[i] = sample; + } + } else { + for ( i = 0 ; i < outcount ; i++ ) + { + srcsample = samplefrac >> 8; + samplefrac += fracstep; + if ( inwidth == 2 ) { + sample = LittleShort( ( (short *)data )[srcsample] ); + } else { + sample = (int)( ( unsigned char )( data[srcsample] ) - 128 ) << 8; + } + sfx[i] = sample; + } + } + return outcount; +} + + +//============================================================================= + +/* +============== +S_LoadSound + +The filename may be different than sfx->name in the case +of a forced fallback of a player specific sound +============== +*/ +qboolean S_LoadSound( sfx_t *sfx ) { + byte *data; + short *samples; + wavinfo_t info; + int size; + + // player specific sounds are never directly loaded + if ( sfx->soundName[0] == '*' ) { + return qfalse; + } + + // load it in + size = FS_ReadFile( sfx->soundName, (void **)&data ); + if ( !data ) { + return qfalse; + } + + info = GetWavinfo( sfx->soundName, data, size ); + if ( info.channels != 1 ) { + Com_Printf( "%s is a stereo wav file\n", sfx->soundName ); + FS_FreeFile( data ); + return qfalse; + } + + if ( info.width == 1 ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName ); + } + + if ( info.rate != 22050 ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName ); + } + + samples = Hunk_AllocateTempMemory( info.samples * sizeof( short ) * 2 ); + + // DHM - Nerve + sfx->lastTimeUsed = Sys_Milliseconds() + 1; + + // each of these compression schemes works just fine + // but the 16bit quality is much nicer and with a local + // install assured we can rely upon the sound memory + // manager to do the right thing for us and page + // sound in as needed + + + if ( s_nocompressed->value ) { + sfx->soundCompressionMethod = 0; + sfx->soundLength = info.samples; + sfx->soundData = NULL; + ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse ); + } else if ( sfx->soundCompressed == qtrue ) { + sfx->soundCompressionMethod = 1; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, ( data + info.dataofs ) ); + S_AdpcmEncodeSound( sfx, samples ); +#ifdef COMPRESSION + } else if ( info.samples > ( SND_CHUNK_SIZE * 16 ) && info.width > 1 ) { + sfx->soundCompressionMethod = 3; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, ( data + info.dataofs ) ); + encodeMuLaw( sfx, samples ); + } else if ( info.samples > ( SND_CHUNK_SIZE * 6400 ) && info.width > 1 ) { + sfx->soundCompressionMethod = 2; + sfx->soundData = NULL; + sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, ( data + info.dataofs ) ); + encodeWavelet( sfx, samples ); +#endif + } else { + sfx->soundCompressionMethod = 0; + sfx->soundLength = info.samples; + sfx->soundData = NULL; + ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse ); + } + Hunk_FreeTempMemory( samples ); + FS_FreeFile( data ); + + return qtrue; +} + +/* +================ +S_DisplayFreeMemory +================ +*/ +void S_DisplayFreeMemory() { + Com_Printf( "%d bytes (%.2fMB) free sound buffer memory, %d bytes (%.2fMB) total used\n%d bytes (%.2fMB) sound buffer memory have been allocated since the last SND_setup", inUse, inUse / Square( 1024.f ), totalInUse, totalInUse / Square( 1024.f ), totalAllocated, totalAllocated / Square( 1024.f ) ); +} diff --git a/src/client/snd_mix.c b/src/client/snd_mix.c new file mode 100644 index 0000000..d6d1a00 --- /dev/null +++ b/src/client/snd_mix.c @@ -0,0 +1,963 @@ +/* +=========================================================================== + +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: snd_mix.c + * + * desc: portable code to mix sounds for snd_dma.c + * + * $Archive: /Wolfenstein MP/src/client/snd_mix.c $ + * + *****************************************************************************/ + +#include "snd_local.h" + +portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; +static int snd_vol; + +// TTimo not static, required by unix/snd_mixa.s +int *snd_p; +int snd_linear_count; +short *snd_out; + +#ifdef __linux__ + +// snd_mixa.s +void S_WriteLinearBlastStereo16( void ); + +#elif id386 + +__declspec( naked ) void S_WriteLinearBlastStereo16( void ) { + __asm { + + push edi + push ebx + mov ecx,ds : dword ptr[snd_linear_count] + mov ebx,ds : dword ptr[snd_p] + mov edi,ds : dword ptr[snd_out] +LWLBLoopTop: + mov eax,ds : dword ptr[-8 + ebx + ecx * 4] + sar eax,8 + cmp eax,07FFFh + jg LClampHigh + cmp eax,0FFFF8000h + jnl LClampDone + mov eax,0FFFF8000h + jmp LClampDone +LClampHigh: + mov eax,07FFFh +LClampDone: + mov edx,ds : dword ptr[-4 + ebx + ecx * 4] + sar edx,8 + cmp edx,07FFFh + jg LClampHigh2 + cmp edx,0FFFF8000h + jnl LClampDone2 + mov edx,0FFFF8000h + jmp LClampDone2 +LClampHigh2: + mov edx,07FFFh +LClampDone2: + shl edx,16 + and eax,0FFFFh + or edx,eax + mov ds : dword ptr[-4 + edi + ecx * 2],edx + sub ecx,2 + jnz LWLBLoopTop + pop ebx + pop edi + ret + } +} + +#else + +/* +=================== +S_WriteLinearBlastStereo16 +=================== +*/ +void S_WriteLinearBlastStereo16( void ) { + int i; + int val; + + for ( i = 0 ; i < snd_linear_count ; i += 2 ) + { + val = snd_p[i] >> 8; + if ( val > 0x7fff ) { + snd_out[i] = 0x7fff; + } else if ( val < (short)0x8000 ) { + snd_out[i] = (short)0x8000; + } else { + snd_out[i] = val; + } + + val = snd_p[i + 1] >> 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; + } + } +} + +#endif + +/* +=================== +S_TransferStereo16 +=================== +*/ +void S_TransferStereo16( unsigned long *pbuf, int endtime ) { + int lpos; + int ls_paintedtime; + + snd_p = (int *) paintbuffer; + ls_paintedtime = s_paintedtime; + + while ( ls_paintedtime < endtime ) + { + // handle recirculating buffer issues + lpos = ls_paintedtime & ( ( dma.samples >> 1 ) - 1 ); + + snd_out = (short *) pbuf + ( lpos << 1 ); + + snd_linear_count = ( dma.samples >> 1 ) - lpos; + if ( ls_paintedtime + snd_linear_count > endtime ) { + snd_linear_count = endtime - ls_paintedtime; + } + + snd_linear_count <<= 1; + + // write a linear blast of samples + S_WriteLinearBlastStereo16(); + + snd_p += snd_linear_count; + ls_paintedtime += ( snd_linear_count >> 1 ); + } +} + +/* +=================== +S_TransferPaintBuffer +=================== +*/ +void S_TransferPaintBuffer( int endtime ) { + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + unsigned long *pbuf; + + pbuf = (unsigned long *)dma.buffer; + if ( !pbuf ) { + return; + } + + if ( s_testsound->integer ) { + int i; + int count; + + // write a fixed sine wave + count = ( endtime - s_paintedtime ); + for ( i = 0 ; i < count ; i++ ) + paintbuffer[i].left = paintbuffer[i].right = sin( ( s_paintedtime + i ) * 0.1 ) * 20000 * 256; + } + + + if ( dma.samplebits == 16 && dma.channels == 2 ) { + // optimized case + S_TransferStereo16( pbuf, endtime ); + } else + { // general case + p = (int *) paintbuffer; + count = ( endtime - s_paintedtime ) * dma.channels; + out_mask = dma.samples - 1; + out_idx = s_paintedtime * dma.channels & out_mask; + step = 3 - dma.channels; + + if ( dma.samplebits == 16 ) { + short *out = (short *) pbuf; + while ( count-- ) + { + val = *p >> 8; + p += step; + if ( val > 0x7fff ) { + val = 0x7fff; + } else if ( val < -32768 ) { + val = -32768; + } + out[out_idx] = val; + out_idx = ( out_idx + 1 ) & out_mask; + } + } else if ( dma.samplebits == 8 ) { + unsigned char *out = (unsigned char *) pbuf; + while ( count-- ) + { + val = *p >> 8; + p += step; + if ( val > 0x7fff ) { + val = 0x7fff; + } else if ( val < -32768 ) { + val = -32768; + } + out[out_idx] = ( val >> 8 ) + 128; + out_idx = ( out_idx + 1 ) & out_mask; + } + } + } +} + +/* +=============================================================================== + +LIP SYNCING + +=============================================================================== +*/ + +#ifdef TALKANIM + +unsigned char s_entityTalkAmplitude[MAX_CLIENTS]; + +/* +=================== +S_SetVoiceAmplitudeFrom16 +=================== +*/ +void S_SetVoiceAmplitudeFrom16( const sfx_t *sc, int sampleOffset, int count, int entnum ) { + int data, i, sfx_count; + sndBuffer *chunk; + short *samples; + + if ( count <= 0 ) { + return; // must have gone ahead of the end of the sound + } + chunk = sc->soundData; + while ( sampleOffset >= SND_CHUNK_SIZE ) { + chunk = chunk->next; + sampleOffset -= SND_CHUNK_SIZE; + if ( !chunk ) { + chunk = sc->soundData; + } + } + + sfx_count = 0; + samples = chunk->sndChunk; + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= SND_CHUNK_SIZE ) { + chunk = chunk->next; + samples = chunk->sndChunk; + sampleOffset = 0; + } + data = samples[sampleOffset++]; + if ( abs( data ) > 5000 ) { + sfx_count += ( data * 255 ) >> 8; + } + } + //Com_Printf("Voice sfx_count = %d, count = %d\n", sfx_count, count ); + // adjust the sfx_count according to the frametime (scale down for longer frametimes) + sfx_count = abs( sfx_count ); + sfx_count = (int)( (float)sfx_count / ( 2.0 * (float)count ) ); + if ( sfx_count > 255 ) { + sfx_count = 255; + } + if ( sfx_count < 25 ) { + sfx_count = 0; + } + //Com_Printf("sfx_count = %d\n", sfx_count ); + // update the amplitude for this entity + s_entityTalkAmplitude[entnum] = (unsigned char)sfx_count; +} + +/* +=================== +S_SetVoiceAmplitudeFromADPCM +=================== +*/ +void S_SetVoiceAmplitudeFromADPCM( const sfx_t *sc, int sampleOffset, int count, int entnum ) { + int data, i, sfx_count; + sndBuffer *chunk; + short *samples; + + if ( count <= 0 ) { + return; // must have gone ahead of the end of the sound + } + i = 0; + chunk = sc->soundData; + while ( sampleOffset >= ( SND_CHUNK_SIZE * 4 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE * 4 ); + i++; + } + + if ( i != sfxScratchIndex || sfxScratchPointer != sc ) { + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + + sfx_count = 0; + samples = sfxScratchBuffer; + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= SND_CHUNK_SIZE * 4 ) { + chunk = chunk->next; + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sampleOffset = 0; + sfxScratchIndex++; + } + data = samples[sampleOffset++]; + if ( abs( data ) > 5000 ) { + sfx_count += ( data * 255 ) >> 8; + } + } + //Com_Printf("Voice sfx_count = %d, count = %d\n", sfx_count, count ); + // adjust the sfx_count according to the frametime (scale down for longer frametimes) + sfx_count = abs( sfx_count ); + sfx_count = (int)( (float)sfx_count / ( 2.0 * (float)count ) ); + if ( sfx_count > 255 ) { + sfx_count = 255; + } + if ( sfx_count < 25 ) { + sfx_count = 0; + } + //Com_Printf("sfx_count = %d\n", sfx_count ); + // update the amplitude for this entity + s_entityTalkAmplitude[entnum] = (unsigned char)sfx_count; +} + +/* +=================== +S_SetVoiceAmplitudeFromWavelet +=================== +*/ +void S_SetVoiceAmplitudeFromWavelet( const sfx_t *sc, int sampleOffset, int count, int entnum ) { + int data, i, sfx_count; + sndBuffer *chunk; + short *samples; + + if ( count <= 0 ) { + return; // must have gone ahead of the end of the sound + } + i = 0; + chunk = sc->soundData; + while ( sampleOffset >= ( SND_CHUNK_SIZE_FLOAT * 4 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE_FLOAT * 4 ); + i++; + } + if ( i != sfxScratchIndex || sfxScratchPointer != sc ) { + decodeWavelet( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + sfx_count = 0; + samples = sfxScratchBuffer; + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= ( SND_CHUNK_SIZE_FLOAT * 4 ) ) { + chunk = chunk->next; + decodeWavelet( chunk, sfxScratchBuffer ); + sfxScratchIndex++; + sampleOffset = 0; + } + data = samples[sampleOffset++]; + if ( abs( data ) > 5000 ) { + sfx_count += ( data * 255 ) >> 8; + } + } + + //Com_Printf("Voice sfx_count = %d, count = %d\n", sfx_count, count ); + // adjust the sfx_count according to the frametime (scale down for longer frametimes) + sfx_count = abs( sfx_count ); + sfx_count = (int)( (float)sfx_count / ( 2.0 * (float)count ) ); + if ( sfx_count > 255 ) { + sfx_count = 255; + } + if ( sfx_count < 25 ) { + sfx_count = 0; + } + //Com_Printf("sfx_count = %d\n", sfx_count ); + // update the amplitude for this entity + s_entityTalkAmplitude[entnum] = (unsigned char)sfx_count; +} + +/* +=================== +S_SetVoiceAmplitudeFromMuLaw +=================== +*/ +void S_SetVoiceAmplitudeFromMuLaw( const sfx_t *sc, int sampleOffset, int count, int entnum ) { + int data, i, sfx_count; + sndBuffer *chunk; + byte *samples; + + if ( count <= 0 ) { + return; // must have gone ahead of the end of the sound + } + chunk = sc->soundData; + while ( sampleOffset >= ( SND_CHUNK_SIZE * 2 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE * 2 ); + if ( !chunk ) { + chunk = sc->soundData; + } + } + sfx_count = 0; + samples = (byte *)chunk->sndChunk + sampleOffset; + for ( i = 0; i < count; i++ ) { + if ( samples >= (byte *)chunk->sndChunk + ( SND_CHUNK_SIZE * 2 ) ) { + chunk = chunk->next; + samples = (byte *)chunk->sndChunk; + } + data = mulawToShort[*samples]; + if ( abs( data ) > 5000 ) { + sfx_count += ( data * 255 ) >> 8; + } + samples++; + } + //Com_Printf("Voice sfx_count = %d, count = %d\n", sfx_count, count ); + // adjust the sfx_count according to the frametime (scale down for longer frametimes) + sfx_count = abs( sfx_count ); + sfx_count = (int)( (float)sfx_count / ( 2.0 * (float)count ) ); + if ( sfx_count > 255 ) { + sfx_count = 255; + } + if ( sfx_count < 25 ) { + sfx_count = 0; + } + //Com_Printf("sfx_count = %d\n", sfx_count ); + // update the amplitude for this entity + s_entityTalkAmplitude[entnum] = (unsigned char)sfx_count; +} + +/* +=================== +S_GetVoiceAmplitude +=================== +*/ +int S_GetVoiceAmplitude( int entityNum ) { + if ( entityNum >= MAX_CLIENTS ) { + Com_Printf( "Error: S_GetVoiceAmplitude() called for a non-client\n" ); + return 0; + } + + return (int)s_entityTalkAmplitude[entityNum]; +} +#else + +// NERVE - SMF +int S_GetVoiceAmplitude( int entityNum ) { + return 0; +} +// -NERVE - SMF + +#endif + +/* +=============================================================================== + +CHANNEL MIXING + +=============================================================================== +*/ + +/* +=================== +S_PaintChannelFrom16 +=================== +*/ +static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data, aoff, boff; + int leftvol, rightvol; + int i, j; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + float ooff, fdata, fdiv, fleftvol, frightvol; + + samp = &paintbuffer[ bufferOffset ]; + + if ( ch->doppler ) { + sampleOffset = sampleOffset * ch->oldDopplerScale; + } + + chunk = sc->soundData; + while ( sampleOffset >= SND_CHUNK_SIZE ) { + chunk = chunk->next; + sampleOffset -= SND_CHUNK_SIZE; + if ( !chunk ) { + chunk = sc->soundData; + } + } + + if ( !ch->doppler ) { + leftvol = ch->leftvol * snd_vol; + rightvol = ch->rightvol * snd_vol; + + samples = chunk->sndChunk; + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= SND_CHUNK_SIZE ) { + chunk = chunk->next; + if ( chunk == NULL ) { + chunk = sc->soundData; + } + samples = chunk->sndChunk; + sampleOffset -= SND_CHUNK_SIZE; + } + data = samples[sampleOffset++]; + samp[i].left += ( data * leftvol ) >> 8; + samp[i].right += ( data * rightvol ) >> 8; + } + } else { + fleftvol = ch->leftvol * snd_vol; + frightvol = ch->rightvol * snd_vol; + + ooff = sampleOffset; + samples = chunk->sndChunk; + + for ( i = 0 ; i < count ; i++ ) { + aoff = ooff; + ooff = ooff + ch->dopplerScale; + boff = ooff; + fdata = 0; + for ( j = aoff; j < boff; j++ ) { + if ( j >= SND_CHUNK_SIZE ) { + chunk = chunk->next; + if ( !chunk ) { + chunk = sc->soundData; + } + samples = chunk->sndChunk; + ooff -= SND_CHUNK_SIZE; + } + fdata += samples[j & ( SND_CHUNK_SIZE - 1 )]; + } + fdiv = 256 * ( boff - aoff ); + samp[i].left += ( fdata * fleftvol ) / fdiv; + samp[i].right += ( fdata * frightvol ) / fdiv; + } + } +} + +/* +=================== +S_PaintChannelFromWavelet +=================== +*/ +void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + + leftvol = ch->leftvol * snd_vol; + rightvol = ch->rightvol * snd_vol; + + i = 0; + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + while ( sampleOffset >= ( SND_CHUNK_SIZE_FLOAT * 4 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE_FLOAT * 4 ); + i++; + } + + if ( i != sfxScratchIndex || sfxScratchPointer != sc ) { + decodeWavelet( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + + samples = sfxScratchBuffer; + + // FIXME: doppler + + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= ( SND_CHUNK_SIZE_FLOAT * 4 ) ) { + chunk = chunk->next; + decodeWavelet( chunk, sfxScratchBuffer ); + sfxScratchIndex++; + sampleOffset = 0; + } + data = samples[sampleOffset++]; + samp[i].left += ( data * leftvol ) >> 8; + samp[i].right += ( data * rightvol ) >> 8; + } +} + +/* +=================== +S_PaintChannelFromADPCM +=================== +*/ +void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + short *samples; + + leftvol = ch->leftvol * snd_vol; + rightvol = ch->rightvol * snd_vol; + + i = 0; + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + + if ( ch->doppler ) { + sampleOffset = sampleOffset * ch->oldDopplerScale; + } + + while ( sampleOffset >= ( SND_CHUNK_SIZE * 4 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE * 4 ); + i++; + } + + if ( i != sfxScratchIndex || sfxScratchPointer != sc ) { + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sfxScratchIndex = i; + sfxScratchPointer = sc; + } + + samples = sfxScratchBuffer; + for ( i = 0; i < count; i++ ) { + if ( sampleOffset >= SND_CHUNK_SIZE * 4 ) { + chunk = chunk->next; + if ( !chunk ) { + chunk = sc->soundData; + } + S_AdpcmGetSamples( chunk, sfxScratchBuffer ); + sampleOffset = 0; + sfxScratchIndex++; + } + data = samples[sampleOffset++]; + samp[i].left += ( data * leftvol ) >> 8; + samp[i].right += ( data * rightvol ) >> 8; + } +} + +/* +=================== +S_PaintChannelFromMuLaw +=================== +*/ +void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { + int data; + int leftvol, rightvol; + int i; + portable_samplepair_t *samp; + sndBuffer *chunk; + byte *samples; + float ooff; + + leftvol = ch->leftvol * snd_vol; + rightvol = ch->rightvol * snd_vol; + + samp = &paintbuffer[ bufferOffset ]; + chunk = sc->soundData; + while ( sampleOffset >= ( SND_CHUNK_SIZE * 2 ) ) { + chunk = chunk->next; + sampleOffset -= ( SND_CHUNK_SIZE * 2 ); + if ( !chunk ) { + chunk = sc->soundData; + } + } + + if ( !ch->doppler ) { + samples = (byte *)chunk->sndChunk + sampleOffset; + for ( i = 0; i < count; i++ ) { + if ( samples >= (byte *)chunk->sndChunk + ( SND_CHUNK_SIZE * 2 ) ) { + chunk = chunk->next; + samples = (byte *)chunk->sndChunk; + } + data = mulawToShort[*samples]; + samp[i].left += ( data * leftvol ) >> 8; + samp[i].right += ( data * rightvol ) >> 8; + samples++; + } + } else { + ooff = sampleOffset; + samples = (byte *)chunk->sndChunk; + for ( i = 0; i < count; i++ ) { + if ( ooff >= SND_CHUNK_SIZE * 2 ) { + chunk = chunk->next; + if ( !chunk ) { + chunk = sc->soundData; + } + samples = (byte *)chunk->sndChunk; + ooff = 0.0; + } + data = mulawToShort[samples[(int)( ooff )]]; + ooff = ooff + ch->dopplerScale; + samp[i].left += ( data * leftvol ) >> 8; + samp[i].right += ( data * rightvol ) >> 8; + } + } +} + +#define TALK_FUTURE_SEC 0.25 // go this far into the future (seconds) + +//bani - cl_main.c +void CL_WriteWaveFilePacket( int endtime ); + +/* +=================== +S_PaintChannels +=================== +*/ +void S_PaintChannels( int endtime ) { + int i, si; + int end; + channel_t *ch; + sfx_t *sc; + int ltime, count; + int sampleOffset; + streamingSound_t *ss; + qboolean firstPass = qtrue; + + if ( s_mute->value ) { + snd_vol = 0; + } else { + snd_vol = s_volume->value * 256; + } + + if ( snd.volCurrent < 1 ) { // only when fading (at map start/end) + snd_vol = (int)( (float)snd_vol * snd.volCurrent ); + } + + //Com_Printf ("%i to %i\n", s_paintedtime, endtime); + while ( s_paintedtime < endtime ) { + // if paintbuffer is smaller than DMA buffer + // we may need to fill it multiple times + end = endtime; + if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) { + //% Com_DPrintf("endtime exceeds PAINTBUFFER_SIZE %i\n", endtime - s_paintedtime); + end = s_paintedtime + PAINTBUFFER_SIZE; + } + + // clear paint buffer for the current time + Com_Memset( paintbuffer, 0, ( end - s_paintedtime ) * sizeof( portable_samplepair_t ) ); + // mix all streaming sounds into paint buffer + for ( si = 0, ss = streamingSounds; si < MAX_STREAMING_SOUNDS; si++, ss++ ) { + // if this streaming sound is still playing + if ( s_rawend[si] >= s_paintedtime ) { + // copy from the streaming sound source + int s; + int stop; +// float fsir, fsil; // TTimo: unused + + stop = ( end < s_rawend[si] ) ? end : s_rawend[si]; + + // precalculating this saves zillions of cycles +//DAJ fsir = ((float)s_rawVolume[si].left/255.0f); +//DAJ fsil = ((float)s_rawVolume[si].right/255.0f); + for ( i = s_paintedtime ; i < stop ; i++ ) { + s = i & ( MAX_RAW_SAMPLES - 1 ); +//DAJ paintbuffer[i-s_paintedtime].left += (int)((float)s_rawsamples[si][s].left * fsir); +//DAJ paintbuffer[i-s_paintedtime].right += (int)((float)s_rawsamples[si][s].right * fsil); + //DAJ even faster + paintbuffer[i - s_paintedtime].left += ( s_rawsamples[si][s].left * s_rawVolume[si].left ) >> 8; + paintbuffer[i - s_paintedtime].right += ( s_rawsamples[si][s].right * s_rawVolume[si].right ) >> 8; + } +#ifdef TALKANIM + if ( firstPass && ss->channel == CHAN_VOICE && ss->entnum < MAX_CLIENTS ) { + int talkcnt, talktime; + int sfx_count, vstop; + int data; + + // we need to go into the future, since the interpolated behaviour of the facial + // animation creates lag in the time it takes to display the current facial frame + talktime = s_paintedtime + (int)( TALK_FUTURE_SEC * (float)s_khz->integer * 1000 ); + vstop = ( talktime + 100 < s_rawend[si] ) ? talktime + 100 : s_rawend[si]; + talkcnt = 1; + sfx_count = 0; + + for ( i = talktime ; i < vstop ; i++ ) { + s = i & ( MAX_RAW_SAMPLES - 1 ); + data = abs( ( s_rawsamples[si][s].left ) / 8000 ); + if ( data > sfx_count ) { + sfx_count = data; + } + } + + if ( sfx_count > 255 ) { + sfx_count = 255; + } + if ( sfx_count < 25 ) { + sfx_count = 0; + } + + //Com_Printf("sfx_count = %d\n", sfx_count ); + + // update the amplitude for this entity + // rain - the announcer is ent -1, so make sure we're >= 0 + if ( ss->entnum >= 0 ) { + s_entityTalkAmplitude[ss->entnum] = (unsigned char)sfx_count; + } + } +#endif + } + } + + // paint in the channels. + ch = s_channels; + for ( i = 0; i < MAX_CHANNELS; i++, ch++ ) { + if ( ch->startSample == START_SAMPLE_IMMEDIATE || !ch->thesfx || ( ch->leftvol < 0.25 && ch->rightvol < 0.25 ) ) { + continue; + } + + ltime = s_paintedtime; + sc = ch->thesfx; + + // (SA) hmm, why was this commented out? + if ( !sc->inMemory ) { + S_memoryLoad( sc ); + } + + sampleOffset = ltime - ch->startSample; + count = end - ltime; + if ( sampleOffset + count > sc->soundLength ) { + count = sc->soundLength - sampleOffset; + } + + if ( count > 0 ) { +#ifdef TALKANIM + // Ridah, talking animations + // TODO: check that this entity has talking animations enabled! + if ( firstPass && ch->entchannel == CHAN_VOICE && ch->entnum < MAX_CLIENTS ) { + int talkofs, talkcnt, talktime; + // we need to go into the future, since the interpolated behaviour of the facial + // animation creates lag in the time it takes to display the current facial frame + talktime = ltime + (int)( TALK_FUTURE_SEC * (float)s_khz->integer * 1000 ); + talkofs = talktime - ch->startSample; + talkcnt = 100; + if ( talkofs + talkcnt < sc->soundLength ) { + if ( sc->soundCompressionMethod == 1 ) { + S_SetVoiceAmplitudeFromADPCM( sc, talkofs, talkcnt, ch->entnum ); + } else if ( sc->soundCompressionMethod == 2 ) { + S_SetVoiceAmplitudeFromWavelet( sc, talkofs, talkcnt, ch->entnum ); + } else if ( sc->soundCompressionMethod == 3 ) { + S_SetVoiceAmplitudeFromMuLaw( sc, talkofs, talkcnt, ch->entnum ); + } else { + S_SetVoiceAmplitudeFrom16( sc, talkofs, talkcnt, ch->entnum ); + } + } + } +#endif + if ( sc->soundCompressionMethod == 1 ) { + S_PaintChannelFromADPCM( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else if ( sc->soundCompressionMethod == 2 ) { + S_PaintChannelFromWavelet( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else if ( sc->soundCompressionMethod == 3 ) { + S_PaintChannelFromMuLaw( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else { + S_PaintChannelFrom16( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } + } + } + + // paint in the looped channels. + ch = loop_channels; + for ( i = 0; i < numLoopChannels ; i++, ch++ ) { + if ( !ch->thesfx || ( !ch->leftvol && !ch->rightvol ) ) { + continue; + } + + ltime = s_paintedtime; + sc = ch->thesfx; + + if ( sc->soundData == NULL || sc->soundLength == 0 ) { + continue; + } + // we might have to make two passes if it + // is a looping sound effect and the end of + // the sample is hit + do { + //% sampleOffset = (ltime % sc->soundLength); + //% sampleOffset = (ltime - ch->startSample) % sc->soundLength; // ydnar + sampleOffset = ( ltime /*- ch->startSample*/ ) % sc->soundLength; // ydnar + + count = end - ltime; + if ( sampleOffset + count > sc->soundLength ) { + count = sc->soundLength - sampleOffset; + } + + if ( count > 0 ) { +#ifdef TALKANIM + // Ridah, talking animations + // TODO: check that this entity has talking animations enabled! + if ( firstPass && ch->entchannel == CHAN_VOICE && ch->entnum < MAX_CLIENTS ) { + int talkofs, talkcnt, talktime; + // we need to go into the future, since the interpolated behaviour of the facial + // animation creates lag in the time it takes to display the current facial frame + talktime = ltime + (int)( TALK_FUTURE_SEC * (float)s_khz->integer * 1000 ); + talkofs = talktime % sc->soundLength; + talkcnt = 100; + if ( talkofs + talkcnt < sc->soundLength ) { + if ( sc->soundCompressionMethod == 1 ) { + S_SetVoiceAmplitudeFromADPCM( sc, talkofs, talkcnt, ch->entnum ); + } else if ( sc->soundCompressionMethod == 2 ) { + S_SetVoiceAmplitudeFromWavelet( sc, talkofs, talkcnt, ch->entnum ); + } else if ( sc->soundCompressionMethod == 3 ) { + S_SetVoiceAmplitudeFromMuLaw( sc, talkofs, talkcnt, ch->entnum ); + } else { + S_SetVoiceAmplitudeFrom16( sc, talkofs, talkcnt, ch->entnum ); + } + } + } +#endif + if ( sc->soundCompressionMethod == 1 ) { + S_PaintChannelFromADPCM( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else if ( sc->soundCompressionMethod == 2 ) { + S_PaintChannelFromWavelet( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else if ( sc->soundCompressionMethod == 3 ) { + S_PaintChannelFromMuLaw( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } else { + S_PaintChannelFrom16( ch, sc, count, sampleOffset, ltime - s_paintedtime ); + } + ltime += count; + } + } while ( ltime < end ); + } + + // transfer out according to DMA format + S_TransferPaintBuffer( end ); + //bani + CL_WriteWaveFilePacket( end ); + s_paintedtime = end; + firstPass = qfalse; + } +} diff --git a/src/client/snd_public.h b/src/client/snd_public.h new file mode 100644 index 0000000..8991664 --- /dev/null +++ b/src/client/snd_public.h @@ -0,0 +1,115 @@ +/* +=========================================================================== + +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. + +=========================================================================== +*/ + + +#ifdef DOOMSOUND ///// (SA) DOOMSOUND +#ifdef __cplusplus +extern "C" { +#endif +#endif ///// (SA) DOOMSOUND + +#ifndef __snd_public_h__ +#define __snd_public_h__ + +void S_Init( void ); +void S_Shutdown( void ); +void S_UpdateThread( void ); +void S_Reload( void ); + +// if origin is NULL, the sound will be dynamically sourced from the entity +void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx, int volume ); +void S_StartSoundEx( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx, int flags, int volume ); +void S_StartLocalSound( sfxHandle_t sfx, int channelNum, int volume ); + +void S_StartBackgroundTrack( const char *intro, const char *loop, int fadeupTime ); +void S_StopBackgroundTrack( void ); +void S_QueueBackgroundTrack( const char *loop ); //----(SA) added +void S_FadeStreamingSound( float targetvol, int time, int ssNum ); //----(SA) added +void S_FadeAllSounds( float targetvol, int time, qboolean stopsounds ); //----(SA) added + +float S_StartStreamingSound( const char *intro, const char *loop, int entnum, int channel, int attenuation ); +void S_StopStreamingSound( int index ); +void S_StopEntStreamingSound( int entNum ); //----(SA) added + +void S_AddLoopSounds( void ); + +// cinematics and voice-over-network will send raw samples +// 1.0 volume will be direct output of source samples +void S_RawSamples( int samples, int rate, int width, int s_channels, + const byte *data, float lvol, float rvol, int streamingIndex ); + +// stop all sounds and the background track +void S_StopAllSounds( void ); + +// all continuous looping sounds must be added before calling S_Update +void S_ClearLoopingSounds( void ); +void S_ClearSounds( qboolean clearStreaming, qboolean clearMusic ); //----(SA) modified +void S_AddLoopingSound( const vec3_t origin, const vec3_t velocity, const int range, sfxHandle_t sfxHandle, int volume, int soundTime ); +void S_AddRealLoopingSound( const vec3_t origin, const vec3_t velocity, const int range, sfxHandle_t sfxHandle, int volume, int soundTime ); + +#ifdef DOOMSOUND ///// (SA) DOOMSOUND +void S_ClearSoundBuffer( void ); +#endif ///// (SA) DOOMSOUND +// recompute the reletive volumes for all running sounds +// reletive to the given entityNum / orientation +void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ); + +// let the sound system know where an entity currently is +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ); + +void S_Update_Debug( void ); +void S_Update( void ); + +void S_DisableSounds( void ); + +void S_BeginRegistration( void ); + +// RegisterSound will allways return a valid sample, even if it +// has to create a placeholder. This prevents continuous filesystem +// checks for missing files +#ifdef DOOMSOUND ///// (SA) DOOMSOUND +sfxHandle_t S_RegisterSound( const char *sample ); +#else +sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed ); +#endif ///// (SA) DOOMSOUND + +void S_DisplayFreeMemory( void ); + +// +int S_GetVoiceAmplitude( int entityNum ); + +int S_GetSoundLength( sfxHandle_t sfxHandle ); +int S_GetCurrentSoundTime( void ); + +#endif // __snd_public_h__ + +#ifdef DOOMSOUND ///// (SA) DOOMSOUND +#ifdef __cplusplus +} +#endif +#endif ///// (SA) DOOMSOUND diff --git a/src/client/snd_wavelet.c b/src/client/snd_wavelet.c new file mode 100644 index 0000000..ec40e39 --- /dev/null +++ b/src/client/snd_wavelet.c @@ -0,0 +1,276 @@ +/* +=========================================================================== + +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: snd_wavelet.c + * + * desc: + * + * + *****************************************************************************/ + +#include "snd_local.h" + +#define C0 0.4829629131445341 +#define C1 0.8365163037378079 +#define C2 0.2241438680420134 +#define C3 -0.1294095225512604 + +void daub4( float b[], unsigned long n, int isign ) { + float wksp[4097]; + float *a = b - 1; // numerical recipies so a[1] = b[0] + + unsigned long nh,nh1,i,j; + + if ( n < 4 ) { + return; + } + + nh1 = ( nh = n >> 1 ) + 1; + if ( isign >= 0 ) { + for ( i = 1, j = 1; j <= n - 3; j += 2, i++ ) { + wksp[i] = C0 * a[j] + C1 * a[j + 1] + C2 * a[j + 2] + C3 * a[j + 3]; + wksp[i + nh] = C3 * a[j] - C2 * a[j + 1] + C1 * a[j + 2] - C0 * a[j + 3]; + } + wksp[i ] = C0 * a[n - 1] + C1 * a[n] + C2 * a[1] + C3 * a[2]; + wksp[i + nh] = C3 * a[n - 1] - C2 * a[n] + C1 * a[1] - C0 * a[2]; + } else { + wksp[1] = C2 * a[nh] + C1 * a[n] + C0 * a[1] + C3 * a[nh1]; + wksp[2] = C3 * a[nh] - C0 * a[n] + C1 * a[1] - C2 * a[nh1]; + for ( i = 1, j = 3; i < nh; i++ ) { + wksp[j++] = C2 * a[i] + C1 * a[i + nh] + C0 * a[i + 1] + C3 * a[i + nh1]; + wksp[j++] = C3 * a[i] - C0 * a[i + nh] + C1 * a[i + 1] - C2 * a[i + nh1]; + } + } + for ( i = 1; i <= n; i++ ) { + a[i] = wksp[i]; + } +} + +void wt1( float a[], unsigned long n, int isign ) { + unsigned long nn; + int inverseStartLength = n / 4; + if ( n < inverseStartLength ) { + return; + } + if ( isign >= 0 ) { + for ( nn = n; nn >= inverseStartLength; nn >>= 1 ) daub4( a,nn,isign ); + } else { + for ( nn = inverseStartLength; nn <= n; nn <<= 1 ) daub4( a,nn,isign ); + } +} + +/* The number of bits required by each value */ +static unsigned char numBits[] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +}; + +byte MuLawEncode( short s ) { + unsigned long adjusted; + byte sign, exponent, mantissa; + + sign = ( s < 0 ) ? 0 : 0x80; + + if ( s < 0 ) { + s = -s; + } + adjusted = (long)s << ( 16 - sizeof( short ) * 8 ); + adjusted += 128L + 4L; + if ( adjusted > 32767 ) { + adjusted = 32767; + } + exponent = numBits[( adjusted >> 7 ) & 0xff] - 1; + mantissa = ( adjusted >> ( exponent + 3 ) ) & 0xf; + return ~( sign | ( exponent << 4 ) | mantissa ); +} + +short MuLawDecode( byte uLaw ) { + signed long adjusted; + byte exponent, mantissa; + + uLaw = ~uLaw; + exponent = ( uLaw >> 4 ) & 0x7; + mantissa = ( uLaw & 0xf ) + 16; + adjusted = ( mantissa << ( exponent + 3 ) ) - 128 - 4; + + return ( uLaw & 0x80 ) ? adjusted : -adjusted; +} + +short mulawToShort[256]; +static qboolean madeTable = qfalse; + +static int NXStreamCount; + +void NXPutc( NXStream *stream, char out ) { + stream[NXStreamCount++] = out; +} + + +void encodeWavelet( sfx_t *sfx, short *packets ) { + float wksp[4097], temp; + int i, samples, size; + sndBuffer *newchunk, *chunk; + byte *out; + + if ( !madeTable ) { + for ( i = 0; i < 256; i++ ) { + mulawToShort[i] = (float)MuLawDecode( (byte)i ); + } + madeTable = qtrue; + } + chunk = NULL; + + samples = sfx->soundLength; + while ( samples > 0 ) { + size = samples; + if ( size > ( SND_CHUNK_SIZE * 2 ) ) { + size = ( SND_CHUNK_SIZE * 2 ); + } + + if ( size < 4 ) { + size = 4; + } + + newchunk = SND_malloc(); + if ( sfx->soundData == NULL ) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + for ( i = 0; i < size; i++ ) { + wksp[i] = *packets; + packets++; + } + wt1( wksp, size, 1 ); + out = (byte *)chunk->sndChunk; + + for ( i = 0; i < size; i++ ) { + temp = wksp[i]; + if ( temp > 32767 ) { + temp = 32767; + } else if ( temp < -32768 ) { + temp = -32768; + } + out[i] = MuLawEncode( (short)temp ); + } + + chunk->size = size; + samples -= size; + } +} + +void decodeWavelet( sndBuffer *chunk, short *to ) { + float wksp[4097]; + int i; + byte *out; + + int size = chunk->size; + + out = (byte *)chunk->sndChunk; + for ( i = 0; i < size; i++ ) { + wksp[i] = mulawToShort[out[i]]; + } + + wt1( wksp, size, -1 ); + + if ( !to ) { + return; + } + + for ( i = 0; i < size; i++ ) { + to[i] = wksp[i]; + } +} + + +void encodeMuLaw( sfx_t *sfx, short *packets ) { + int i, samples, size, grade, poop; + sndBuffer *newchunk, *chunk; + byte *out; + + if ( !madeTable ) { + for ( i = 0; i < 256; i++ ) { + mulawToShort[i] = (float)MuLawDecode( (byte)i ); + } + madeTable = qtrue; + } + + chunk = NULL; + samples = sfx->soundLength; + grade = 0; + + while ( samples > 0 ) { + size = samples; + if ( size > ( SND_CHUNK_SIZE * 2 ) ) { + size = ( SND_CHUNK_SIZE * 2 ); + } + + newchunk = SND_malloc(); + if ( sfx->soundData == NULL ) { + sfx->soundData = newchunk; + } else { + chunk->next = newchunk; + } + chunk = newchunk; + out = (byte *)chunk->sndChunk; + for ( i = 0; i < size; i++ ) { + poop = packets[0] + grade; + if ( poop > 32767 ) { + poop = 32767; + } else if ( poop < -32768 ) { + poop = -32768; + } + out[i] = MuLawEncode( (short)poop ); + grade = poop - mulawToShort[out[i]]; + packets++; + } + chunk->size = size; + samples -= size; + } +} + +void decodeMuLaw( sndBuffer *chunk, short *to ) { + int i; + byte *out; + + int size = chunk->size; + + out = (byte *)chunk->sndChunk; + for ( i = 0; i < size; i++ ) { + to[i] = mulawToShort[out[i]]; + } +} diff --git a/src/curl-7.12.2/CHANGES b/src/curl-7.12.2/CHANGES new file mode 100644 index 0000000..e21559b --- /dev/null +++ b/src/curl-7.12.2/CHANGES @@ -0,0 +1,1634 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + Changelog + +Version 7.12.2 (18 October 2004) + +Daniel (16 October 2004) +- Alexander Krasnostavsky made the CURLOPT_FTP_CREATE_MISSING_DIRS option work + fine even for third party transfers. + +- runekl at opoint.com found out (and provided a fix) that libcurl leaked + memory for cookies with the "max-age" field set. + +Gisle (16 October 2004) +- Issue 50 in TODO-RELEASE; Added Traian Nicolescu's patches for threaded + resolver on Windows. Plugged some potential handle and memory leaks. + +Daniel (14 October 2004) +- Eric Vergnaud pointed out that libcurl didn't treat ?-letters in the user + name and password fields properly in URLs, like + ftp://us?er:pass?word@site.com/. Added test 191 to verify the fix. + +Daniel (11 October 2004) +- libcurl now uses SO_NOSIGPIPE for systems that support it (Mac OS X 10.2 or + later is one) to inhibit the SIGPIPE signal when writing to a socket while + the peer dies. The same effect is provide by the MSG_NOSIGNAL parameter to + send() on other systems. Alan Pinstein verified the fix. + +Daniel (10 October 2004) +- Systems with 64bit longs no longeruse strtoll() or our strtoll- replacement + to parse 64 bit numbers. strtol() works fine. Added a configure check to + detect if [constant]LL works and if so, use that in the strtoll replacement + code to work around compiler warnings reported by Andy Cedilnik. + +Gisle (6 October 2004) +- For USE_LIBIDN builds: Added Top-Level-Domain (TLD) check of host-name + used in fix_hostname(). Checks if characters in 'host->name' (indirectly + via 'ace_hostname') are legal according to the TLD tables in libidn. + +Daniel (6 October 2004) +- Chih-Chung Chang reported that if you use CURLOPT_RESUME_FROM and enabled + CURLOPT_FOLLOWLOCATION, libcurl reported error if a redirect happened even + if the new URL would provide the resumed file. Test case 188 added to verify + the fix (together with existing test 99). + +- Dan Fandrich fixed a configure flaw for systems that need both nsl and socket + libs to use gethostbyname(). + +- Removed tabs and trailing whitespace from lots of source files. + +Daniel (5 October 2004) +- Made configure --with-libidn=PATH try the given PATH before the default + paths to make it possible to override. + +- If idna_strerror() is present in libidn, we can use that instead of our + internal replacement. This function was added by Simon in libidn 0.5.6 and + is detected by configure. + +- It seems basename() on IRIX is in the libgen library and since we don't use + that, configure finds libgen.h but not basename and then we get a compiler + error because our basename() replacement doesn't match the proto in + libgen.h. Starting now, we don't include the file if basename wasn't found + as well. + +Daniel (4 October 2004) +- Chris found a race condition resulting in CURLE_COULDNT_RESOLVE_HOST and + potential crash, in the windows threaded name resolver code. + +Daniel (3 October 2004) +- Replaced the use of isspace() in cookie.c with our own version instead since + we have most data as 'char *' and that makes us pass in negative values if + there is 8bit data in the string. Changing to unsigned causes too much + warnings or too many required typecasts to the normal string functions. + Harshal Pradhan identified this problem. + +Daniel (2 October 2004) +- Bertrand Demiddelaer found a case where libcurl could read already freed + data when CURLOPT_VERBOSE is used and a (very) persistent connection. It + happened when the dns cache entry for the connection was pruned while the + connection was still alive and then again re-used. We worked together on + this fix. + +- Gisle Vanem provided code that displays an error message when the (libidn + based) IDN conversion fails. This is really due to a missing suitable + function in the libidn API that I hope we can remove once libidn gets a + function like this. + +Daniel (1 October 2004) +- Aleksandar Milivojevic reported a problem in the Redhat bugzilla (see + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=134133) and not to + anyone involved in the curl project! This happens when you try to curl a + file from a proftpd site using SSL. It seems proftpd sends a somewhat + unorthodox response code (232 instead of 230). I relaxed the response code + check to deal with this and similar cases. + +- Based on Fedor Karpelevitch's formpost path basename patch, file parts in + formposts no longer include the path part. If you _really_ want them, you + must provide your preferred full file name with CURLFORM_FILENAME. + + Added detection for libgen.h and basename() to configure. My custom + basename() replacement function for systems without it, might be a bit too + naive... + + Updated 6 test cases to make them work with the stripped paths. + +Daniel (30 September 2004) +- Larry Campbell added CURLINFO_OS_ERRNO to curl_easy_getinfo() that allows an + app to retrieve the errno variable after a (connect) failure. It will make + sense to provide this for more failures in a more generic way, but let's + start like this. + +- Günter Knauf and Casey O'Donnell worked out an extra #if condition for the + curl/multi.h header to work better in winsock-using apps. + +- Jean-Philippe Barrette-LaPierre made buildconf run better on Mac OS X by + properly using glibtoolize instead of plain libtoolize. (This is made if + glibtool was found and used instead of plain libtool.) + +Daniel (29 September 2004) +- Bertrand Demiddelaer fixed curl_easy_reset() so that it doesn't mistakingly + enable the progress meter. + +Daniel (28 September 2004) +- "Mekonikum" found out that if you built curl without SSL support, although + your current SSL installation supports Engine, the compile fails. + +Daniel (27 September 2004) +- When --with-ssl=PATH is used to the configure script, it no longer uses + pkg-config to figure out extra details. That is now only done if no PATH is + included or if SSL is checked for by default without the --with-ssl option. + +Daniel (25 September 2004) +- Peter Sylvester pointed out that CURLOPT_SSLENGINE couldn't even be set to + NULL when no engine was supported. It can now. + +Daniel (22 September 2004) +- Dan Fandrich fixed three test cases to no longer use "localhost" but instead + use "127.0.0.1" to avoid requiring that localhost resolves nicely. + +- Jean-Claude Chauve fixed an LDAP crash when more than one record was + retrieved. + +Daniel (19 September 2004) +- Andreas Rieke pointed out that when attempting to connect to a host without + a service on the specified port, curl_easy_perform() didn't properly provide + an error message in the CURLOPT_ERRORBUFFER buffer. + +Daniel (16 September 2004) +- Daniel at touchtunes uses the FTP+SSL server "BSDFTPD-SSL from + http://bsdftpd-ssl.sc.ru/" which accordingly doesn't properly work with curl + when "AUTH SSL" is issued (although the server responds fine and everything) + but requires that curl issues "AUTH TLS" instead. See + http://curl.haxx.se/feedback/display.cgi?id=10951944937603&support=yes + + Introducing CURLOPT_FTPSSLAUTH that allows the application to select which + of the AUTH strings to attempt first. + +- Anonymous filed bug report #1029478 which identified a bug when you 1) used + a URL without properly seperating the host name and the parameters with a + slash. 2) the URL had parameters to the right of a ? that contains a slash + 3) curl was told to follow Location:s 4) the request got a response that + contained a Location: to redirect to "/dir". curl then appended the new path + on the wrong position of the original URL. + + Test case 187 was added to verify that this was fixed properly. + +Daniel (11 September 2004) +- Added parsedate.c that contains a rewrite of the date parser currently + provided by getdate.y. The new one is MUCH smaller and will allow us to run + away from the yacc/bison jungle. It is also slightly lacking in features + compared to the old one, but it supports parsing of all date formats HTTP + involves (and a fair bunch of others). + +Daniel (10 September 2004) +- As found out by Jonas Forsman, curl didn't allow -F to set Content-Type on + text-parts. Starting now, we can do -F "name=daniel;type=text/extra". Added + test case 186 to verify. + +- Bug report #1025986. When following a Location: with a custom Host: header + replacement, curl only replaced the Host: header on the initial request + and didn't replace it on the following ones. This resulted in requests with + two Host: headers. + + Now, curl checks if the location is on the same host as the initial request + and then continues to replace the Host: header. And when it moves to another + host, it doesn't replace the Host: header but it also doesn't make the + second Host: header get used in the request. + + This change is verified by the two new test cases 184 and 185. + +Daniel (8 September 2004) +- Modified the test suite to be able to use and run with customized port + numbers. This was always intended but never before possible. Now a simple + change in the runtests.pl script can make all tests use different ports. + The default ports in use from now on are 8990 to 8993. + +Daniel (2 September 2004) +- Minor modification of an SSL-related error message. + +Daniel (31 August 2004) +- David Tarendash found out that curl_multi_add_handle() returned + CURLM_CALL_MULTI_PERFORM instead of CURLM_OK. + +Daniel (30 August 2004) +- Make "Proxy-Connection: close" close the current proxy connection, as Roman + Koifman found out. + +Daniel (24 August 2004) +- Fixed a getdate problem by post-replacing the getdate.c file after the + bison/yacc process to add the fix Harshal Pradhan suggested. The problem + caused a crash on Windows when parsing some dates. + +Daniel (23 August 2004) +- Roman Koifman pointed out that libcurl send Expect: 100-continue on POSTs + even when told to use HTTP 1.0, which is not correct. Test case 180 and + 181 verify this. + +- Added test case 182 to verify that zero byte transfers call the callback + properly. + +Daniel (20 August 2004) +- Alexander Krasnostavsky made the write callback get called even when a zero + byte file is downloaded. + +Daniel (18 August 2004) +- Ling Thio pointed out that when libcurl is built ipv6-enabled, it still did + reverse DNS lookups when fed with a numerical IP-address (like + http://127.0.0.1/), although it doesn't when built ipv6-disabled. libcurl + should never do reverse lookups. + +Daniel (17 August 2004) +- Kjetil Jacobsen noticed that when transferring a file:// URL pointing to an + empty file, libcurl would return with the file still open. + +- Alexander Krasnostavsky pointed out that the configure script needs to define + _THREAD_SAFE for AIX systems to make libcurl built really thread-safe. + + Also added a check for the xlc compiler on AIX, and if that is detect we use + the -qthreaded compiler option + +Daniel (16 August 2004) +- libcurl now allows a custom "Accept-Encoding:" header override the + internally set one that gets set with CURLOPT_ENCODING. Pointed out by Alex. + +- Roland Krikava found and fixed a cookie problem when using a proxy (the + path matching was wrong). I added test case 179 to verify that we now do + right. + +Daniel (15 August 2004) +- Casey O'Donnell fixed some MSVC makefile targets to link properly. + +Daniel (11 August 2004) +- configure now defines _XOPEN_SOURCE to 500 on systems that need it to build + warning-free (the only known one so far is non-gcc builds on 64bit SGI + IRIX). (Reverted this change later as it caused compiler errors.) + +- the FTP code now includes the server response in the error message when the + server gives back a 530 after the password is provided, as it isn't + necessary because of a bad user name or password. + +Version 7.12.1 (10 August 2004) + +Daniel (10 August 2004) +- In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input is + already UTF-8 encoded. This made the certificate verification fail if the + remote server used a certificate with the name UTF-8 encoded. + + Work-around brought by Alexis S. L. Carvalho. + +Daniel (9 August 2004) +- I fixed the configure script for krb4 to use -lcom_err as well, as I started + to get link problems with it unless I did that on my Solaris 2.7 box. I + don't understand why I started to get problems with this now! + +Daniel (5 August 2004) +- Enrico Scholz fixed the HTTP-Negotiate service name to be uppercase as + reported in bug report #1004105 + +Daniel (4 August 2004) +- Gisle Vanem provided a fix for the multi interface and connecting to a host + using multiple IP (bad) addresses. + +- Dylan Salisbury made libcurl no longer accept cookies set to a TLD only (it + previously allowed that on the seven three-letter domains). + +Daniel (31 July 2004) +- Joel Chen reported that the digest code assumed quotes around the contents a + bit too much. + +Daniel (28 July 2004) +- Bertrand Demiddelaer fixed the host name to get setup properly even when a + connection is re-used, when a proxy is in use. Previously the wrong Host: + header could get sent when re-using a proxy connection to a different target + host. + +- Fixed Brian Akins' reported problems with duplicate Host: headers on re-used + connections. If you attempted to replace the Host: header in the second + request, you got two such headers! + +- src/Makefile.am now includes the Makefile.inc file to get info about files + +Daniel (26 July 2004) +- Made "curl [URL] -o name#2" work as expected. If there's no globbing for the + #-number, it will simply be used as #2 in the file name. + +- Bertrand Demiddelaer fixed testing with valgrind 2.1.x and added two missing + newlines in the cookie informationals. + +Daniel (24 July 2004) +- I fixed the autobuilds with ares, since they now need to have buildconf run + in the ares dir before the configure script is run. + +- Added Casey O'Donnell's curl_easy_reset() function. It has a proto in + curl/curl.h but we have no man page yet. + +Daniel (20 July 2004) +- Added buildconf and buildconf.bat to the release archives, since they are + handy for rebuilding curl when using a daily snapshot (and not a pure CVS + checkout). + +Daniel (16 July 2004) +- As suggested by Toby Peterson, libcurl now ignores Content-Length data if the + given size is a negative number. Test case 178 verifies this. + +Daniel (14 July 2004) +- Günter Knauf has made the Netware builds do without the config-netware.h + files, so they are now removed from the dist packages. + +- Günter Knauf made curl and libcurl build with Borland again. + +- Andres Garcia fixed the common test 505 failures on windows. + +Daniel (6 July 2004) +- Andrés García found out why the windows tests failed on file:// "uploads". + +Daniel (2 July 2004) +- Andrés García reported a curl_share_cleanup() crash that occurs when no + lock/unlock callbacks have been set and the share is cleaned up. + +Daniel (1 July 2004) +- When using curl --trace or --trace-ascii, no trace messages that were sent + by curl_easy_cleanup() were included in the trace file. This made the + message "Closing connection #0" never appear in trace dumps. + +Daniel (30 June 2004) +- Niels van Tongeren found that setting CURLOPT_NOBODY to TRUE doesn't disable + a previously set POST request, making a very odd request get sent (unless + you disabled the POST) a HEAD request with a POST request-body. I've now + made CURLOPT_NOBODY enforce a proper HEAD. Added test case 514 for this. + +Daniel (29 June 2004) +- Günter Knauf made the testcurl.pl script capable of using a custom setup + file to easier run multiple autobuilds on the same source tree. + +- Gisle fixed the djgpp build and fixed a memory problem in some of the + reorged name resolved code. + +- Fixed code to allow connects done using the multi interface to attempt the + next IP when connecting to a host that resolves to multiple IPs and a + connect attempt fails. + +Daniel (27 June 2004) +- Based on Rob Stanzel's bug report #979480, I wrote a configure check that + checks if poll() can be used to wait on NULL as otherwise select() should be + used to do it. The select() usage was also fixed according to his report. + + Mac OS X 10.3 says "poll() functionality for Mac OS X is implemented via an + emulation layer on top of select(), not in the kernel directly. It is + recommended that programs running under OS X 10.3 prefer select() over + poll(). Configure scripts should look for the _POLL_EMUL_H_ define (instead + of _POLL_H_ or _SYS_POLL_H_) and avoid implementations where poll is not + implemented in the kernel." + + Yes, we can probably use select() on most platforms but today I prefered to + leave the code unaltered. + +Daniel (24 June 2004) +- The standard curl_version() string now only includes version info about + involved libraries and not about particular features. Thus it will no longer + include info about ipv6 nor GSS. That info is of course still available in + the feature bitmask curl_version_info() offers. + +- Replaced all occurances of sprintf() with snprintf(). This is mostly because + it is "A Good Thing" rather than actually fixing any known problem. This + will help preventing future possible mistakes to cause buffer overflows. + +- Major reorganization in the host resolve code (again). This time, I've + modified the code to now always use a linked list of Curl_addrinfo structs + to return resolved info in, no matter what resolver method or support that + is available on the platform. It makes it a lot easier to write code that + uses or depends on resolved data. + + Internally, this means amongst other things that we can stop doing the weird + "increase buffer size until it works" trick when resolving hosts on + ipv4-only with gethostbyname_r(), we support socks even on libcurls built + with ipv6 enabled (but only to socks servers that resolve to an ipv4 + address) and we no longer deep-copy or relocate hostent structs (we create + Curl_addrinfo chains instead). + + The new "hostent to Curl_addrinfo" converter function is named Curl_he2ai() + and is slightly naive and simple, yet I believe it is functional enough to + work for libcurl. + +Daniel (22 June 2004) +- David Cohen pointed out that RFC2109 says clients should allow cookies to + contain least 4096 bytes while libcurl only allowed 2047. I raised the limit + to 4999 now and made the used buffer get malloc()ed instead of simply + allocated on stack as before. Extended test case 46 to include a cookie with + very huge content to verify the fix. + +- Günter Knauf fixed getdate.y to remove a few warnings. I removed the + ifdef'ed test we never ever use anyway. + +- Gisle Vanem fixed the certificate wildcard checks to support a '*'-letter + anywhere in the wildcard string, support multiple '*'-letters in the + wildcard and to allow the '*'-letter to match a string that includes a dot. + +Daniel (21 June 2004) +- testcurl.sh is now removed completely, tests/testcurl.pl is the script to + use when autobuilding curl! + +- Kjetil Jacobsen brought my attention to the fact that you cannot properly + abort an upload with the readfunction callback, since returning 0 or -1 only + stops the upload and libcurl will continue waiting for downloaded data and + the server often waits for the rest of the upload data to arrive. + + Thus, I've now added the ability for read callbacks to return + CURL_READFUNC_ABORT to abort an upload from a read callback. This will stop + the transfer immediately with a CURLE_ABORTED_BY_CALLBACK return code. + + Test case 513 was added to verify that it works. I had to improve the test + HTTP server too to dump the request to a file even when the client + disconnects prematurely. + +Daniel (19 June 2004) +- Luca Alteas provided a test case with a failing curl operation: when we POST + to a site with --digest (or similar) set, and the server responded with a 302 + Location: to the "authprobe" request, it was not treated correctly. We still + will behave badly if FOLLOWLOCATION is enabled for this case, but I'm not + in the mood to dive into this right now and will leave it as-is for now. + Verified my fix with test case 177. + +Daniel (18 June 2004) +- Gisle Vanem's patch that provides more details from the SSL layers (if you + use an OpenSSL version that supports it). It also introduces two new types + of data that can be sent to the debug callback: CURLINFO_SSL_DATA_IN and + CURLINFO_SSL_DATA_OUT. + +- With David Byron's test server I could repeat his problem and make sure that + POSTing over HTTPS:// with NTLM works fine now. There was a general problem + with multi-pass authentication with non-GET operations with CONNECT. + +Daniel (16 June 2004) +- Modified to keep the upload byte counter in an curl_off_t, not an int as + before. 32bits is not enough. This is most likely the bug Jean-Louis Lemaire + reported that makes 2GB FTP uploads to report error ("unaligned file sizes") + when completed. + +Daniel (15 June 2004) +- Luca Alteas reported a problem that I fixed: if you did a POST with + CURLAUTH_DIGEST set but the server didn't require any authentication, + libcurl would repeatedly send HEAD lots of times until it gives up. This was + actually the case for all multi-pass authentications. Added test case 174, + 175 and 176 to verify this. + +Daniel (14 June 2004) +- Multipart formposts uploading files no longer inserts the files themselves + into the huge prebuilt chunk. This enables libcurl to formpost files that is + larger than the amount of system memory. When the file given is passed on + stdin, libcurl still uses the old method of reading the full fill before the + upload takes place. This approach was selected in order to not alter the + behavior for existing applications, as when using stdin libcurl can't know + the size of the upload and chunked transfer-encoding can only be used on + HTTP 1.1 servers. + +Daniel (13 June 2004) +- Gisle found out that we did wildcard cert name checks wrong, so that parts + of the check wrongly was case sensitive. + +Daniel (11 June 2004) +- Tim Sneddon brought a minor VMS fix to make curl build properly on his VMS + machine. He also had some interesting libcurl patches... they might be able + to do in a slightly nicer way. Discussions are in progress. + +Daniel (10 June 2004) +- Gisle Vanem brought code cleanupsm better verbose output and better connect + timeout handling when attempting to connect to a host that resolves to + multiple IP addresses. + +- Steven Bazyl and Seshubabu Pasam pointed out a bug on win32 when freeing the + path after a file:// transfer. + +Daniel (9 June 2004) +- Alexander Krasnostavsky made 'configure --disable-http' work to build libcurl + without HTTP support. I added a new return code for curl_formadd() in case + libcurl is built with HTTP disable: CURL_FORMADD_DISABLED. + +- Alexander Krasnostavsky pointed out a missing file in the generated + curllib.dsp file, and now people building with this should get a libcurl.lib + file generated as it used to do before we generated this file. + +Daniel (8 June 2004) +- Marty Kuhrt fixed a minor build problem for VMS. + +Daniel (7 June 2004) +- Reverted the configure check from the 4th since it obviously didn't work. + Remade it in a different manner that hopefully works better. + +Daniel (4 June 2004) +- Günter Knauf brought patches to make curl build fine on NetWare again. + +- Made the configure checks for strerror_r() not exit the configure script + when built for cross-compiling. + +Daniel (3 June 2004) +- Chris Gaukroger pointed out that 'make test' attempts to run the tests even + if curl is built cross-compiled. I've now made it output a short message + instead, saying it isn't possible to do. + +- Alexander Krasnostavsky brought FTP 3rd party transfer support to libcurl. + You can now use libcurl to transfer files between two remote hosts using + FTP. There are a bunch of new options to control this with: + CURLOPT_SOURCE_HOST + CURLOPT_SOURCE_USERPWD + CURLOPT_SOURCE_PATH + CURLOPT_SOURCE_PORT + CURLOPT_PASV_HOST + CURLOPT_SOURCE_PREQUOTE + CURLOPT_SOURCE_POSTQUOTE + + (They still remain to be documented properly in the curl_easy_setopt man + page.) + + When using this, the ordinary CURLOPT_URL specifies the target URL, and you + specify the source data with these additional options. ftp3rdparty.c is a + new example source code showing how to use this. + +- Vincent Bronner fixed the HTTP Digest code to use the proxy user name and + password when doing proxy authentication, it previously always used the host + user name and password! + +Daniel (2 June 2004) +- CURLOPT_UPLOAD and CURLOPT_PUT now do the exact same thing internally, which + fixes some old confusions on when which of these should be used and what the + differences are. + +- Applied Gisle's fixes to make curl build fine with lcc-win32 + +Version 7.12.0 (2 June 2004) + +Daniel (1 June 2004) +- I clarified the --create-dirs option somewhat in the curl man page. + +- Renaud Duhaut corrected the curl_unescape man page. + +- David Byron modified one of Massimiliano Ziccardi's recent MSVC makefile + changes to now again use the mm lib by default. + +Daniel (26 May 2004) +- Mohun Biswas added release-zlib and debug-zlib targets to the MSVC libcurl + Makefile + +- David Byron reported a problem with proxy authentication when doing CONNECT, + like when accessing HTTPS sites wiht a proxy. This probably broke when I + rewrote the auth stuff recently. + +- I added fileupload.c in the examples directory, showing how an upload to a + file:// URL is made. + +Daniel (25 May 2004) +- Massimiliano Ziccardi updated the MSVC makefiles. + +Daniel (24 May 2004) +- libcurl now supports "uploading" to file:// URLs. Test 204 and 205 were + added to verify. + +- Simon Josefsson added a idn_free() function in libidn 0.4.5 as a reaction to + Gisle's previous mail. We now use this function, and thus we require libidn + 0.4.5 or later. No earlier version will do. + +- Robert D. Young reported that CURLOPT_COOKIEFILE and CURLOPT_COOKIE could + not be used both in one request. Fixed it and added test case 172 to verify. + +Daniel (21 May 2004) +- While talking to host a.b.c, libcurl did wrongly not accept cookies that + were set to the domain .a.b.c (that is with a dot prefix). This is now fixed + and test case 171 verifies it. + +Daniel (20 May 2004) +- Jesse Noller reported that the upload speed info reported by libcurl was + wrong. The same was true for the download speed. Fixed now. + +Daniel (19 May 2004) +- David Byron added test case 170 - this used to crash the previous version of + curl. + +Daniel (17 May 2004) +- Peter Sylvester's patch that addresses two flaws in the peer certificate + name verification: + o when multiple common names are used (as in the curl tests), the last name + needs to be selected. + o allow comparing with encoded values, at least with BMP and ISO latin1 + encoded T61strings. + +- All 191 test cases run through the torture test OK! 'make test-torture' is + now available in the root makefile (on configure-based environments). + +Daniel (14 May 2004) +- With a slightly modified ftpserver.pl I've now run almost all tests through + with runtests.pl -t. This is goodness! + +- Since I have been unable to contact the CVS admins for several months, I've + decided that the current CVS hosting was not good enough. I've now moved the + CVS repo once again, see README for updated cvs checkout instructions. + +Daniel (13 May 2004) +- runtests.pl -t now runs fine all the way to test 100. I believe test case + 100 fails because of an FTP server problem. + +Daniel (12 May 2004) +- General cleanups all over to make libcurl survive and do well when a memory + function returns NULL. runtests.pl -t now works fine for the first 26 test + cases. + +Daniel (11 May 2004) +- Seshubabu Pasam provided a patch that introduces curl_global_init_mem() - + like normal curl_global_init() but allows the app to replace all memory + functions with its own set. I modified it slightly. + +- Based on Luca Alteas' comments, I modified the curllib.dsp generation code. + +Daniel (10 May 2004) +- Gisle mailed Simon Josefsson (of libidn fame) about the benefits of a + separate free()-function by that lib to make sure the memory is freed by the + same memory subsystem that allocated it. He responded positively and this + will likely cause us to require a newer version of libidn as soon as Simon + releases one with such a libidn_free() function. + +- James Bursa made runtests.pl's -t option work for any given test case, and I + edited to allow -g too. Not even test case 1 worked... + +- Luca Altea made the nc= field not use quotes in outgoing HTTP Digest headers. + +- Andrés García fixed a problem in the test script that made it fail to + recognize our own running HTTP server. + +Daniel (7 May 2004) +- James Bursa fixed the memanalyze.pl script to conder malloc(0) areas OK to + free() and he made two failed-resolve error messages use the new display- + name instead of the internally-used name. + +- Gisle Vanem tried curl with + www.etdomenenavnkanmaksimaltinneholdesekstitrebokstaversliksomdette.com + which caused problems, and I fixed the single zero byte buffer overwrite + that occurred (due to a stupid protocol buffer size and parser). + +- Made the lib/curllib.dsp file get generated automaticly when a distribution + package is made, with the msvcproj.* files as templates and all + win32-sources added. I think this can be made to work better than the always + lagging-behind previous approach. I'm not sure this builds a working project + file right now though! + +Daniel (6 May 2004) +- Michael Benedict brought a fix that fills in the errorbuffer properly when + ares fails to resolve a name for a case not previously dealt with like this. + +Daniel (5 May 2004) +- Joe Halpin fixed the annoying typecast warning in lib/ldap.c + +- Gisle Vanem fixes: + o memdebug to not access NULL on several places + o libcurl.def; curl_formparse is gone. + o progress.c; fixed the percent values being trunced to 0. + o if2ip.*; constified the 'interface' argument. + +- Tor Arntsen reported that many of his autobuilds froze and I found and fixed + a problem introduced with the HTTP auth overhaul that could lead to a + never-ending internal request-loop due to un-initialized variables! + +- Removed several compiler warnings on various compilers/platforms. + +Daniel (4 May 2004) +- curl_formparse() has been removed from the library. It has been marked and + mentioned as deprecated for several years. + +Daniel (3 May 2004) +- Rewritten HTTP authentication code. The previous code could not properly + deal with the added test cases 167, 168 and 169. I've now rewritten the code + to better separate host and proxy authentication and not re-use the same + variables as much as before as it proved non working in the more involved + cases. All the current tests run OK now, and so do the new ones. The curl + tool got a new option named --proxy-digest to enable HTTP Digest + authentication with the proxy. I also made the library support it. + +- Gisle Vanem made the LDAP code work with wldap32.dll as supplied with + Win-98/ME/2000/XP, so no extra .dlls are required when curl/libcurl is used + on these Windows versions. + +Daniel (30 April 2004) +- runtests.pl now scans the valgrind log for valgrind-detected memory leaks + after each test case if valgrind was found and used. + +- I modified the app-code in curl to include the new lib/curlx.h and only + access those functions using the curlx_-prefix in preparation for the future + removal of several curl_-functions from the public libcurl API. + +- Introduced lib/curlx.h as a single header to provide the curlx_-functions + to apps. + +- Added notices in the man pages for curl_getenv, curl_mprintf, curl_strequal + and curl_strnequal that they are subject for removal in a future release. + STOP USING THESE FUNCTIONS. + +- Mihai Ionescu noticed he couldn't do formposts with whitespace in the file + names and yes, I broke that on April 23. Sigh. I fixed it now and added + test case 166 to verify it. + +- Luca Altea pointed out a mistake left from the Digest patch of yesterday. + +Daniel (29 April 2004) +- Made IDN domains work when sending requsts over HTTP proxy as well. Added + test case 165 to verify the functionality. + +- Fixed a bug in the new internal host name setup when re-using connections. + +- James Bursa found out that curl_easy_duphandle() with ares-built libcurl + created a bad handle that would crash in the first name resolve attempt. This + is now fixed and test case 512 was added to verify it. + +- Luca Altea provided a major HTTP Digest code fix and cleanup. We now follow + the Digest RFC a lot better. + +- Gisle Vanem made the SSL code use ERR_error_string_n() where applicable. + +Daniel (27 April 2004) +- I remodeled Gisle's IDN code slightly and now we convert both the host name + and proxy name to the ACE encoded version to use internally for resolves and + cookies etc. They are now using one 'struct hostname' each that keep both + the original name and the possibly encoded name. IDN resolves work for me + now using ipv6, ipv4 and ares resolving. Even cookies on IDN sites seem to + do right. I got some failures at first when CHARSET wasn't set at all which + confused libidn completely and it decided by encoding of choice was + 'ANSI_X3.4-1968'... + +- made 'configure --without-libidn' work + +Daniel (25 April 2004) +- Fixed the src/hugehelp.c file to include "setup.h" instead of "config.h" to + make the problems with USE_MANUAL on windows go away. + +- configure --without-ssl could still wrongly include some OpenSSL info in the + Makefiles if pkg-config had info about OpenSSL. Bug #941762 reported by + Martin. + +- Since we can now build and use quite a large set of 3rd party libraries, I + decided I would make configure produce a summary at the end showing what + libraries it uses and if not, what option to use to make it use that. I also + added some other random info that is nice in a "configure summary" output. + +- Applied TommyTam's patch that now make curl work with telnet and stdin + properly on Windows. + +- The changes for today below were made by me and Gisle Vanem. + + The file previously known as hostip.c has now undergone a huge cleanup and + split: + + hostip.c explained + ================== + + The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c + source file are these: + + CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use + that. The host may not be able to resolve IPv6, but we don't really have to + take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 + defined. + + CURLRES_ARES - is defined if libcurl is built to use c-ares for asynchronous + name resolves. It cannot have ENABLE_IPV6 defined at the same time, as + c-ares has no ipv6 support. This can be Windows or *nix. + + CURLRES_THREADED - is defined if libcurl is built to run under (native) + Windows, and then the name resolve will be done in a new thread, and the + supported asynch API will be the same as for ares-builds. + + If any of the two previous are defined, CURLRES_ASYNCH is defined too. If + libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is + defined. + + The host*.c sources files are split up like this: + + hostip.c - method-independent resolver functions and utility functions + hostasyn.c - functions for asynchronous name resolves + hostsyn.c - functions for synchronous name resolves + hostares.c - functions for ares-using name resolves + hostthre.c - functions for threaded name resolves + hostip4.c - ipv4-specific functions + hostip6.c - ipv6-specific functions + + The hostip.h is the single united header file for all this. It defines the + CURLRES_* defines based on the config*.h and setup.h defines. + +- Added function header comments to many functions in an attempt to better + explain the purpose of them all. + +- configure --with-libidn is now supported. It makes the configure script + check for libidn libs and include files in the prefix path given. If you + say --with-libidn=/usr/local, it will check for the lib in /usr/local/lib + and the includes in /usr/local/include etc. + +- curl_version_info() now returns a struct aged CURLVERSION_THIRD including + libidn version info. The string curl_version() returns also includes libidn + version info, if available. + +Version 7.11.2 (26 April 2004) + +Daniel (25 April 2004) +- Erwin Authried pointed out that configure --disable-manual didn't do right + if you already had a src/hugehelp.c source file present (which most people + do I guess). It now uses the USE_MANUAL define properly. + +Daniel (23 April 2004) +- Gisle Vanem found and fixed a memory leak when doing (failing) Windows + threaded name resolves. + +- I also added test case 163 just to make sure -F "var=2GB files. curl -V now + outputs 'Largefile' in the Features: field if this is the case. Most systems + are likely to support this. + +- We offer a CURL_FORMAT_OFF_T define in the public header, which can be used + to printf() curl_off_t variables. We also modified the libcurl sources to + use this define instead of the previous %Od approach (although I've left the + O-flag functional in the code). This should also prevent compilers to warn + on the home-grown option. + +- Fixed the resume-check code to test for a working resume at the end of the + headers and not at the first body-byte. + +- CURLOPT_DNS_USE_GLOBAL_CACHE is now considered obsolete. Stop using it. If + you need a global DNS cache for whatever reason, use the share interface and + you'll get a global cache that works the way it should work. You can even + have any number of global caches, all at your command. This is now also + mentioned in the docs. + +- Made the *printf code support the z-flag to enable size_t printf() in a + manner similar to how glibc allows it. To make printfing of this work on + platforms with 64bit size_t and 32bit ints. If there even are any! ;-) + +- Christopher R. Palmer discovered that if you CURLOPT_FRESH_CONNECT and + CURLAUTH_NTLM (or CURLAUTH_ANY and libcurl then picked NTLM), libcurl would + loop without succeeding to authenticate due to the new connection that was + made for all round-trips in the authentication. Now, the FRESH_CONNECT is + remade to only matter for the first connection made with curl_easy_perform() + and all the rest that might follow due to FOLLOWLOCATION or HTTP + authentication are now ignoring that option. + +- Adjusted the QUIT code slightly since it could core-dump. + +- Corrected the test suite's FTP server to provide a correct size to the + 'verifiedserver' request. + +Daniel (27 February 2004) +- Joe Halpin made the FTP code send QUIT on the control connection before + disconnecting the TCP connection. This is what good-behaving ftp clients + should do. + +Daniel (26 February 2004) +- David Byron updated several files to make curl build fine on MSVC 6. He + also added the 'buildconf.bat' that works like the 'buildconf + configure' + combo does on unixes. + +- Gisle Vanem made the memdebug stuff support calloc() as well. + +- Tor Arntsen pointed out that testcurl.sh needed to remove the generated + files in order to have them re-generated in each build. + +- Andy Serpa found out that the share interface did not enjoy life when not + having the lock and unlock callbacks set, even though documented to be + OK. It still is OK, and now the code won't segfault anymore! + +Daniel (25 February 2004) +- Based on a patch by Greg Hewgill I modified how long long is used in the + mprintf code, as we can use a 64bit type with MSVC that is a long long + equivalent. This corrects some weird large file behaviors on windows. + +- Tor Arntsen helped me work out --enable-debug to work better with different + versions of the gcc and icc compilers. + +- Added CURLOPT_SHARE to the curl_easy_setopt.3 man page. + +Daniel (22 February 2004) +- Applied the final pieces of Gisle Vanem's patch that brings a working name + resolve timeout to the windows versions of curl! + +Daniel (21 February 2004) +- David Byron's fix to allow the speed-limit logic work even if you set + limit-rate. It does work on the expense of the rate limiter. + +Daniel (20 February 2004) +- configure --enable-debug with gcc now also tries to detect the icc compiler + (which somehow gets treated as if it is a gcc) to stop using all the gcc + options with it, and we also provide -isystem options for each extra -I + option the configure script has figured out (for OpenSSL, kerberos, zlib, + Heimdal etc). This of course to prevent warnings on headers we don't have + control of. + +Daniel (19 February 2004) +- Doug Porter made libcurl use the HOME environment variable before the + getpwuid results when looking for .netrc files. + +- If 'configure --enable-debug' is used with gcc, it now checks which gcc + version it is and uses as picky compiler options as possible for the + particular version. + +- Code that can be used in both the lib and in the curl app is now made to use + the curlx_ prefix. The first function to be available like this is the + curlx_strtoll() function. This is made to allow the app to use existing code, + but without polluting the libcurl API. Further explanations posted here: + + http://curl.haxx.se/mail/lib-2004-02/0215.html + +Daniel (18 February 2004) +- Fixed buildconf to not use "which" as AIX and Tru64 have what have been + referred to as "horribly broken 'which' programs". + +- Made sure dns cache timeout set to -1 really means caching forever. + +Daniel (17 February 2004) +- Made it possibly to build c-ares with the libcurl memdebug system to better + track memory. + +Daniel (16 February 2004) +- When using ares, we now initialize the ares 'channel' in curl_easy_init() + and re-use that same handle during the entire curl handle's life-time. It + improves performance. + +- Fixed a problem when displaying verbose for ipv6-enabled libcurls and + re-used connections. Problem reported and fix verified by Grigory Entin. + +- Jeff Lawson fixed the version-check in the SOCKS5 code. + +Daniel (15 February 2004) +- Fixed a case where a host cache entry was not flagged in-use properly when a + cached entry was used. + +- Andrés García's patch that checks for winmm in the configure script was + applied. + +Daniel (13 February 2004) +- Ben Greear's SO_BINDTODEVICE patch for the binding of the local end to a + specific network interface. + +- Greg Hewgill found out that the variable holding 'contentlength' wasn't big + enough to hold a large file! + +- Tor Arntsen fixed a 64bit-related problem in date-related code in the ftp + department, and there was another potential problem in the name resolve code + too. + +Daniel (11 February 2004) +- Removed a few variables that were only set but never used, as some compilers + warn about that and we do not like compiler warnings! + +- Removed the need for symlinks in the tests/data directory if curl is built + outside of the source directory and the 'make test' is used. This was done + by providing a "source dir path" to the scripts/servers. + +- Now, if the configure script can't find an nroff tool or an option to nroff + to use to convert man pages with, it will completely switch off the built-in + manual. + +- 'configure --disable-manual' completely disables the built-in manual from + the curl command tool. + +- Andrés García fixed the configure script and a minor source edit, and now + he has managed to get msys/mingw to run configure and then build! + +Daniel (9 February 2004) +- The default HTTP Accept: header was modified to the much simpler + "Accept: */*". + +- P R Schaffner updated the curl-ssl spec file for RPMs. + +- Dominick Meglio brought lots of documentation for the share interface's man + pages that were previously missing. + +- Tor Arntsen provided a patch that makes libcurl work-around a bug in the + AIX5 implementation of getaddrinfo(). This makes the FTP PORT stuff work on + ipv6-enabled AIX builds. + +- Ken Rastatter provided portability fixes for the curlgtk.c example, and now + it runs on windows with GTK as well! + +Daniel (6 February 2004) +- Andrés García made the configure script find gethostbyname() fine when run + with mingw on windows. + +- Modified the ldap code to use proper function pointers all over (instead of + mixed data and function pointers) to work-around the picky MIPSPro compiler + warnings. + +- A custom Host: header is only considered if the request is not made by + following a location. After discussions with Tim Baker. + +Daniel (5 February 2004) +- The libz part of the configure script now only set the two libz-related + define HAVE_ZLIB_H and HAVE_LIBZ if both the lib and the header is found. + If one is missing, none of the defines is set. + +- Andrés García fixed the Mingw makefiles. + +- Len Krause reported that curl 7.9.X could do uploading from stdin without + doing chunked encoding, which current curl cannot do even if you disable + the transfer-encoding chunked header. Now it can again, and test case 98 + verifies this functionality. + +- Tor Arntsen fixed a weird getaddrinfo() usage in the FTP code, preventing + the ipv6-code for PORT work on AIX 5.2. We now also provide (better) error + messages when bailing out in the that function. + +- Tor Arntsen now provides AIX and IRIX (using gcc, xlc and the MIPSPro + compilers) automated build logs (http://curl.haxx.se/auto/) and we've fixed + numerous minor quirks to make less warnings appear. + +Daniel (4 February 2004) +- Based on a patch by Gilad, we now use the custom timeouts when waiting for a + server to connect when using FTP PORT. Previously we always waited 10 + seconds, no more no less. We now also changed the default (if no timeout is + set) to wait 60 seconds for the connect before we fail. + +Daniel (3 February 2004) +- Modified to link with c-ares instead of ares. + +Daniel (2 February 2004) +- Added a configure test to check for which option the (g)nroff tool wants + to extract plain text from the man pages. Tor Arntsen told us the AIX + version of GNU gnroff doesn't support -man! + +- Added an undef of accept in memdebug.h to make curl build with --enable-debug + on AIX 5.2 which seems to have accept defined. Reported by Tor Arntsen. + +- curl_version() now includes c-ares version info, and curl_version_info() now + returns a struct with version SECOND that also includes that info. + +- We are now officially using c-ares for asynch name resolves. c-ares is the + new library, based on the existing ares but with an extended and slightly + modified API. + +- Dirk improved the ares timeout code, and now we also include the ares error + string when we fail to resolve a name. + +- Another tweak to make test case 91 run fine. Now we have another bit on a + connection that is set true if the connection is marked for 'retry'. That + makes the connection get closed and re-opened and the HTTP-done code must + not complain on the fact that no data was received. + +- Based on Dirk Manske's patch, I modified the name resolving with ares to + feature a timeout for really slow lookups. It now defaults to 300 seconds, + but is now adjusted to the CONNECTTIMEOUT/TIMOUE timeouts if one of them + is set. + +- Fixed the inclusion of ca-bundle.h to really use the one in the build dir + before the one in the source dir. Domenico Andreoli found out and reported. + +- Added test case 97, a simple POST with a custom Content-Type header + replacing the original application/x-www-form-urlencoded one. + +Daniel (30 January 2004) +- Added code that attempts to fix the test 91 failure. As has been figured out + by Patrick Smith, the error happens because we re-use a connection that the + server is just about to close and we even manage to send away the request + without seeing an error. On the first read attempt we get a ECONNRESET. + Starting now, we attempt to detect this and if so, we retry the request on a + fresh connection. + +- I added test case 510 which is a custom program that does a POST using a + read callback, with chunked transfer-encoding. + +- Adjusted one of the MPE/iX changes as it made test case 504 fail all over. + +- Added --socks as a recognized option. It works just like --proxy but sets a + SOCKS5 proxy to use. SOCKS5 support has been available in libcurl for a + while, just not provided by the curl tool. This does not currently work for + IPv6-enabled libcurls. + +Daniel (29 January 2004) +- Stadler Stephan pointed out that src/hugehelp.c included config.h without + checking the define if its present... + +- Ken Hirsch provided patches to make curl build fine on the MPE/iX operating + system. + +- Dan Fandrich compiled curl with lots of aggressively pedantic compiler + options and thus found a few minor errors and did some general cleanups to + avoid them. + +- Dirk Manske fixed a flaw in ares that prevented it to use non-blocking + sockets properly. + +Daniel (28 January 2004) +- Richard Bramante fixed chunked transfer-encoded "uploads" to send a final + CRLF combo properly. + +Daniel (27 January 2004) +- Made the response-headers during a CONNECT request to a proxy get passed on + as regular headers, so they appear with -i/-I options and similar. + +- Based on a patch by Gisle Vanem, I've made the progress meter display + properly switch to a GB-display when more than 9999MB have been transfered. + +Daniel (23 January 2004) +- Gisle Vanem pointed out a curlrc parser problem/crash when an option with a + required didn't have one and was on the last line of a file. + +- More Windows fixes for large files. We now build and link with + ../lib/strtoofft.c in the app code since Curl_strtoll() is not a provided + libcurl function... Perhaps we should consider a 'common' dir or similar + where we put source code used in both the lib and the client. Or perhaps + we'll just make this function available in the library... + +- Vincent Bronner found out the socks5 code crashed when no username was + set. + +- Vincent Bronner spotted a problem with proxy username/password when re-using + a persistent connection. + +- Fixed the progress meter display for files larger than 2^31 bytes. Gisle + Vanem reported. + +Daniel (22 January 2004) +- Gisle Vanem made strtoll() get used when curl is built with the mingw + compiler. + +- Gisle Vanem fixed the compressed help text code to display properly. + +- Removed the '#define HttpPost' from the public header file, as curl_httppost + is the proper name and it has been for quite some time now. Fixes another + name space pollution. + +- Added 'curl_off_t' typedef in the public header file, to be used to provide + large file sizes to the *_LARGE options. Adjusted the code all over to use + this variable type instead of 'off_t'. This is an attempt to make the large + file support work on more platforms. The configure script now checks the + size of the curl_off_t instead of the plain off_t. + +Version 7.11.0 (22 January 2004) + +Daniel (21 January 2004) +- Removed the defines in the public header file with TIMECOND_ prefixes. They + have been obsolete since April 22nd 2002, and if this causes anyone any + problems now it is very easy to just add CURL_ to the names. This corrects + this name space pollution. + +Daniel (19 January 2004) +- David Byron cleaned up how --trace with no option was treated, and also + arguments in a config file without a required parameter! + +Daniel (16 January 2004) +- Gisle Vanem fixed a few issues where compilers warned about variables + possibly being used unassigned. + +- Minor Interix build problem fixed. + +Daniel (15 January 2004) +- Peter Sylvester pointed out some necessary escaping needed in the + acinclude.m4 file when automake 1.8 or later is used. + +Daniel (14 January 2004) +- Vincent Bronner fixed the Curl_resolv() return code. This extends the fix + Steve Green provided on december 3... + +Daniel (13 January 2004) +- Luke Call made the win32 version of the password prompting function support + backspace. + +- Dan Fandrich fixed the hugehelp source file to contain both a compressed and + an uncompressed version in the distribution, so that more people easier can + build curl with the compressed version. + +- Diego Casorran brought another AmigaOS build patch for native Amiga builds. + +- Matt Veenstra updated the Mac OS X framework files. + +- Brian R Duffy brought a section to the INSTALL file on how to build a + SSL-enabled curl using the free Borland C++ compiler. He also updated the + Borland lib/Makefile.b32. + +- I fixed the test case 509 which I broke yesterday. Now the libtest are + compiled with an include path that points to the library's source dir, so + that the libtests can include files from the source tree. This was made to + make it possible to use the USE_SSLEAY define in the library test files. + +Daniel (12 January 2004) +- Peter Sylvester brought code that now allows a callback to modified the URL + even when the multi interface is used, and then libcurl will simulate a + "follow location" to that new URL. Test 509 was added to test this feature. + +- Extended the time we retry servers in the test script, and I also made it + retry the https and ftps servers before they are considered bad. I believe + the previous approach could turn problematic on really slow hosts. + +Version 7.11.0-pre1 (12 January 2004) + +Daniel (11 January 2004) +- Dominick Meglio pointed out FTPS should use default port 990 according to + IANA. + +Daniel (8 January 2004) +- Fixed the SPNEGO configure check to not use -R or other non-portable options + in the LDFLAGS. Reported by Pierre in bug report #872930. + +Daniel (5 January 2004) +- Dan Fandrich provided a fix on our zlib usage. + +- David J Meyer's patch that introduce large file support to libcurl was + applied. New curl_easy_setopt options that accept 'off_t' arguments are: + + INFILESIZE_LARGE + RESUME_FROM_LARGE + MAXFILESIZE_LARGE + +Daniel (4 January 2004) +- Based on Dominick Meglio's comments, I made our private version of + gettimeofday() declared static. This would otherwise collide with the same + function in other libs (like ares for example). + +- Added Dominick Meglio's description on how to build libcurl with ares + on win32. diff --git a/src/curl-7.12.2/COPYING b/src/curl-7.12.2/COPYING new file mode 100644 index 0000000..d7ad261 --- /dev/null +++ b/src/curl-7.12.2/COPYING @@ -0,0 +1,21 @@ +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2004, Daniel Stenberg, . + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. diff --git a/src/curl-7.12.2/Makefile.am b/src/curl-7.12.2/Makefile.am new file mode 100644 index 0000000..6a5104a --- /dev/null +++ b/src/curl-7.12.2/Makefile.am @@ -0,0 +1,116 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# $Id: Makefile.am,v 1.52 2004/07/22 22:22:49 bagder Exp $ +########################################################################### + +AUTOMAKE_OPTIONS = foreign + +EXTRA_DIST = CHANGES COPYING maketgz reconf Makefile.dist curl-config.in \ + curl-style.el sample.emacs RELEASE-NOTES buildconf buildconf.bat + +bin_SCRIPTS = curl-config + +SUBDIRS = lib src +DIST_SUBDIRS = $(SUBDIRS) tests include packages docs + +dist-hook: + rm -rf $(top_builddir)/tests/log + find $(distdir) -name "*.dist" -exec rm {} \; + (distit=`find $(srcdir) -name "*.dist"`; \ + for file in $$distit; do \ + strip=`echo $$file | sed -e s/^$(srcdir)// -e s/\.dist//`; \ + cp $$file $(distdir)$$strip; \ + done) + +html: + cd docs; make html + +pdf: + cd docs; make pdf + +check: test + +test: + @(cd tests; $(MAKE) all quiet-test) + +test-full: + @(cd tests; $(MAKE) all full-test) + +test-torture: + @(cd tests; $(MAKE) all torture-test) + +# +# Build source and binary rpms. For rpm-3.0 and above, the ~/.rpmmacros +# must contain the following line: +# %_topdir /home/loic/local/rpm +# and that /home/loic/local/rpm contains the directory SOURCES, BUILD etc. +# +# cd /home/loic/local/rpm ; mkdir -p SOURCES BUILD RPMS/i386 SPECS SRPMS +# +# If additional configure flags are needed to build the package, add the +# following in ~/.rpmmacros +# %configure CFLAGS="%{optflags}" ./configure %{_target_platform} --prefix=%{_prefix} ${AM_CONFIGFLAGS} +# and run make rpm in the following way: +# AM_CONFIGFLAGS='--with-uri=/home/users/loic/local/RedHat-6.2' make rpm +# + +rpms: + $(MAKE) RPMDIST=curl rpm + $(MAKE) RPMDIST=curl-ssl rpm + +rpm: + RPM_TOPDIR=`rpm --showrc | $(PERL) -n -e 'print if(s/.*_topdir\s+(.*)/$$1/)'` ; \ + cp $(srcdir)/packages/Linux/RPM/$(RPMDIST).spec $$RPM_TOPDIR/SPECS ; \ + cp $(PACKAGE)-$(VERSION).tar.gz $$RPM_TOPDIR/SOURCES ; \ + rpm -ba --clean --rmsource $$RPM_TOPDIR/SPECS/$(RPMDIST).spec ; \ + mv $$RPM_TOPDIR/RPMS/i386/$(RPMDIST)-*.rpm . ; \ + mv $$RPM_TOPDIR/SRPMS/$(RPMDIST)-*.src.rpm . + +# +# Build a Solaris pkkgadd format file +# run 'make pkgadd' once you've done './configure' and 'make' to make a Solaris pkgadd format +# file (which ends up back in this directory). +# The pkgadd file is in 'pkgtrans' format, so to install on Solaris, do +# pkgadd -d ./HAXXcurl-* +# + +# gak - libtool requires an absoulte directory, hence the pwd below... +pkgadd: + umask 022 ; \ + make install DESTDIR=`/bin/pwd`/packages/Solaris/root ; \ + cat COPYING > $(srcdir)/packages/Solaris/copyright ; \ + cd $(srcdir)/packages/Solaris && $(MAKE) package + +# +# Build a cygwin binary tarball installation file +# resulting .tar.bz2 file will end up at packages/Win32/cygwin +cygwinbin: + $(MAKE) -C packages/Win32/cygwin cygwinbin + +# We extend the standard install with a custom hook: +install-data-hook: + cd include && $(MAKE) install + cd docs && $(MAKE) install + +# We extend the standard uninstall with a custom hook: +uninstall-hook: + cd include && $(MAKE) uninstall + cd docs && $(MAKE) uninstall diff --git a/src/curl-7.12.2/Makefile.in b/src/curl-7.12.2/Makefile.in new file mode 100644 index 0000000..3df1206 --- /dev/null +++ b/src/curl-7.12.2/Makefile.in @@ -0,0 +1,760 @@ +# Makefile.in generated by automake 1.8.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# $Id: Makefile.am,v 1.52 2004/07/22 22:22:49 bagder Exp $ +########################################################################### + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_triplet = @host@ +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/curl-config.in \ + $(top_srcdir)/configure COPYING config.guess config.sub \ + depcomp install-sh ltmain.sh missing mkinstalldirs +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/lib/config.h \ + $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = curl-config +am__installdirs = "$(DESTDIR)$(bindir)" +binSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(bin_SCRIPTS) +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CABUNDLE_FALSE = @CABUNDLE_FALSE@ +CABUNDLE_TRUE = @CABUNDLE_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CROSSCOMPILING_FALSE = @CROSSCOMPILING_FALSE@ +CROSSCOMPILING_TRUE = @CROSSCOMPILING_TRUE@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +HAVE_ARES = @HAVE_ARES@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_LIBZ_FALSE = @HAVE_LIBZ_FALSE@ +HAVE_LIBZ_TRUE = @HAVE_LIBZ_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +KRB4_ENABLED = @KRB4_ENABLED@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MANOPT = @MANOPT@ +MIMPURE_FALSE = @MIMPURE_FALSE@ +MIMPURE_TRUE = @MIMPURE_TRUE@ +NO_UNDEFINED_FALSE = @NO_UNDEFINED_FALSE@ +NO_UNDEFINED_TRUE = @NO_UNDEFINED_TRUE@ +NROFF = @NROFF@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_ENABLED = @OPENSSL_ENABLED@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_MANUAL_FALSE = @USE_MANUAL_FALSE@ +USE_MANUAL_TRUE = @USE_MANUAL_TRUE@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_AS = @ac_ct_AS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DLLTOOL = @ac_ct_DLLTOOL@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_OBJDUMP = @ac_ct_OBJDUMP@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +AUTOMAKE_OPTIONS = foreign +EXTRA_DIST = CHANGES COPYING maketgz reconf Makefile.dist curl-config.in \ + curl-style.el sample.emacs RELEASE-NOTES buildconf buildconf.bat + +bin_SCRIPTS = curl-config +SUBDIRS = lib src +DIST_SUBDIRS = $(SUBDIRS) tests include packages docs +all: all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \ + cd $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +curl-config: $(top_builddir)/config.status $(srcdir)/curl-config.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \ + else :; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if (etags --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + else \ + include_option=--include; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkdir_p) $(distdir)/. $(distdir)/packages/EPM $(distdir)/packages/Linux/RPM + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || mkdir "$(distdir)/$$subdir" \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="../$(top_distdir)" \ + distdir="../$(distdir)/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + $(AMTAR) chof - $(distdir) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + $(AMTAR) chof - $(distdir) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(AMTAR) xf - ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(AMTAR) xf - ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(SCRIPTS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook + +install-exec-am: install-binSCRIPTS + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-info-am + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-libtool clean-recursive \ + ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ + dist-shar dist-tarZ dist-zip distcheck distclean \ + distclean-generic distclean-libtool distclean-recursive \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-binSCRIPTS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-libtool mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am \ + uninstall-binSCRIPTS uninstall-info-am + + +dist-hook: + rm -rf $(top_builddir)/tests/log + find $(distdir) -name "*.dist" -exec rm {} \; + (distit=`find $(srcdir) -name "*.dist"`; \ + for file in $$distit; do \ + strip=`echo $$file | sed -e s/^$(srcdir)// -e s/\.dist//`; \ + cp $$file $(distdir)$$strip; \ + done) + +html: + cd docs; make html + +pdf: + cd docs; make pdf + +check: test + +test: + @(cd tests; $(MAKE) all quiet-test) + +test-full: + @(cd tests; $(MAKE) all full-test) + +test-torture: + @(cd tests; $(MAKE) all torture-test) + +# +# Build source and binary rpms. For rpm-3.0 and above, the ~/.rpmmacros +# must contain the following line: +# %_topdir /home/loic/local/rpm +# and that /home/loic/local/rpm contains the directory SOURCES, BUILD etc. +# +# cd /home/loic/local/rpm ; mkdir -p SOURCES BUILD RPMS/i386 SPECS SRPMS +# +# If additional configure flags are needed to build the package, add the +# following in ~/.rpmmacros +# %configure CFLAGS="%{optflags}" ./configure %{_target_platform} --prefix=%{_prefix} ${AM_CONFIGFLAGS} +# and run make rpm in the following way: +# AM_CONFIGFLAGS='--with-uri=/home/users/loic/local/RedHat-6.2' make rpm +# + +rpms: + $(MAKE) RPMDIST=curl rpm + $(MAKE) RPMDIST=curl-ssl rpm + +rpm: + RPM_TOPDIR=`rpm --showrc | $(PERL) -n -e 'print if(s/.*_topdir\s+(.*)/$$1/)'` ; \ + cp $(srcdir)/packages/Linux/RPM/$(RPMDIST).spec $$RPM_TOPDIR/SPECS ; \ + cp $(PACKAGE)-$(VERSION).tar.gz $$RPM_TOPDIR/SOURCES ; \ + rpm -ba --clean --rmsource $$RPM_TOPDIR/SPECS/$(RPMDIST).spec ; \ + mv $$RPM_TOPDIR/RPMS/i386/$(RPMDIST)-*.rpm . ; \ + mv $$RPM_TOPDIR/SRPMS/$(RPMDIST)-*.src.rpm . + +# +# Build a Solaris pkkgadd format file +# run 'make pkgadd' once you've done './configure' and 'make' to make a Solaris pkgadd format +# file (which ends up back in this directory). +# The pkgadd file is in 'pkgtrans' format, so to install on Solaris, do +# pkgadd -d ./HAXXcurl-* +# + +# gak - libtool requires an absoulte directory, hence the pwd below... +pkgadd: + umask 022 ; \ + make install DESTDIR=`/bin/pwd`/packages/Solaris/root ; \ + cat COPYING > $(srcdir)/packages/Solaris/copyright ; \ + cd $(srcdir)/packages/Solaris && $(MAKE) package + +# +# Build a cygwin binary tarball installation file +# resulting .tar.bz2 file will end up at packages/Win32/cygwin +cygwinbin: + $(MAKE) -C packages/Win32/cygwin cygwinbin + +# We extend the standard install with a custom hook: +install-data-hook: + cd include && $(MAKE) install + cd docs && $(MAKE) install + +# We extend the standard uninstall with a custom hook: +uninstall-hook: + cd include && $(MAKE) uninstall + cd docs && $(MAKE) uninstall +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/curl-7.12.2/README b/src/curl-7.12.2/README new file mode 100644 index 0000000..fa0353b --- /dev/null +++ b/src/curl-7.12.2/README @@ -0,0 +1,79 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +README + + Curl is a command line tool for transferring data specified with URL + syntax. Find out how to use Curl by reading the curl.1 man page or the + MANUAL document. Find out how to install Curl by reading the INSTALL + document. + + libcurl is the library curl is using to do its job. It is readily + available to be used by your software. Read the libcurl.3 man page to + learn how! + + You find answers to the most frequent questions we get in the FAQ document. + + Study the COPYING file for distribution terms and similar. If you distribute + curl binaries or other binaries that involve libcurl, you might enjoy the + LICENSE-MIXING document. + +CONTACT + + If you have problems, questions, ideas or suggestions, please contact us + by posting to a suitable mailing list. See http://curl.haxx.se/mail/ + + Many major contributors to the project are listed in the THANKS document. + +WEB SITE + + Visit the curl web site or mirrors for the latest news: + + Sweden -- http://curl.haxx.se/ + Australia -- http://curl.planetmirror.com/ + Denmark -- http://curl.cofman.dk/ + Estonia -- http://curl.wildyou.net/ + Germany -- http://curl.mirror.at.stealer.net/ + Germany -- http://curl.netmirror.org/ + Russia -- http://curl.tsuren.net/ + Thailand -- http://curl.siamu.ac.th/ + US (CA) -- http://curl.mirror.redwire.net/ + US -- http://curl.signal42.com/ + +DOWNLOAD + + The official download mirror sites are: + + Australia -- http://curl.planetmirror.com/download.html + Estonia -- http://curl.wildyou.net/download.html + Germany -- ftp://ftp.fu-berlin.de/pub/unix/network/curl/ + Germany -- http://curl.mirror.at.stealer.net/download.html + Germany -- http://curl.netmirror.org/download.html + Germany -- http://www.mirrorspace.org/curl/ + Hongkong -- http://www.execve.net/curl/ + Russia -- http://curl.tsuren.net/download.html + Sweden -- ftp://ftp.sunet.se/pub/www/utilities/curl/ + Sweden -- http://cool.haxx.se/curl/ + Thailand -- http://curl.siamu.ac.th/download.html + US (CA) -- http://curl.mirror.redwire.net/download.html + +CVS + + To download the very latest source off the CVS server do this: + + cvs -d :pserver:anonymous@cool.haxx.se:/cvsroot/curl login + + (just press enter when asked for password) + + cvs -d :pserver:anonymous@cool.haxx.se:/cvsroot/curl co curl + + (you'll get a directory named curl created, filled with the source code) + +NOTICE + + Curl contains pieces of source code that is Copyright (c) 1998, 1999 + Kungliga Tekniska Högskolan. This notice is included here to comply with the + distribution terms. diff --git a/src/curl-7.12.2/RELEASE-NOTES b/src/curl-7.12.2/RELEASE-NOTES new file mode 100644 index 0000000..069d6e8 --- /dev/null +++ b/src/curl-7.12.2/RELEASE-NOTES @@ -0,0 +1,84 @@ +Curl and libcurl 7.12.2 + + Public curl release number: 83 + Releases counted from the very beginning: 110 + Available command line options: 96 + Available curl_easy_setopt() options: 121 + Number of public functions in libcurl: 36 + Amount of public web site mirrors: 13 + Number of known libcurl bindings: 27 + +This release includes the following changes: + + o the IDN code now verifies that only TLD-legitmate letters are used in the + name or a warning is displayed (when verbose is enabled) + o provides error texts for IDN errors + o file upload parts in formposts now get their directory names cut off + o added CURLINFO_OS_ERRNO + o added CURLOPT_FTPSSLAUTH to allow ftp connects to attempt "AUTH TLS" instead + before "AUTH SSL" + o curl_getdate() completely rewritten: may affect rare curl -z use cases + +This release includes the following bugfixes: + + o CURLOPT_FTP_CREATE_MISSING_DIRS works for third party transfers + o memory leak for cookies received with max-age set + o potential memory leaks in the window name resolver + o URLs with ?-letters in the user name or password fields + o libcurl error message is now provided when send() fails + o no more SIGPIPE on Mac OS X and other SO_NOSIGPIPE-supporting platforms + o HTTP resume was refused if redirected + o configure's gethostbyname check when both nsl and socket libs are required + o configure --with-libidn now checks the given path before defaults + o a race condition sometimes resulting in CURLE_COULDNT_RESOLVE_HOST in the + windows threaded name resolver code + o isspace() invokes with negative values in the cookie code + o a case of read-already-freed-data when CURLOPT_VERBOSE is used and a (very) + persistent connection + o now includes descriptive error messages for IDN errors + o more forgivning PASS response code check for better working with proftpd + o curl/multi.h works better included in winsock-using apps + o curl_easy_reset() no longer enables the progress meter + o build fix for SSL disabled curl with SSL Engine support present + o configure --with-ssl=PATH now ignores pkg-config path info + o CURLOPT_SSLENGINE can be set to NULL even if no engine support is available + o LDAP crash when more than one record was received + o connect failures properly stores an error message in the errorbuffer + o Rare Location:-following problem with bad original URL + o -F can now add Content-Type on non-file sections + o double Host: header when following Location: with replaced Host: + o curl_multi_add_handle() return code + o "Proxy-Connection: close" is now understood and properly dealt with + o curl_getdate() crash + o downloading empty files now calls the write callback properly + o no reverse DNS lookups for ip-only addresses with ipv6-enabled libcurl + o file handler leak when getting an empty file:// URL + o libcurl works better multi-threaded on AIX (when built with xlc) + o cookies over proxy didn't match the path properly + o MSVC makefile fixes to build better + o FTP response 530 on 'PASS' now sends back a better error message + +Other curl-related news since the previous public release: + + o AdacURL version 7.12.1 http://www.almroth.com/adacurl/index.html + o pycurl version 7.12.1 http://pycurl.sourceforge.net/ + o tclcurl version 0.12.1 + http://personal1.iddeo.es/andresgarci/tclcurl/english/ + o libcurl.NET was announce: http://www.seasideresearch.com/downloads.html + o Get your fresh Mozilla-extracted ca cert bundle here: + http://curl.haxx.se/docs/caextract.html + o New web mirror in Taiwan: http://curl.cs.pu.edu.tw/ + +This release would not have looked like this without help, code, reports and +advice from friends like these: + + Casey O'Donnell, Roland Krikava, Alex, Alexander Krasnostavsky, Kjetil + Jacobsen, Ling Thio, Roman Koifman, Harshal Pradhan, Jonas Forsman, David + Tarendash, Daniel at touchtunes, Bertrand Demiddelaer, Andreas Rieke, + Jean-Claude Chauve, Dan Fandrich, Peter Sylvester, "Mekonikum", Jean-Philippe + Barrette-LaPierre, Günter Knauf, Larry Campbell, Fedor Karpelevitch, + Aleksandar Milivojevic, Gisle Vanem, Chris "Bob Bob", Chih-Chung Chang, Andy + Cedilnik, Alan Pinstein, Eric Vergnaud, Traian Nicolescu, runekl at + opoint.com + + Thanks! (and sorry if I forgot to mention someone) diff --git a/src/curl-7.12.2/acinclude.m4 b/src/curl-7.12.2/acinclude.m4 new file mode 100644 index 0000000..719f65d --- /dev/null +++ b/src/curl-7.12.2/acinclude.m4 @@ -0,0 +1,765 @@ +dnl Check for how to set a socket to non-blocking state. There seems to exist +dnl four known different ways, with the one used almost everywhere being POSIX +dnl and XPG3, while the other different ways for different systems (old BSD, +dnl Windows and Amiga). +dnl +dnl There are two known platforms (AIX 3.x and SunOS 4.1.x) where the +dnl O_NONBLOCK define is found but does not work. This condition is attempted +dnl to get caught in this script by using an excessive number of #ifdefs... +dnl +AC_DEFUN([CURL_CHECK_NONBLOCKING_SOCKET], +[ + AC_MSG_CHECKING([non-blocking sockets style]) + + AC_TRY_COMPILE([ +/* headers for O_NONBLOCK test */ +#include +#include +#include +],[ +/* try to compile O_NONBLOCK */ + +#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4) +# define PLATFORM_AIX_V3 +#endif + +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#error "O_NONBLOCK does not work on this platform" +#endif + int socket; + int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK); +],[ +dnl the O_NONBLOCK test was fine +nonblock="O_NONBLOCK" +AC_DEFINE(HAVE_O_NONBLOCK, 1, [use O_NONBLOCK for non-blocking sockets]) +],[ +dnl the code was bad, try a different program now, test 2 + + AC_TRY_COMPILE([ +/* headers for FIONBIO test */ +#include +#include +],[ +/* FIONBIO source test (old-style unix) */ + int socket; + int flags = ioctl(socket, FIONBIO, &flags); +],[ +dnl FIONBIO test was good +nonblock="FIONBIO" +AC_DEFINE(HAVE_FIONBIO, 1, [use FIONBIO for non-blocking sockets]) +],[ +dnl FIONBIO test was also bad +dnl the code was bad, try a different program now, test 3 + + AC_TRY_COMPILE([ +/* headers for ioctlsocket test (cygwin?) */ +#include +],[ +/* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); +],[ +dnl ioctlsocket test was good +nonblock="ioctlsocket" +AC_DEFINE(HAVE_IOCTLSOCKET, 1, [use ioctlsocket() for non-blocking sockets]) +],[ +dnl ioctlsocket didnt compile!, go to test 4 + + AC_TRY_LINK([ +/* headers for IoctlSocket test (Amiga?) */ +#include +],[ +/* IoctlSocket source code */ + int socket; + int flags = IoctlSocket(socket, FIONBIO, (long)1); +],[ +dnl ioctlsocket test was good +nonblock="IoctlSocket" +AC_DEFINE(HAVE_IOCTLSOCKET_CASE, 1, [use Ioctlsocket() for non-blocking sockets]) +],[ +dnl Ioctlsocket didnt compile, do test 5! + AC_TRY_COMPILE([ +/* headers for SO_NONBLOCK test (BeOS) */ +#include +#include +#include +],[ +/* SO_NONBLOCK source code */ + long b = 1; + int socket; + int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +],[ +dnl the SO_NONBLOCK test was good +nonblock="SO_NONBLOCK" +AC_DEFINE(HAVE_SO_NONBLOCK, 1, [use SO_NONBLOCK for non-blocking sockets]) +],[ +dnl test 5 didnt compile! +nonblock="nada" +AC_DEFINE(HAVE_DISABLED_NONBLOCKING, 1, [disabled non-blocking sockets]) +]) +dnl end of fifth test + +]) +dnl end of forth test + +]) +dnl end of third test + +]) +dnl end of second test + +]) +dnl end of non-blocking try-compile test + AC_MSG_RESULT($nonblock) + + if test "$nonblock" = "nada"; then + AC_MSG_WARN([non-block sockets disabled]) + fi +]) + +dnl Check for socklen_t: historically on BSD it is an int, and in +dnl POSIX 1g it is a type of its own, but some platforms use different +dnl types for the argument to getsockopt, getpeername, etc. So we +dnl have to test to find something that will work. +AC_DEFUN([TYPE_SOCKLEN_T], +[ + AC_CHECK_TYPE([socklen_t], ,[ + AC_MSG_CHECKING([for socklen_t equivalent]) + AC_CACHE_VAL([curl_cv_socklen_t_equiv], + [ + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + curl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t unsigned long "unsigned long"; do + AC_TRY_COMPILE([ + #ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + + int getpeername (int, $arg2 *, $t *); + ],[ + $t len; + getpeername(0,0,&len); + ],[ + curl_cv_socklen_t_equiv="$t" + break + ]) + done + done + + if test "x$curl_cv_socklen_t_equiv" = x; then + AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) + fi + ]) + AC_MSG_RESULT($curl_cv_socklen_t_equiv) + AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined])], + [#include +#include ]) +]) + +dnl Check for in_addr_t: it is used to receive the return code of inet_addr() +dnl and a few other things. If not found, we set it to unsigned int, as even +dnl 64-bit implementations use to set it to a 32-bit type. +AC_DEFUN([TYPE_IN_ADDR_T], +[ + AC_CHECK_TYPE([in_addr_t], ,[ + AC_MSG_CHECKING([for in_addr_t equivalent]) + AC_CACHE_VAL([curl_cv_in_addr_t_equiv], + [ + curl_cv_in_addr_t_equiv= + for t in "unsigned long" int size_t unsigned long; do + AC_TRY_COMPILE([ + #ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + ],[ + $t data = inet_addr ("1.2.3.4"); + ],[ + curl_cv_in_addr_t_equiv="$t" + break + ]) + done + + if test "x$curl_cv_in_addr_t_equiv" = x; then + AC_MSG_ERROR([Cannot find a type to use in place of in_addr_t]) + fi + ]) + AC_MSG_RESULT($curl_cv_in_addr_t_equiv) + AC_DEFINE_UNQUOTED(in_addr_t, $curl_cv_in_addr_t_equiv, + [type to use in place of in_addr_t if not defined])], + [#include +#include +#include ]) +]) + +dnl ************************************************************ +dnl check for "localhost", if it doesn't exist, we can't do the +dnl gethostbyname_r tests! +dnl + +AC_DEFUN([CURL_CHECK_WORKING_RESOLVER],[ +AC_MSG_CHECKING([if "localhost" resolves]) +AC_TRY_RUN([ +#include +#include +#include + +int +main () { +struct hostent *h; +h = gethostbyname("localhost"); +exit (h == NULL ? 1 : 0); }],[ + AC_MSG_RESULT(yes)],[ + AC_MSG_RESULT(no) + AC_MSG_ERROR([can't figure out gethostbyname_r() since localhost doesn't resolve]) + + ] +) +]) + +dnl ************************************************************ +dnl check for working getaddrinfo() +dnl +AC_DEFUN([CURL_CHECK_WORKING_GETADDRINFO],[ + AC_CACHE_CHECK(for working getaddrinfo, ac_cv_working_getaddrinfo,[ + AC_TRY_RUN( [ +#include +#include +#include + +int main(void) +{ + struct addrinfo hints, *ai; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo("127.0.0.1", "8080", &hints, &ai); + if (error) { + return 1; + } + return 0; +} +],[ + ac_cv_working_getaddrinfo="yes" +],[ + ac_cv_working_getaddrinfo="no" +],[ + ac_cv_working_getaddrinfo="yes" +])]) +if test "$ac_cv_working_getaddrinfo" = "yes"; then + AC_DEFINE(HAVE_GETADDRINFO, 1, [Define if getaddrinfo exists and works]) + AC_DEFINE(ENABLE_IPV6, 1, [Define if you want to enable IPv6 support]) + + IPV6_ENABLED=1 + AC_SUBST(IPV6_ENABLED) +fi +]) + +dnl ************************************************************ +dnl check for working NI_WITHSCOPEID in getnameinfo() +dnl +AC_DEFUN([CURL_CHECK_NI_WITHSCOPEID],[ + AC_CACHE_CHECK(for working NI_WITHSCOPEID, ac_cv_working_ni_withscopeid,[ + + AC_RUN_IFELSE([[ +#include +#include +#include +#include +int main() +{ +#ifdef NI_WITHSCOPEID + struct sockaddr_storage ss; + int sslen = sizeof(ss); + int rc; + char hbuf[NI_MAXHOST]; + int fd = socket(AF_INET6, SOCK_STREAM, 0); + if(fd < 0) { + perror("socket()"); + return 1; /* couldn't create socket of either kind */ + } + + rc = getsockname(fd, (struct sockaddr *)&ss, &sslen); + if(rc) { + perror("getsockname()"); + return 2; + } + + rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), + NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID); + + if(rc) { + printf("rc = %s\n", gai_strerror(rc)); + return 3; + } + + return 0; /* everything works fine, use NI_WITHSCOPEID! */ +#else + return 4; /* we don't seem to have the definition, don't use it */ +#endif +} +]], +dnl program worked: +[ ac_cv_working_ni_withscopeid="yes" ], +dnl program failed: +[ ac_cv_working_ni_withscopeid="no" ], +dnl we cross-compile: +[ ac_cv_working_ni_withscopeid="yes" ] +) dnl end of AC_RUN_IFELSE + +]) dnl end of AC_CACHE_CHECK + +if test "$ac_cv_working_ni_withscopeid" = "yes"; then + AC_DEFINE(HAVE_NI_WITHSCOPEID, 1, + [Define if NI_WITHSCOPEID exists and works]) +fi + +]) dnl end of AC_DEFUN + + +AC_DEFUN([CURL_CHECK_LOCALTIME_R], +[ + dnl check for localtime_r + AC_CHECK_FUNCS(localtime_r,[ + AC_MSG_CHECKING(whether localtime_r is declared) + AC_EGREP_CPP(localtime_r,[ +#include ],[ + AC_MSG_RESULT(yes)],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(whether localtime_r with -D_REENTRANT is declared) + AC_EGREP_CPP(localtime_r,[ +#define _REENTRANT +#include ],[ + AC_DEFINE(NEED_REENTRANT) + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no))])]) +]) + +dnl +dnl This function checks for strerror_r(). If it isn't found at first, it +dnl retries with _THREAD_SAFE defined, as that is what AIX seems to require +dnl in order to find this function. +dnl +dnl If the function is found, it will then proceed to check how the function +dnl actually works: glibc-style or POSIX-style. +dnl +dnl glibc: +dnl char *strerror_r(int errnum, char *buf, size_t n); +dnl +dnl What this one does is to return the error string (no surprises there), +dnl but it doesn't usually copy anything into buf! The 'buf' and 'n' +dnl parameters are only meant as an optional working area, in case strerror_r +dnl needs it. A quick test on a few systems shows that it's generally not +dnl touched at all. +dnl +dnl POSIX: +dnl int strerror_r(int errnum, char *buf, size_t n); +dnl +AC_DEFUN([CURL_CHECK_STRERROR_R], +[ + dnl determine of strerror_r is present + AC_CHECK_FUNCS(strerror_r,[ + AC_MSG_CHECKING(whether strerror_r is declared) + AC_EGREP_CPP(strerror_r,[ +#include ],[ + strerror_r="yes" + AC_MSG_RESULT(yes)],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(whether strerror_r with -D_THREAD_SAFE is declared) + AC_EGREP_CPP(strerror_r,[ +#define _THREAD_SAFE +#include ],[ + strerror_r="yes" + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no))])]) + + if test "x$strerror_r" = "xyes"; then + + dnl check if strerror_r is properly declared in the headers + AC_CHECK_DECL(strerror_r, , + AC_DEFINE(HAVE_NO_STRERROR_R_DECL, 1, [we have no strerror_r() proto]) +, +[#include +#include +#include +#include +]) + + dnl determine if this strerror_r() is glibc or POSIX + AC_MSG_CHECKING([for a glibc strerror_r API]) + AC_TRY_RUN([ +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + char *string = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* this should've returned a string */ + if(!string || !string[0]) + return 99; + return 0; +} +], + GLIBC_STRERROR_R="1" + AC_DEFINE(HAVE_GLIBC_STRERROR_R, 1, [we have a glibc-style strerror_r()]) + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]), + dnl cross-compiling! + AC_MSG_NOTICE([cannot determine strerror_r() style: edit lib/config.h manually!]) + ) + + if test -z "$GLIBC_STRERROR_R"; then + + AC_MSG_CHECKING([for a POSIX strerror_r API]) + AC_TRY_RUN([ +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + int error = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* This should've returned zero, and written an error string in the + buffer.*/ + if(!buffer[0] || error) + return 99; + return 0; +} +], + AC_DEFINE(HAVE_POSIX_STRERROR_R, 1, [we have a POSIX-style strerror_r()]) + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) , + dnl cross-compiling! + AC_MSG_NOTICE([cannot determine strerror_r() style: edit lib/config.h manually!]) + ) + + fi dnl if not using glibc API + + fi dnl we have a strerror_r + +]) + +AC_DEFUN([CURL_CHECK_INET_NTOA_R], +[ + dnl determine if function definition for inet_ntoa_r exists. + AC_CHECK_FUNCS(inet_ntoa_r,[ + AC_MSG_CHECKING(whether inet_ntoa_r is declared) + AC_EGREP_CPP(inet_ntoa_r,[ +#include ],[ + AC_DEFINE(HAVE_INET_NTOA_R_DECL, 1, [inet_ntoa_r() is declared]) + AC_MSG_RESULT(yes)],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(whether inet_ntoa_r with -D_REENTRANT is declared) + AC_EGREP_CPP(inet_ntoa_r,[ +#define _REENTRANT +#include ],[ + AC_DEFINE(HAVE_INET_NTOA_R_DECL, 1, [inet_ntoa_r() is declared]) + AC_DEFINE(NEED_REENTRANT, 1, [need REENTRANT defined]) + AC_MSG_RESULT(yes)], + AC_MSG_RESULT(no))])]) +]) + +AC_DEFUN([CURL_CHECK_GETHOSTBYADDR_R], +[ + dnl check for number of arguments to gethostbyaddr_r. it might take + dnl either 5, 7, or 8 arguments. + AC_CHECK_FUNCS(gethostbyaddr_r,[ + AC_MSG_CHECKING(if gethostbyaddr_r takes 5 arguments) + AC_TRY_COMPILE([ +#include +#include ],[ +char * address; +int length; +int type; +struct hostent h; +struct hostent_data hdata; +int rc; +rc = gethostbyaddr_r(address, length, type, &h, &hdata);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYADDR_R_5, 1, [gethostbyaddr_r() takes 5 args]) + ac_cv_gethostbyaddr_args=5],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if gethostbyaddr_r with -D_REENTRANT takes 5 arguments) + AC_TRY_COMPILE([ +#define _REENTRANT +#include +#include ],[ +char * address; +int length; +int type; +struct hostent h; +struct hostent_data hdata; +int rc; +rc = gethostbyaddr_r(address, length, type, &h, &hdata);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYADDR_R_5, 1, [gethostbyaddr_r() takes 5 args]) + AC_DEFINE(NEED_REENTRANT, 1, [need REENTRANT]) + ac_cv_gethostbyaddr_args=5],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if gethostbyaddr_r takes 7 arguments) + AC_TRY_COMPILE([ +#include +#include ],[ +char * address; +int length; +int type; +struct hostent h; +char buffer[8192]; +int h_errnop; +struct hostent * hp; + +hp = gethostbyaddr_r(address, length, type, &h, + buffer, 8192, &h_errnop);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYADDR_R_7, 1, [gethostbyaddr_r() takes 7 args] ) + ac_cv_gethostbyaddr_args=7],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING(if gethostbyaddr_r takes 8 arguments) + AC_TRY_COMPILE([ +#include +#include ],[ +char * address; +int length; +int type; +struct hostent h; +char buffer[8192]; +int h_errnop; +struct hostent * hp; +int rc; + +rc = gethostbyaddr_r(address, length, type, &h, + buffer, 8192, &hp, &h_errnop);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYADDR_R_8, 1, [gethostbyaddr_r() takes 8 args]) + ac_cv_gethostbyaddr_args=8],[ + AC_MSG_RESULT(no) + have_missing_r_funcs="$have_missing_r_funcs gethostbyaddr_r"])])])])]) +]) + +AC_DEFUN([CURL_CHECK_GETHOSTBYNAME_R], +[ + dnl check for number of arguments to gethostbyname_r. it might take + dnl either 3, 5, or 6 arguments. + AC_CHECK_FUNCS(gethostbyname_r,[ + AC_MSG_CHECKING([if gethostbyname_r takes 3 arguments]) + AC_TRY_COMPILE([ +#include +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *, struct hostent *, struct hostent_data *);],[ +struct hostent_data data; +gethostbyname_r(NULL, NULL, NULL);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_3, 1, [gethostbyname_r() takes 3 args]) + ac_cv_gethostbyname_args=3],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if gethostbyname_r with -D_REENTRANT takes 3 arguments]) + AC_TRY_COMPILE([ +#define _REENTRANT + +#include +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *,struct hostent *, struct hostent_data *);],[ +struct hostent_data data; +gethostbyname_r(NULL, NULL, NULL);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_3, 1, [gethostbyname_r() takes 3 args]) + AC_DEFINE(NEED_REENTRANT, 1, [needs REENTRANT]) + ac_cv_gethostbyname_args=3],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if gethostbyname_r takes 5 arguments]) + AC_TRY_COMPILE([ +#include +#include +#undef NULL +#define NULL (void *)0 + +struct hostent * +gethostbyname_r(const char *, struct hostent *, char *, int, int *);],[ +gethostbyname_r(NULL, NULL, NULL, 0, NULL);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_5, 1, [gethostbyname_r() takes 5 args]) + ac_cv_gethostbyname_args=5],[ + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if gethostbyname_r takes 6 arguments]) + AC_TRY_COMPILE([ +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *, struct hostent *, char *, size_t, +struct hostent **, int *);],[ +gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL);],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_6, 1, [gethostbyname_r() takes 6 args]) + ac_cv_gethostbyname_args=6],[ + AC_MSG_RESULT(no) + have_missing_r_funcs="$have_missing_r_funcs gethostbyname_r"], + [ac_cv_gethostbyname_args=0])], + [ac_cv_gethostbyname_args=0])], + [ac_cv_gethostbyname_args=0])], + [ac_cv_gethostbyname_args=0])]) + +if test "$ac_cv_func_gethostbyname_r" = "yes"; then + if test "$ac_cv_gethostbyname_args" = "0"; then + dnl there's a gethostbyname_r() function, but we don't know how + dnl many arguments it wants! + AC_MSG_ERROR([couldn't figure out how to use gethostbyname_r()]) + fi +fi +]) + +dnl We create a function for detecting which compiler we use and then set as +dnl pendantic compiler options as possible for that particular compiler. The +dnl options are only used for debug-builds. + +AC_DEFUN([CURL_CC_DEBUG_OPTS], +[ + if test "$GCC" = "yes"; then + + dnl figure out gcc version! + AC_MSG_CHECKING([gcc version]) + gccver=`$CC -dumpversion` + num1=`echo $gccver | cut -d . -f1` + num2=`echo $gccver | cut -d . -f2` + gccnum=`(expr $num1 "*" 100 + $num2) 2>/dev/null` + AC_MSG_RESULT($gccver) + + AC_MSG_CHECKING([if this is icc in disguise]) + AC_EGREP_CPP([^__INTEL_COMPILER], [__INTEL_COMPILER], + dnl action if the text is found, this it has not been replaced by the + dnl cpp + ICC="no" + AC_MSG_RESULT([no]), + dnl the text was not found, it was replaced by the cpp + ICC="yes" + AC_MSG_RESULT([yes]) + ) + + if test "$ICC" = "yes"; then + dnl this is icc, not gcc. + + dnl ICC warnings we ignore: + dnl * 269 warns on our "%Od" printf formatters for curl_off_t output: + dnl "invalid format string conversion" + dnl * 279 warns on static conditions in while expressions + dnl * 981 warns on "operands are evaluated in unspecified order" + dnl * 1418 "external definition with no prior declaration" + dnl * 1419 warns on "external declaration in primary source file" + dnl which we know and do on purpose. + + WARN="-wd279,269,981,1418,1419" + + if test "$gccnum" -gt "600"; then + dnl icc 6.0 and older doesn't have the -Wall flag + WARN="-Wall $WARN" + fi + else dnl $ICC = yes + dnl this is a set of options we believe *ALL* gcc versions support: + WARN="-W -Wall -Wwrite-strings -pedantic -Wno-long-long -Wpointer-arith -Wnested-externs -Winline -Wmissing-declarations -Wmissing-prototypes -Wsign-compare" + + dnl -Wcast-align is a bit too annoying on all gcc versions ;-) + + if test "$gccnum" -gt "295"; then + dnl only if the compiler is newer than 2.95 since we got lots of + dnl "`_POSIX_C_SOURCE' is not defined" in system headers with + dnl gcc 2.95.4 on FreeBSD 4.9! + WARN="$WARN -Wundef" + fi + + if test "$gccnum" -ge "296"; then + dnl gcc 2.96 or later + WARN="$WARN -Wfloat-equal" + fi + + if test "$gccnum" -gt "296"; then + dnl this option does not exist in 2.96 + WARN="$WARN -Wno-format-nonliteral" + fi + + dnl -Wunreachable-code seems totally unreliable on my gcc 3.3.2 on + dnl on i686-Linux as it gives us heaps with false positives + if test "$gccnum" -ge "303"; then + dnl gcc 3.3 and later + WARN="$WARN -Wendif-labels -Wstrict-prototypes" + fi + + if test "$gccnum" -ge "304"; then + # try -Wunreachable-code on gcc 3.4 + WARN="$WARN -Wunreachable-code" + fi + + for flag in $CPPFLAGS; do + case "$flag" in + -I*) + dnl Include path, provide a -isystem option for the same dir + dnl to prevent warnings in those dirs. The -isystem was not very + dnl reliable on earlier gcc versions. + add=`echo $flag | sed 's/^-I/-isystem /g'` + WARN="$WARN $add" + ;; + esac + done + + fi dnl $ICC = no + + CFLAGS="$CFLAGS $WARN" + + AC_MSG_NOTICE([Added this set of compiler options: $WARN]) + + else dnl $GCC = yes + + AC_MSG_NOTICE([Added no extra compiler options]) + + fi dnl $GCC = yes + + dnl strip off optimizer flags + NEWFLAGS="" + for flag in $CFLAGS; do + case "$flag" in + -O*) + dnl echo "cut off $flag" + ;; + *) + NEWFLAGS="$NEWFLAGS $flag" + ;; + esac + done + CFLAGS=$NEWFLAGS + +]) dnl end of AC_DEFUN() + diff --git a/src/curl-7.12.2/aclocal.m4 b/src/curl-7.12.2/aclocal.m4 new file mode 100644 index 0000000..fe71d99 --- /dev/null +++ b/src/curl-7.12.2/aclocal.m4 @@ -0,0 +1,6876 @@ +# generated automatically by aclocal 1.8.3 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- + +# serial 47 AC_PROG_LIBTOOL + + +# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) +# ----------------------------------------------------------- +# If this macro is not defined by Autoconf, define it here. +m4_ifdef([AC_PROVIDE_IFELSE], + [], + [m4_define([AC_PROVIDE_IFELSE], + [m4_ifdef([AC_PROVIDE_$1], + [$2], [$3])])]) + + +# AC_PROG_LIBTOOL +# --------------- +AC_DEFUN([AC_PROG_LIBTOOL], +[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl +dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX +dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX. + AC_PROVIDE_IFELSE([AC_PROG_CXX], + [AC_LIBTOOL_CXX], + [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX + ])]) +dnl And a similar setup for Fortran 77 support + AC_PROVIDE_IFELSE([AC_PROG_F77], + [AC_LIBTOOL_F77], + [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77 +])]) + +dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly. +dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run +dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both. + AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ], + [AC_LIBTOOL_GCJ], + [ifdef([AC_PROG_GCJ], + [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])]) + ifdef([A][M_PROG_GCJ], + [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])]) + ifdef([LT_AC_PROG_GCJ], + [define([LT_AC_PROG_GCJ], + defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])]) +])])# AC_PROG_LIBTOOL + + +# _AC_PROG_LIBTOOL +# ---------------- +AC_DEFUN([_AC_PROG_LIBTOOL], +[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl +AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl +AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl +AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +# Prevent multiple expansion +define([AC_PROG_LIBTOOL], []) +])# _AC_PROG_LIBTOOL + + +# AC_LIBTOOL_SETUP +# ---------------- +AC_DEFUN([AC_LIBTOOL_SETUP], +[AC_PREREQ(2.50)dnl +AC_REQUIRE([AC_ENABLE_SHARED])dnl +AC_REQUIRE([AC_ENABLE_STATIC])dnl +AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_LD])dnl +AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl +AC_REQUIRE([AC_PROG_NM])dnl + +AC_REQUIRE([AC_PROG_LN_S])dnl +AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl +# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! +AC_REQUIRE([AC_OBJEXT])dnl +AC_REQUIRE([AC_EXEEXT])dnl +dnl + +AC_LIBTOOL_SYS_MAX_CMD_LEN +AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE +AC_LIBTOOL_OBJDIR + +AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl +_LT_AC_PROG_ECHO_BACKSLASH + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'] + +# Same as above, but do not quote variable references. +[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'] + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +# Constants: +rm="rm -f" + +# Global variables: +default_ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except M$VC, +# which needs '.lib'). +libext=a +ltmain="$ac_aux_dir/ltmain.sh" +ofile="$default_ofile" +with_gnu_ld="$lt_cv_prog_gnu_ld" + +AC_CHECK_TOOL(AR, ar, false) +AC_CHECK_TOOL(RANLIB, ranlib, :) +AC_CHECK_TOOL(STRIP, strip, :) + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +test -z "$AS" && AS=as +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$DLLTOOL" && DLLTOOL=dlltool +test -z "$LD" && LD=ld +test -z "$LN_S" && LN_S="ln -s" +test -z "$MAGIC_CMD" && MAGIC_CMD=file +test -z "$NM" && NM=nm +test -z "$SED" && SED=sed +test -z "$OBJDUMP" && OBJDUMP=objdump +test -z "$RANLIB" && RANLIB=: +test -z "$STRIP" && STRIP=: +test -z "$ac_objext" && ac_objext=o + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds" + ;; + *) + old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + +# Only perform the check for file, if the check method requires it +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + AC_PATH_MAGIC + fi + ;; +esac + +AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no) +AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], +enable_win32_dll=yes, enable_win32_dll=no) + +AC_ARG_ENABLE([libtool-lock], + [AC_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +AC_ARG_WITH([pic], + [AC_HELP_STRING([--with-pic], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [pic_mode="$withval"], + [pic_mode=default]) +test -z "$pic_mode" && pic_mode=default + +# Use C for the default configuration in the libtool script +tagname= +AC_LIBTOOL_LANG_C_CONFIG +_LT_AC_TAGCONFIG +])# AC_LIBTOOL_SETUP + + +# _LT_AC_SYS_COMPILER +# ------------------- +AC_DEFUN([_LT_AC_SYS_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_AC_SYS_COMPILER + + +# _LT_AC_SYS_LIBPATH_AIX +# ---------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX], +[AC_LINK_IFELSE(AC_LANG_PROGRAM,[ +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi],[]) +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi +])# _LT_AC_SYS_LIBPATH_AIX + + +# _LT_AC_SHELL_INIT(ARG) +# ---------------------- +AC_DEFUN([_LT_AC_SHELL_INIT], +[ifdef([AC_DIVERSION_NOTICE], + [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], + [AC_DIVERT_PUSH(NOTICE)]) +$1 +AC_DIVERT_POP +])# _LT_AC_SHELL_INIT + + +# _LT_AC_PROG_ECHO_BACKSLASH +# -------------------------- +# Add some code to the start of the generated configure script which +# will find an echo command which doesn't interpret backslashes. +AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH], +[_LT_AC_SHELL_INIT([ +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} + +case X$ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` + ;; +esac + +echo=${ECHO-echo} +if test "X[$]1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X[$]1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then + # Yippee, $echo works! + : +else + # Restart under the correct shell. + exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} +fi + +if test "X[$]1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null && + echo_test_string="`eval $cmd`" && + (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null + then + break + fi + done +fi + +if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : +else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + + if test "X$echo" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + echo='print -r' + elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} + else + # Try using printf. + echo='printf %s\n' + if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + echo="$CONFIG_SHELL [$]0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$CONFIG_SHELL [$]0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do + if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null + then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "[$]0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} + else + # Oops. We lost completely, so just stick with echo. + echo=echo + fi + fi + fi + fi +fi +fi + +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +ECHO=$echo +if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then + ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" +fi + +AC_SUBST(ECHO) +])])# _LT_AC_PROG_ECHO_BACKSLASH + + +# _LT_AC_LOCK +# ----------- +AC_DEFUN([_LT_AC_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AC_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case "`/usr/bin/file conftest.o`" in + *32-bit*) + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], +[*-*-cygwin* | *-*-mingw* | *-*-pw32*) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; + ]) +esac + +need_locks="$enable_libtool_lock" + +])# _LT_AC_LOCK + + +# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], +[AC_REQUIRE([LT_AC_PROG_SED]) +AC_CACHE_CHECK([$1], [$2], + [$2=no + ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + $2=yes + fi + fi + $rm conftest* +]) + +if test x"[$]$2" = xyes; then + ifelse([$5], , :, [$5]) +else + ifelse([$6], , :, [$6]) +fi +])# AC_LIBTOOL_COMPILER_OPTION + + +# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ------------------------------------------------------------ +# Check whether the given compiler option works +AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], +[AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + printf "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + else + $2=yes + fi + fi + $rm conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + ifelse([$4], , :, [$4]) +else + ifelse([$5], , :, [$5]) +fi +])# AC_LIBTOOL_LINKER_OPTION + + +# AC_LIBTOOL_SYS_MAX_CMD_LEN +# -------------------------- +AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], +[# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + *) + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while (test "X"`$CONFIG_SHELL [$]0 --fallback-echo "X$teststring" 2>/dev/null` \ + = "XX$teststring") >/dev/null 2>&1 && + new_result=`expr "X$teststring" : ".*" 2>&1` && + lt_cv_sys_max_cmd_len=$new_result && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + teststring= + # Add a significant safety factor because C++ compilers can tack on massive + # amounts of additional arguments before passing them to the linker. + # It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +])# AC_LIBTOOL_SYS_MAX_CMD_LEN + + +# _LT_AC_CHECK_DLFCN +# -------------------- +AC_DEFUN([_LT_AC_CHECK_DLFCN], +[AC_CHECK_HEADERS(dlfcn.h)dnl +])# _LT_AC_CHECK_DLFCN + + +# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ------------------------------------------------------------------ +AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF], +[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +}] +EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_unknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_AC_TRY_DLOPEN_SELF + + +# AC_LIBTOOL_DLOPEN_SELF +# ------------------- +AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], +[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_AC_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + LDFLAGS="$LDFLAGS $link_static_flag" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_AC_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +])# AC_LIBTOOL_DLOPEN_SELF + + +# AC_LIBTOOL_PROG_CC_C_O([TAGNAME]) +# --------------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler +AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O], +[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files + $rm out/* && rmdir out + cd .. + rmdir conftest + $rm conftest* +]) +])# AC_LIBTOOL_PROG_CC_C_O + + +# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME]) +# ----------------------------------------- +# Check to see if we can do hard links to lock some files if needed +AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], +[AC_REQUIRE([_LT_AC_LOCK])dnl + +hard_links="nottested" +if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS + + +# AC_LIBTOOL_OBJDIR +# ----------------- +AC_DEFUN([AC_LIBTOOL_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +])# AC_LIBTOOL_OBJDIR + + +# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME]) +# ---------------------------------------------- +# Check hardcoding attributes. +AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_AC_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \ + test -n "$_LT_AC_TAGVAR(runpath_var $1)" || \ + test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_AC_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_AC_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_AC_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH + + +# AC_LIBTOOL_SYS_LIB_STRIP +# ------------------------ +AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP], +[striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) +fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +])# AC_LIBTOOL_SYS_LIB_STRIP + + +# AC_LIBTOOL_SYS_DYNAMIC_LINKER +# ----------------------------- +# PORTME Fill in your ld.so characteristics +AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER], +[AC_MSG_CHECKING([dynamic linker characteristics]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`$SED -e 's/[:,\t]/ /g;s/=[^=]*$//;s/=[^= ]* / /g' /etc/ld.so.conf | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +knetbsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=yes + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no +])# AC_LIBTOOL_SYS_DYNAMIC_LINKER + + +# _LT_AC_TAGCONFIG +# ---------------- +AC_DEFUN([_LT_AC_TAGCONFIG], +[AC_ARG_WITH([tags], + [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@], + [include additional configurations @<:@automatic@:>@])], + [tagnames="$withval"]) + +if test -f "$ltmain" && test -n "$tagnames"; then + if test ! -f "${ofile}"; then + AC_MSG_WARN([output file `$ofile' does not exist]) + fi + + if test -z "$LTCC"; then + eval "`$SHELL ${ofile} --config | grep '^LTCC='`" + if test -z "$LTCC"; then + AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) + else + AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + fi + fi + + # Extract list of available tagged configurations in $ofile. + # Note that this assumes the entire list is on one line. + available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` + + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for tagname in $tagnames; do + IFS="$lt_save_ifs" + # Check whether tagname contains only valid characters + case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in + "") ;; + *) AC_MSG_ERROR([invalid tag name: $tagname]) + ;; + esac + + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null + then + AC_MSG_ERROR([tag name \"$tagname\" already exists]) + fi + + # Update the list of available tags. + if test -n "$tagname"; then + echo appending configuration tag \"$tagname\" to $ofile + + case $tagname in + CXX) + if test -n "$CXX" && test "X$CXX" != "Xno"; then + AC_LIBTOOL_LANG_CXX_CONFIG + else + tagname="" + fi + ;; + + F77) + if test -n "$F77" && test "X$F77" != "Xno"; then + AC_LIBTOOL_LANG_F77_CONFIG + else + tagname="" + fi + ;; + + GCJ) + if test -n "$GCJ" && test "X$GCJ" != "Xno"; then + AC_LIBTOOL_LANG_GCJ_CONFIG + else + tagname="" + fi + ;; + + RC) + AC_LIBTOOL_LANG_RC_CONFIG + ;; + + *) + AC_MSG_ERROR([Unsupported tag name: $tagname]) + ;; + esac + + # Append the new tag name to the list of available tags. + if test -n "$tagname" ; then + available_tags="$available_tags $tagname" + fi + fi + done + IFS="$lt_save_ifs" + + # Now substitute the updated list of available tags. + if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then + mv "${ofile}T" "$ofile" + chmod +x "$ofile" + else + rm -f "${ofile}T" + AC_MSG_ERROR([unable to update list of available tagged configurations.]) + fi +fi +])# _LT_AC_TAGCONFIG + + +# AC_LIBTOOL_DLOPEN +# ----------------- +# enable checks for dlopen support +AC_DEFUN([AC_LIBTOOL_DLOPEN], + [AC_BEFORE([$0],[AC_LIBTOOL_SETUP]) +])# AC_LIBTOOL_DLOPEN + + +# AC_LIBTOOL_WIN32_DLL +# -------------------- +# declare package support for building win32 dll's +AC_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_BEFORE([$0], [AC_LIBTOOL_SETUP]) +])# AC_LIBTOOL_WIN32_DLL + + +# AC_ENABLE_SHARED([DEFAULT]) +# --------------------------- +# implement the --enable-shared flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_SHARED], +[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([shared], + [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]AC_ENABLE_SHARED_DEFAULT) +])# AC_ENABLE_SHARED + + +# AC_DISABLE_SHARED +# ----------------- +#- set the default shared flag to --disable-shared +AC_DEFUN([AC_DISABLE_SHARED], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_SHARED(no) +])# AC_DISABLE_SHARED + + +# AC_ENABLE_STATIC([DEFAULT]) +# --------------------------- +# implement the --enable-static flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_STATIC], +[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([static], + [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]AC_ENABLE_STATIC_DEFAULT) +])# AC_ENABLE_STATIC + + +# AC_DISABLE_STATIC +# ----------------- +# set the default static flag to --disable-static +AC_DEFUN([AC_DISABLE_STATIC], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_STATIC(no) +])# AC_DISABLE_STATIC + + +# AC_ENABLE_FAST_INSTALL([DEFAULT]) +# --------------------------------- +# implement the --enable-fast-install flag +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +AC_DEFUN([AC_ENABLE_FAST_INSTALL], +[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE([fast-install], + [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT) +])# AC_ENABLE_FAST_INSTALL + + +# AC_DISABLE_FAST_INSTALL +# ----------------------- +# set the default to --disable-fast-install +AC_DEFUN([AC_DISABLE_FAST_INSTALL], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_FAST_INSTALL(no) +])# AC_DISABLE_FAST_INSTALL + + +# AC_LIBTOOL_PICMODE([MODE]) +# -------------------------- +# implement the --with-pic flag +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +AC_DEFUN([AC_LIBTOOL_PICMODE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +pic_mode=ifelse($#,1,$1,default) +])# AC_LIBTOOL_PICMODE + + +# AC_PROG_EGREP +# ------------- +# This is predefined starting with Autoconf 2.54, so this conditional +# definition can be removed once we require Autoconf 2.54 or later. +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP], +[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], + [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi]) + EGREP=$ac_cv_prog_egrep + AC_SUBST([EGREP]) +])]) + + +# AC_PATH_TOOL_PREFIX +# ------------------- +# find a file program which can recognise shared library +AC_DEFUN([AC_PATH_TOOL_PREFIX], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="ifelse([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +])# AC_PATH_TOOL_PREFIX + + +# AC_PATH_MAGIC +# ------------- +# find a file program which can recognise a shared library +AC_DEFUN([AC_PATH_MAGIC], +[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# AC_PATH_MAGIC + + +# AC_PROG_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([AC_PROG_LD], +[AC_ARG_WITH([gnu-ld], + [AC_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no]) +AC_REQUIRE([LT_AC_PROG_SED])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case "$host_cpu" in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be Linux ELF. +linux*) + case $host_cpu in + alpha*|hppa*|i*86|ia64*|m68*|mips*|powerpc*|sparc*|s390*|sh*) + lt_cv_deplibs_check_method=pass_all ;; + *) + # glibc up to 2.1.1 does not perform some relocations on ARM + # this will be overridden with pass_all, but let us keep it just in case + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; + esac + lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so` + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +nto-qnx*) + lt_cv_deplibs_check_method=unknown + ;; + +openbsd*) + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB shared object' + else + lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +sco3.2v5*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown +])# AC_DEPLIBS_CHECK_METHOD + + +# AC_PROG_NM +# ---------- +# find the pathname to a BSD-compatible name lister +AC_DEFUN([AC_PROG_NM], +[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/${ac_tool_prefix}nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + esac + fi + done + IFS="$lt_save_ifs" + test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm +fi]) +NM="$lt_cv_path_NM" +])# AC_PROG_NM + + +# AC_CHECK_LIBM +# ------------- +# check for math library +AC_DEFUN([AC_CHECK_LIBM], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +])# AC_CHECK_LIBM + + +# AC_LIBLTDL_CONVENIENCE([DIRECTORY]) +# ----------------------------------- +# sets LIBLTDL to the link flags for the libltdl convenience library and +# LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-convenience to the configure arguments. Note that LIBLTDL +# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +# DIRECTORY is not provided, it is assumed to be `libltdl'. LIBLTDL will +# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed with +# '${top_srcdir}/' (note the single quotes!). If your package is not +# flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. +AC_DEFUN([AC_LIBLTDL_CONVENIENCE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + case $enable_ltdl_convenience in + no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; + "") enable_ltdl_convenience=yes + ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; + esac + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la + LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) + # For backwards non-gettext consistent compatibility... + INCLTDL="$LTDLINCL" +])# AC_LIBLTDL_CONVENIENCE + + +# AC_LIBLTDL_INSTALLABLE([DIRECTORY]) +# ----------------------------------- +# sets LIBLTDL to the link flags for the libltdl installable library and +# LTDLINCL to the include flags for the libltdl header and adds +# --enable-ltdl-install to the configure arguments. Note that LIBLTDL +# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If +# DIRECTORY is not provided and an installed libltdl is not found, it is +# assumed to be `libltdl'. LIBLTDL will be prefixed with '${top_builddir}/' +# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single +# quotes!). If your package is not flat and you're not using automake, +# define top_builddir and top_srcdir appropriately in the Makefiles. +# In the future, this macro may have to be called after AC_PROG_LIBTOOL. +AC_DEFUN([AC_LIBLTDL_INSTALLABLE], +[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + AC_CHECK_LIB(ltdl, lt_dlinit, + [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], + [if test x"$enable_ltdl_install" = xno; then + AC_MSG_WARN([libltdl not installed, but installation disabled]) + else + enable_ltdl_install=yes + fi + ]) + if test x"$enable_ltdl_install" = x"yes"; then + ac_configure_args="$ac_configure_args --enable-ltdl-install" + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la + LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) + else + ac_configure_args="$ac_configure_args --enable-ltdl-install=no" + LIBLTDL="-lltdl" + LTDLINCL= + fi + # For backwards non-gettext consistent compatibility... + INCLTDL="$LTDLINCL" +])# AC_LIBLTDL_INSTALLABLE + + +# AC_LIBTOOL_CXX +# -------------- +# enable support for C++ libraries +AC_DEFUN([AC_LIBTOOL_CXX], +[AC_REQUIRE([_LT_AC_LANG_CXX]) +])# AC_LIBTOOL_CXX + + +# _LT_AC_LANG_CXX +# --------------- +AC_DEFUN([_LT_AC_LANG_CXX], +[AC_REQUIRE([AC_PROG_CXX]) +AC_REQUIRE([AC_PROG_CXXCPP]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX]) +])# _LT_AC_LANG_CXX + + +# AC_LIBTOOL_F77 +# -------------- +# enable support for Fortran 77 libraries +AC_DEFUN([AC_LIBTOOL_F77], +[AC_REQUIRE([_LT_AC_LANG_F77]) +])# AC_LIBTOOL_F77 + + +# _LT_AC_LANG_F77 +# --------------- +AC_DEFUN([_LT_AC_LANG_F77], +[AC_REQUIRE([AC_PROG_F77]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77]) +])# _LT_AC_LANG_F77 + + +# AC_LIBTOOL_GCJ +# -------------- +# enable support for GCJ libraries +AC_DEFUN([AC_LIBTOOL_GCJ], +[AC_REQUIRE([_LT_AC_LANG_GCJ]) +])# AC_LIBTOOL_GCJ + + +# _LT_AC_LANG_GCJ +# --------------- +AC_DEFUN([_LT_AC_LANG_GCJ], +[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[], + [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[], + [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])], + [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])], + [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ]) +])# _LT_AC_LANG_GCJ + + +# AC_LIBTOOL_RC +# -------------- +# enable support for Windows resource files +AC_DEFUN([AC_LIBTOOL_RC], +[AC_REQUIRE([LT_AC_PROG_RC]) +_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC]) +])# AC_LIBTOOL_RC + + +# AC_LIBTOOL_LANG_C_CONFIG +# ------------------------ +# Ensure that the configuration vars for the C compiler are +# suitably defined. Those variables are subsequently used by +# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG], [_LT_AC_LANG_C_CONFIG]) +AC_DEFUN([_LT_AC_LANG_C_CONFIG], +[lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_AC_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}\n' + +_LT_AC_SYS_COMPILER + +# +# Check for any special shared library compilation flags. +# +_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)= +if test "$GCC" = no; then + case $host_os in + sco3.2v5*) + _LT_AC_TAGVAR(lt_prog_cc_shlib, $1)='-belf' + ;; + esac +fi +if test -n "$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)"; then + AC_MSG_WARN([`$CC' requires `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to build shared libraries]) + if echo "$old_CC $old_CFLAGS " | grep "[[ ]]$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)[[ ]]" >/dev/null; then : + else + AC_MSG_WARN([add `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to the CC or CFLAGS env variable and reconfigure]) + _LT_AC_TAGVAR(lt_cv_prog_cc_can_build_shared, $1)=no + fi +fi + + +# +# Check to make sure the static flag actually works. +# +AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $_LT_AC_TAGVAR(lt_prog_compiler_static, $1) works], + _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), + $_LT_AC_TAGVAR(lt_prog_compiler_static, $1), + [], + [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) + + +AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1) +AC_LIBTOOL_PROG_COMPILER_PIC($1) +AC_LIBTOOL_PROG_CC_C_O($1) +AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) +AC_LIBTOOL_PROG_LD_SHLIBS($1) +AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) +AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +AC_LIBTOOL_SYS_LIB_STRIP +AC_LIBTOOL_DLOPEN_SELF($1) + +# Report which librarie types wil actually be built +AC_MSG_CHECKING([if libtool supports shared libraries]) +AC_MSG_RESULT([$can_build_shared]) + +AC_MSG_CHECKING([whether to build shared libraries]) +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + +aix4* | aix5*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + darwin* | rhapsody*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + output_verbose_link_cmd='echo' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring' + _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; +esac +AC_MSG_RESULT([$enable_shared]) + +AC_MSG_CHECKING([whether to build static libraries]) +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes +AC_MSG_RESULT([$enable_static]) + +AC_LIBTOOL_CONFIG($1) + +AC_LANG_POP +CC="$lt_save_CC" +])# AC_LIBTOOL_LANG_C_CONFIG + + +# AC_LIBTOOL_LANG_CXX_CONFIG +# -------------------------- +# Ensure that the configuration vars for the C compiler are +# suitably defined. Those variables are subsequently used by +# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. +AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)]) +AC_DEFUN([_LT_AC_LANG_CXX_CONFIG], +[AC_LANG_PUSH(C++) +AC_REQUIRE([AC_PROG_CXX]) +AC_REQUIRE([AC_PROG_CXXCPP]) + +_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_AC_TAGVAR(allow_undefined_flag, $1)= +_LT_AC_TAGVAR(always_export_symbols, $1)=no +_LT_AC_TAGVAR(archive_expsym_cmds, $1)= +_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_AC_TAGVAR(hardcode_direct, $1)=no +_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_AC_TAGVAR(hardcode_libdir_separator, $1)= +_LT_AC_TAGVAR(hardcode_minus_L, $1)=no +_LT_AC_TAGVAR(hardcode_automatic, $1)=no +_LT_AC_TAGVAR(module_cmds, $1)= +_LT_AC_TAGVAR(module_expsym_cmds, $1)= +_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown +_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_AC_TAGVAR(no_undefined_flag, $1)= +_LT_AC_TAGVAR(whole_archive_flag_spec, $1)= +_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Dependencies to place before and after the object being linked: +_LT_AC_TAGVAR(predep_objects, $1)= +_LT_AC_TAGVAR(postdep_objects, $1)= +_LT_AC_TAGVAR(predeps, $1)= +_LT_AC_TAGVAR(postdeps, $1)= +_LT_AC_TAGVAR(compiler_lib_search_path, $1)= + +# Source file extension for C++ test sources. +ac_ext=cc + +# Object file extension for compiled C++ test sources. +objext=o +_LT_AC_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_AC_SYS_COMPILER + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_LD=$LD +lt_save_GCC=$GCC +GCC=$GXX +lt_save_with_gnu_ld=$with_gnu_ld +lt_save_path_LD=$lt_cv_path_LD +if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx +else + unset lt_cv_prog_gnu_ld +fi +if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX +else + unset lt_cv_path_LD +fi +test -z "${LDCXX+set}" || LD=$LDCXX +CC=${CXX-"c++"} +compiler=$CC +_LT_AC_TAGVAR(compiler, $1)=$CC +cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'` + +# We don't want -fno-exception wen compiling C++ code, so set the +# no_builtin_flag separately +if test "$GXX" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' +else + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= +fi + +if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + AC_PROG_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ + grep 'no-whole-archive' > /dev/null; then + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + +else + GXX=no + with_gnu_ld=no + wlarc= +fi + +# PORTME: fill in a description of your system's C++ link characteristics +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +_LT_AC_TAGVAR(ld_shlibs, $1)=yes +case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_AC_TAGVAR(archive_cmds, $1)='' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + else + # We have old collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds it's shared libraries. + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + dgux*) + case $cc_basename in + ec++) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + freebsd[12]*) + # C++ shared libraries reported to be fairly broken before switch to ELF + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + freebsd-elf*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + ;; + gnu*) + ;; + hpux9*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aCC) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[-]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + *) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + *) + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + aCC) + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case "$host_cpu" in + ia64*|hppa*64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + irix5* | irix6*) + case $cc_basename in + CC) + # SGI C++ + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' + fi + fi + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + linux*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc) + # Intel C++ + with_gnu_ld=yes + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + cxx) + # Compaq C++ + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + esac + ;; + lynxos*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + m88k*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + mvs*) + case $cc_basename in + cxx) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + osf3*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + cxx) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + osf4* | osf5*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + cxx) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry $objdir/so_locations -o $lib~ + $rm $lib.exp' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + psos*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + sco*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + lcc) + # Lucid + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The C++ compiler is used as linker so we must use $wl + # flag to pass the commands to the underlying system + # linker. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[[LR]]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | grep -v '^2\.7' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + fi + + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + fi + ;; + esac + ;; + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + vxworks*) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; +esac +AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) +test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_AC_TAGVAR(GCC, $1)="$GXX" +_LT_AC_TAGVAR(LD, $1)="$LD" + +AC_LIBTOOL_POSTDEP_PREDEP($1) +AC_LIBTOOL_PROG_COMPILER_PIC($1) +AC_LIBTOOL_PROG_CC_C_O($1) +AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) +AC_LIBTOOL_PROG_LD_SHLIBS($1) +AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) +AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +AC_LIBTOOL_SYS_LIB_STRIP +AC_LIBTOOL_DLOPEN_SELF($1) + +AC_LIBTOOL_CONFIG($1) + +AC_LANG_POP +CC=$lt_save_CC +LDCXX=$LD +LD=$lt_save_LD +GCC=$lt_save_GCC +with_gnu_ldcxx=$with_gnu_ld +with_gnu_ld=$lt_save_with_gnu_ld +lt_cv_path_LDCXX=$lt_cv_path_LD +lt_cv_path_LD=$lt_save_path_LD +lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld +lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +])# AC_LIBTOOL_LANG_CXX_CONFIG + +# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) +# ------------------------ +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[ +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +ifelse([$1],[],[cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext <> "$cfgfile" +ifelse([$1], [], +[#! $SHELL + +# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# +# This file is part of GNU Libtool: +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can 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. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="$SED -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +# The names of the tagged configurations supported by this script. +available_tags= + +# ### BEGIN LIBTOOL CONFIG], +[# ### BEGIN LIBTOOL TAG CONFIG: $tagname]) + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1) + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_[]_LT_AC_TAGVAR(compiler, $1) + +# Is the compiler the GNU C compiler? +with_gcc=$_LT_AC_TAGVAR(GCC, $1) + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_[]_LT_AC_TAGVAR(LD, $1) + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1) + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1) + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1) + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1) + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1) +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1) + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) + +# Commands used to build and install a shared archive. +archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1) +archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1) +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1) +module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1) + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1) + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1) + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1) + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1) + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1) + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1) + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1) + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1) + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1) + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1) + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1) + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1) + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1) + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1) + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)" + +# Set to yes if exported symbols are required. +always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1) + +# The commands to list exported symbols. +export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1) + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1) + +# Symbols that must always be exported. +include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1) + +ifelse([$1],[], +[# ### END LIBTOOL CONFIG], +[# ### END LIBTOOL TAG CONFIG: $tagname]) + +__EOF__ + +ifelse([$1],[], [ + case $host_os in + aix3*) + cat <<\EOF >> "$cfgfile" + +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +EOF + ;; + esac + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || \ + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +]) +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi +])# AC_LIBTOOL_CONFIG + + +# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------------------- +AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], +[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl + +_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + + AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI + + +# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE +# --------------------------------- +AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], +[AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_NM]) +AC_REQUIRE([AC_OBJEXT]) +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Transform the above into a raw symbol and a C symbol. +symxfrm='\1 \2\3 \3' + +# Transform an extracted symbol line into a proper C declaration +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) # Its linker distinguishes data from code symbols + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris* | sysv5*) + symcode='[[BDRT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Try without a prefix undercore, then with it. +for ac_symprfx in "" "_"; do + + # Write the raw and C identifiers. + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext < $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if grep ' nm_test_var$' "$nlist" >/dev/null; then + if grep ' nm_test_func$' "$nlist" >/dev/null; then + cat < conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' + + cat <> conftest.$ac_ext +#if defined (__STDC__) && __STDC__ +# define lt_ptr_t void * +#else +# define lt_ptr_t char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr_t address; +} +lt_preloaded_symbols[[]] = +{ +EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext + cat <<\EOF >> conftest.$ac_ext + {0, (lt_ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -f conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi +]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE + + +# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME]) +# --------------------------------------- +AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC], +[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_AC_TAGVAR(lt_prog_compiler_static, $1)= + +AC_MSG_CHECKING([for $compiler option to produce PIC]) + ifelse([$1],[CXX],[ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | os2* | pw32*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix4* | aix5*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68) + # Green Hills C++ Compiler + # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + dgux*) + case $cc_basename in + ec++) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + if test "$host_cpu" != ia64; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux*) + case $cc_basename in + KCC) + # KAI C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + icpc) + # Intel C++ + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + cxx) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC) + # Rational C++ 2.4.1 + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx) + # Digital/Compaq C++ + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + sco*) + case $cc_basename in + CC) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + *) + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx) + # Green Hills C++ Compiler + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc) + # Lucid + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + unixware*) + ;; + vxworks*) + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + newsos6) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + linux*) + case $CC in + icc* | ecc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + ccc*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + esac + ;; + + osf3* | osf4* | osf5*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + sco3.2v5*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kpic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-dn' + ;; + + solaris*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sunos4*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + uts4*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)]) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then + AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works], + _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1), + [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])" + ;; +esac +]) + + +# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME]) +# ------------------------------------ +# See if the linker supports building shared libraries. +AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS], +[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +ifelse([$1],[CXX],[ + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix4* | aix5*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + else + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw*) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +],[ + runpath_var= + _LT_AC_TAGVAR(allow_undefined_flag, $1)= + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_AC_TAGVAR(archive_cmds, $1)= + _LT_AC_TAGVAR(archive_expsym_cmds, $1)= + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)= + _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + _LT_AC_TAGVAR(thread_safe_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_minus_L, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown + _LT_AC_TAGVAR(hardcode_automatic, $1)=no + _LT_AC_TAGVAR(module_cmds, $1)= + _LT_AC_TAGVAR(module_expsym_cmds, $1)= + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_AC_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_" + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_AC_TAGVAR(ld_shlibs, $1)=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + fi + ;; + + amigaos*) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can't use + # them. + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(always_export_symbols, $1)=no + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + _LT_AC_TAGVAR(ld_shlibs, $1)=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sunos4*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_cmds, $1)="$tmp_archive_cmds" + supports_anon_versioning=no + case `$LD -v 2>/dev/null` in + *\ [01].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + if test $supports_anon_versioning = yes; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $output_objdir/$libname.ver~ +cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ +$echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + else + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="$tmp_archive_cmds" + fi + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = yes; then + runpath_var=LD_RUN_PATH + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= + fi + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + else + _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_AC_TAGVAR(archive_cmds, $1)='' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + else + # We have old collect2 + _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + _LT_AC_SYS_LIBPATH_AIX + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + _LT_AC_TAGVAR(always_export_symbols, $1)=yes + # Exported symbols can be pulled into shared objects from archives + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' ' + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds it's shared libraries. + _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + # see comment about different semantics on the GNU ld section + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + bsdi4*) + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes ; then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + case "$host_os" in + rhapsody* | darwin1.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[[012]]) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress' + ;; + 10.*) + _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_automatic, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience' + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + else + _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + dgux*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + freebsd1*) + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | kfreebsd*-gnu) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10* | hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case "$host_cpu" in + hppa*64*|ia64*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + ia64*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + ;; + *) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + openbsd*) + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + + os2*) + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ + $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + sco3.2v5*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; + esac + _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_AC_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_AC_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4.2uw2*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_AC_TAGVAR(hardcode_minus_L, $1)=no + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*) + _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z ${wl}text' + if test "$GCC" = yes; then + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + runpath_var='LD_RUN_PATH' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv5*) + _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_AC_TAGVAR(ld_shlibs, $1)=no + ;; + esac + fi +]) +AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) +test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_AC_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_MSG_CHECKING([whether -lc should be explicitly linked in]) + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1) + _LT_AC_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) + then + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + else + _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)]) + ;; + esac + fi + ;; +esac +])# AC_LIBTOOL_PROG_LD_SHLIBS + + +# _LT_AC_FILE_LTDLL_C +# ------------------- +# Be careful that the start marker always follows a newline. +AC_DEFUN([_LT_AC_FILE_LTDLL_C], [ +# /* ltdll.c starts here */ +# #define WIN32_LEAN_AND_MEAN +# #include +# #undef WIN32_LEAN_AND_MEAN +# #include +# +# #ifndef __CYGWIN__ +# # ifdef __CYGWIN32__ +# # define __CYGWIN__ __CYGWIN32__ +# # endif +# #endif +# +# #ifdef __cplusplus +# extern "C" { +# #endif +# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved); +# #ifdef __cplusplus +# } +# #endif +# +# #ifdef __CYGWIN__ +# #include +# DECLARE_CYGWIN_DLL( DllMain ); +# #endif +# HINSTANCE __hDllInstance_base; +# +# BOOL APIENTRY +# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) +# { +# __hDllInstance_base = hInst; +# return TRUE; +# } +# /* ltdll.c ends here */ +])# _LT_AC_FILE_LTDLL_C + + +# _LT_AC_TAGVAR(VARNAME, [TAGNAME]) +# --------------------------------- +AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])]) + + +# old names +AC_DEFUN([AM_PROG_LIBTOOL], [AC_PROG_LIBTOOL]) +AC_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AC_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) +AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) +AC_DEFUN([AM_PROG_LD], [AC_PROG_LD]) +AC_DEFUN([AM_PROG_NM], [AC_PROG_NM]) + +# This is just to silence aclocal about the macro not being used +ifelse([AC_DISABLE_FAST_INSTALL]) + +AC_DEFUN([LT_AC_PROG_GCJ], +[AC_CHECK_TOOL(GCJ, gcj, no) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS) +]) + +AC_DEFUN([LT_AC_PROG_RC], +[AC_CHECK_TOOL(RC, windres, no) +]) + +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +# LT_AC_PROG_SED +# -------------- +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +AC_DEFUN([LT_AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && break + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +SED=$lt_cv_path_SED +]) +AC_MSG_RESULT([$SED]) +]) + +# -*- Autoconf -*- +# Copyright (C) 2002, 2003 Free Software Foundation, Inc. +# Generated from amversion.in; do not edit by hand. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.8"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.8.3])]) + +# AM_AUX_DIR_EXPAND + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 6 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]) +fi])]) + +# serial 7 -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +#serial 2 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + grep '^DEP_FILES *= *[[^ @%:@]]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 7 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# This macro actually does too much some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 +# Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 11 + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl + +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# -*- Autoconf -*- +# Copyright (C) 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 1 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. +# From Jim Meyering + +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# -*- Autoconf -*- + + +# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. + +# Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # Keeping the `.' argument allows $(mkdir_p) to be used without + # argument. Indeed, we sometimes output rules like + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. + # (`test -n '$(somedir)' && $(mkdir_p) $(somedir)' is a more + # expensive solution, as it forces Make to start a sub-shell.) + mkdir_p='mkdir -p -- .' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# +# Check to make sure that the build environment is sane. +# + +# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# AM_PROG_INSTALL_STRIP + +# Copyright (C) 2001, 2003 Free Software Foundation, Inc. + +# This program is free software; you can 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, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +m4_include([acinclude.m4]) diff --git a/src/curl-7.12.2/buildconf b/src/curl-7.12.2/buildconf new file mode 100644 index 0000000..04aeabb --- /dev/null +++ b/src/curl-7.12.2/buildconf @@ -0,0 +1,187 @@ +#!/bin/sh + +die(){ + echo "$@" + exit +} + +# this works as 'which' but we use a different name to make it more obvious we +# aren't using 'which'! ;-) +findtool(){ + file="$1" + + IFS=":" + for path in $PATH + do + if test -r "$path/$file"; then + echo "$path/$file" + return + fi + done +} + +#-------------------------------------------------------------------------- +# autoconf 2.57 or newer +# +need_autoconf="2.57" +ac_version=`${AUTOCONF:-autoconf} --version 2>/dev/null|head -1| sed -e 's/^[^0-9]*//' -e 's/[a-z]* *$//'` +if test -z "$ac_version"; then + echo "buildconf: autoconf not found." + echo " You need autoconf version $need_autoconf or newer installed." + exit 1 +fi +IFS=.; set $ac_version; IFS=' ' +if test "$1" = "2" -a "$2" -lt "57" || test "$1" -lt "2"; then + echo "buildconf: autoconf version $ac_version found." + echo " You need autoconf version $need_autoconf or newer installed." + echo " If you have a sufficient autoconf installed, but it" + echo " is not named 'autoconf', then try setting the" + echo " AUTOCONF environment variable." + exit 1 +fi + +echo "buildconf: autoconf version $ac_version (ok)" + +#-------------------------------------------------------------------------- +# autoheader 2.50 or newer +# +ah_version=`${AUTOHEADER:-autoheader} --version 2>/dev/null|head -1| sed -e 's/^[^0-9]*//' -e 's/[a-z]* *$//'` +if test -z "$ah_version"; then + echo "buildconf: autoheader not found." + echo " You need autoheader version 2.50 or newer installed." + exit 1 +fi +IFS=.; set $ah_version; IFS=' ' +if test "$1" = "2" -a "$2" -lt "50" || test "$1" -lt "2"; then + echo "buildconf: autoheader version $ah_version found." + echo " You need autoheader version 2.50 or newer installed." + echo " If you have a sufficient autoheader installed, but it" + echo " is not named 'autoheader', then try setting the" + echo " AUTOHEADER environment variable." + exit 1 +fi + +echo "buildconf: autoheader version $ah_version (ok)" + +#-------------------------------------------------------------------------- +# automake 1.7 or newer +# +need_automake="1.7" +am_version=`${AUTOMAKE:-automake} --version 2>/dev/null|head -1| sed -e 's/^.* \([0-9]\)/\1/' -e 's/[a-z]* *$//' -e 's/\(.*\)\(-p.*\)/\1/'` +if test -z "$am_version"; then + echo "buildconf: automake not found." + echo " You need automake version $need_automake or newer installed." + exit 1 +fi +IFS=.; set $am_version; IFS=' ' +if test "$1" = "1" -a "$2" -lt "7" || test "$1" -lt "1"; then + echo "buildconf: automake version $am_version found." + echo " You need automake version $need_automake or newer installed." + echo " If you have a sufficient automake installed, but it" + echo " is not named 'automake', then try setting the" + echo " AUTOMAKE environment variable." + exit 1 +fi + +echo "buildconf: automake version $am_version (ok)" + + +#-------------------------------------------------------------------------- +# libtool check +# +LIBTOOL_WANTED_MAJOR=1 +LIBTOOL_WANTED_MINOR=4 +LIBTOOL_WANTED_PATCH=2 +LIBTOOL_WANTED_VERSION=1.4.2 + +# this approach that tries 'glibtool' first is some kind of work-around for +# some BSD-systems I believe that use to provide the GNU libtool named +# glibtool, with 'libtool' being something completely different. +libtool=`findtool glibtool 2>/dev/null` +if test ! -x "$libtool"; then + libtool=`findtool libtool` +fi + +# set the LIBTOOLIZE here so that glibtoolize is used if glibtool was found +LIBTOOLIZE="${libtool}ize" + +lt_pversion=`$libtool --version 2>/dev/null|head -1|sed -e 's/^[^0-9]*//g' -e 's/[- ].*//'` +if test -z "$lt_pversion"; then + echo "buildconf: libtool not found." + echo " You need libtool version $LIBTOOL_WANTED_VERSION or newer installed" + exit 1 +fi +lt_version=`echo $lt_pversion|sed -e 's/\([a-z]*\)$//'` +IFS=.; set $lt_version; IFS=' ' +lt_status="good" + +major=$1 +minor=$2 +patch=$3 + +if test "$major" = "$LIBTOOL_WANTED_MAJOR"; then + if test "$minor" -lt "$LIBTOOL_WANTED_MINOR"; then + lt_status="bad" + elif test -n "$LIBTOOL_WANTED_PATCH"; then + if test "$minor" -gt "$LIBTOOL_WANTED_MINOR"; then + lt_status="good" + elif test -n "$patch"; then + if test "$patch" -lt "$LIBTOOL_WANTED_PATCH"; then + lt_status="bad" + fi + else + lt_status="bad" + fi + fi +fi +if test $lt_status != "good"; then + echo "buildconf: libtool version $lt_pversion found." + echo " You need libtool version $LIBTOOL_WANTED_VERSION or newer installed" + exit 1 +fi + +echo "buildconf: libtool version $lt_version (ok)" + +#-------------------------------------------------------------------------- +# m4 check +# +m4=`${M4:-m4} --version 2>/dev/null|head -1`; +m4_version=`echo $m4 | sed -e 's/^.* \([0-9]\)/\1/' -e 's/[a-z]* *$//'` + +if { echo $m4 | grep "GNU" >/dev/null 2>&1; } then + echo "buildconf: GNU m4 version $m4_version (ok)" +else + echo "buildconf: m4 version $m4 found. You need a GNU m4 installed!" + exit 1 +fi + + +# ------------------------------------------------------------ + +# run the correct scripts now + +echo "buildconf: running libtoolize" +${LIBTOOLIZE:-libtoolize} --copy --automake --force || die "The libtool command failed" +echo "buildconf: running aclocal" +${ACLOCAL:-aclocal} $ACLOCAL_FLAGS || die "The aclocal command line failed" +echo "buildconf: running aclocal hack to convert all mv to mv -f" +perl -i.bak -pe 's/\bmv +([^-\s])/mv -f $1/g' aclocal.m4 +echo "buildconf: running autoheader" +${AUTOHEADER:-autoheader} || die "The autoheader command failed" +echo "buildconf: running autoconf" +${AUTOCONF:-autoconf} || die "The autoconf command failed" + +if test -d ares; then + cd ares + echo "buildconf: running aclocal in the ares directory" + ${ACLOCAL:-aclocal} $ACLOCAL_FLAGS || die "The ares aclocal command failed" + echo "buildconf: running autoconf in the ares directory" + ${AUTOCONF:-autoconf} || die "The ares autoconf command failed" + cd .. +fi + +echo "buildconf: running automake" +${AUTOMAKE:-automake} -a || die "The automake command failed" + +echo "buildconf: OK" +exit 0 diff --git a/src/curl-7.12.2/buildconf.bat b/src/curl-7.12.2/buildconf.bat new file mode 100644 index 0000000..3a804f7 --- /dev/null +++ b/src/curl-7.12.2/buildconf.bat @@ -0,0 +1,14 @@ +@echo off +REM set up a CVS tree to build when there's no autotools +REM $Revision: 1.2 $ +REM $Date: 2004/09/15 08:07:20 $ + +REM create ca-bundle.h +echo /* This file is generated automatically */ >lib\ca-bundle.h +echo #define CURL_CA_BUNDLE getenv("CURL_CA_BUNDLE") >>lib\ca-bundle.h + +REM create hugehelp.c +copy src\hugehelp.c.cvs src\hugehelp.c + +REM create Makefile +copy Makefile.dist Makefile \ No newline at end of file diff --git a/src/curl-7.12.2/config.guess b/src/curl-7.12.2/config.guess new file mode 100644 index 0000000..51fab47 --- /dev/null +++ b/src/curl-7.12.2/config.guess @@ -0,0 +1,1459 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2004-03-12' + +# This file is free software; you can 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. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pegasos:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit 0 ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha*:OpenVMS:*:*) + echo alpha-hp-vms + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + # Determine whether the default compiler uses glibc. + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #if __GLIBC__ >= 2 + LIBC=gnu + #else + LIBC= + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + # GNU/KFreeBSD systems have a "k" prefix to indicate we are using + # FreeBSD's kernel, but not the complete OS. + case ${LIBC} in gnu) kernel_only='k' ;; esac + echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + case `uname -p` in + *86) UNAME_PROCESSOR=i686 ;; + powerpc) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/curl-7.12.2/config.sub b/src/curl-7.12.2/config.sub new file mode 100644 index 0000000..ba33103 --- /dev/null +++ b/src/curl-7.12.2/config.sub @@ -0,0 +1,1549 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2004-03-12' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/curl-7.12.2/configure b/src/curl-7.12.2/configure new file mode 100644 index 0000000..b8a2589 --- /dev/null +++ b/src/curl-7.12.2/configure @@ -0,0 +1,33355 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59 for curl -. +# +# Report bugs to http://curl.haxx.se/mail/>. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +# +# Copyright (c) 1998 - 2004 Daniel Stenberg, +# This configure script may be copied, distributed and modified under the +# terms of the curl license; see COPYING for more details +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + + +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} + +case X$ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$ECHO" | sed 's,\\\\\$\\$0,'$0','` + ;; +esac + +echo=${ECHO-echo} +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then + # Yippee, $echo works! + : +else + # Restart under the correct shell. + exec $SHELL "$0" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null && + echo_test_string="`eval $cmd`" && + (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null + then + break + fi + done +fi + +if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : +else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + + if test "X$echo" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + echo='print -r' + elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} + else + # Try using printf. + echo='printf %s\n' + if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + echo="$CONFIG_SHELL $0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + echo="$CONFIG_SHELL $0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do + if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null + then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "$0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} + else + # Oops. We lost completely, so just stick with echo. + echo=echo + fi + fi + fi + fi +fi +fi + +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +ECHO=$echo +if test "X$ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then + ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" +fi + + + + +tagnames=${tagnames+${tagnames},}CXX + +tagnames=${tagnames+${tagnames},}F77 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='curl' +PACKAGE_TARNAME='curl' +PACKAGE_VERSION='-' +PACKAGE_STRING='curl -' +PACKAGE_BUGREPORT='a suitable curl mailing list => http://curl.haxx.se/mail/' + +ac_unique_file="lib/urldata.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subdirs_all="$ac_subdirs_all ares" +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT SED AR ac_ct_AR INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO AMTAR install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot VERSIONNUM PKGADD_PKG PKGADD_NAME PKGADD_VENDOR build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP EGREP LN_S ECHO RANLIB ac_ct_RANLIB DLLTOOL ac_ct_DLLTOOL AS ac_ct_AS OBJDUMP ac_ct_OBJDUMP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL NO_UNDEFINED_TRUE NO_UNDEFINED_FALSE MIMPURE_TRUE MIMPURE_FALSE CURL_DISABLE_HTTP CURL_DISABLE_GOPHER CURL_DISABLE_FTP CURL_DISABLE_FILE CURL_DISABLE_LDAP CURL_DISABLE_DICT CURL_DISABLE_TELNET IPV6_ENABLED RANDOM_FILE KRB4_ENABLED PKGCONFIG OPENSSL_ENABLED CURL_CA_BUNDLE CABUNDLE_TRUE CABUNDLE_FALSE HAVE_LIBZ HAVE_LIBZ_TRUE HAVE_LIBZ_FALSE PERL NROFF MANOPT USE_MANUAL_TRUE USE_MANUAL_FALSE HAVE_ARES subdirs CROSSCOMPILING_TRUE CROSSCOMPILING_FALSE LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP +ac_env_F77_set=${F77+set} +ac_env_F77_value=$F77 +ac_cv_env_F77_set=${F77+set} +ac_cv_env_F77_value=$F77 +ac_env_FFLAGS_set=${FFLAGS+set} +ac_env_FFLAGS_value=$FFLAGS +ac_cv_env_FFLAGS_set=${FFLAGS+set} +ac_cv_env_FFLAGS_value=$FFLAGS + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures curl - to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of curl -:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --disable-largefile omit support for large files + --enable-shared[=PKGS] + build shared libraries [default=yes] + --enable-static[=PKGS] + build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-http Enable HTTP support + --disable-http Disable HTTP support + --enable-ftp Enable FTP support + --disable-ftp Disable FTP support + --enable-gopher Enable GOPHER support + --disable-gopher Disable GOPHER support + --enable-file Enable FILE support + --disable-file Disable FILE support + --enable-ldap Enable LDAP support + --disable-ldap Disable LDAP support + --enable-dict Enable DICT support + --disable-dict Disable DICT support + --enable-telnet Enable TELNET support + --disable-telnet Disable TELNET support + --enable-manual Enable built-in manual + --disable-manual Disable built-in manual + --enable-libgcc use libgcc when linking + --enable-ipv6 Enable ipv6 (with ipv4) support + --disable-ipv6 Disable ipv6 support + --enable-nonblocking Enable detecting how to do it + --disable-nonblocking Disable non-blocking socket detection + --disable-thread don't look for thread-safe functions + --enable-thread look for thread-safe functions + --enable-ares=PATH Enable ares for name lookups + --disable-ares Disable ares for name lookups + --enable-debug Enable pedantic debug options + --disable-debug Disable debug options + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-pic try to use only PIC/non-PIC objects [default=use + both] + --with-tags[=TAGS] + include additional configurations [automatic] + --with-egd-socket=FILE Entropy Gathering Daemon socket pathname + --with-random=FILE read randomness from FILE (default=/dev/urandom) + --with-krb4-includes=DIR + Specify location of kerberos4 headers + --with-krb4-libs=DIR Specify location of kerberos4 libs + --with-krb4=DIR where to look for Kerberos4 + --with-spnego=DIR Specify location of SPNEGO library fbopenssl + --with-gssapi-includes=DIR + Specify location of GSSAPI header + --with-gssapi-libs=DIR Specify location of GSSAPI libs + --with-gssapi=DIR Where to look for GSSAPI + --with-ssl=PATH where to look for SSL, PATH points to the SSL + installation (default: /usr/local/ssl) + --without-ssl disable SSL + --with-ca-bundle=FILE File name to install the CA bundle as + --without-ca-bundle Don't install the CA bundle + --with-zlib=PATH search for zlib in PATH + --without-zlib disable use of zlib + --with-libidn=PATH Enable libidn usage + --without-libidn Disable libidn usage + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + F77 Fortran 77 compiler command + FFLAGS Fortran 77 compiler flags + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to http://curl.haxx.se/mail/>. +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +curl configure - +generated by GNU Autoconf 2.59 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. + +Copyright (c) 1998 - 2004 Daniel Stenberg, +This configure script may be copied, distributed and modified under the +terms of the curl license; see COPYING for more details +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by curl $as_me -, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers lib/config.h src/config.h" + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then + enableval="$enable_maintainer_mode" + USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6 + + +if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +# Extract the first word of "sed", so it can be a program name with args. +set dummy sed; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_SED+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $SED in + [\\/]* | ?:[\\/]*) + ac_cv_path_SED="$SED" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/bin:/usr/local/bin" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_SED" && ac_cv_path_SED="sed-was-not-found-by-configure" + ;; +esac +fi +SED=$ac_cv_path_SED + +if test -n "$SED"; then + echo "$as_me:$LINENO: result: $SED" >&5 +echo "${ECHO_T}$SED" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +if test "x$SED" = "xsed-was-not-found-by-configure"; then + { echo "$as_me:$LINENO: WARNING: sed was not found, this may ruin your chances to build fine" >&5 +echo "$as_me: WARNING: sed was not found, this may ruin your chances to build fine" >&2;} +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/bin:/usr/local/bin:/usr/ccs/bin" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + echo "$as_me:$LINENO: result: $AR" >&5 +echo "${ECHO_T}$AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/bin:/usr/local/bin:/usr/ccs/bin" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_AR" && ac_cv_prog_ac_ct_AR="ar-was-not-found-by-configure" +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + echo "$as_me:$LINENO: result: $ac_ct_AR" >&5 +echo "${ECHO_T}$ac_ct_AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + AR=$ac_ct_AR +else + AR="$ac_cv_prog_AR" +fi + + +if test "x$AR" = "xar-was-not-found-by-configure"; then + { echo "$as_me:$LINENO: WARNING: ar was not found, this may ruin your chances to build fine" >&5 +echo "$as_me: WARNING: ar was not found, this may ruin your chances to build fine" >&2;} +fi + +VERSION=`$SED -ne 's/^#define LIBCURL_VERSION "\(.*\)"/\1/p' ${srcdir}/include/curl/curlver.h` +am__api_version="1.8" +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # Keeping the `.' argument allows $(mkdir_p) to be used without + # argument. Indeed, we sometimes output rules like + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. + # (`test -n '$(somedir)' && $(mkdir_p) $(somedir)' is a more + # expensive solution, as it forces Make to start a sub-shell.) + mkdir_p='mkdir -p -- .' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE=curl + VERSION=$VERSION + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. + + + +echo "$as_me:$LINENO: checking curl version" >&5 +echo $ECHO_N "checking curl version... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $VERSION" >&5 +echo "${ECHO_T}$VERSION" >&6 + +VERSIONNUM=`$SED -ne 's/^#define LIBCURL_VERSION_NUM 0x\(.*\)/\1/p' ${srcdir}/include/curl/curlver.h` + + +PKGADD_PKG="HAXXcurl" +PKGADD_NAME="cURL - a client that groks URLs" +PKGADD_VENDOR="curl.haxx.se" + + + + + curl_ssl_msg="no (--with-ssl)" + curl_zlib_msg="no (--with-zlib)" + curl_krb4_msg="no (--with-krb4*)" + curl_gss_msg="no (--with-gssapi)" + curl_spnego_msg="no (--with-spnego)" + curl_ares_msg="no (--enable-ares)" + curl_ipv6_msg="no (--enable-ipv6)" + curl_idn_msg="no (--with-libidn)" + curl_manual_msg="no (--enable-manual)" + + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + + +cat >>confdefs.h <<_ACEOF +#define OS "${host}" +_ACEOF + + +DEPDIR="${am__leading_dot}deps" + + ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + + +echo "$as_me:$LINENO: checking for AIX" >&5 +echo $ECHO_N "checking for AIX... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef _AIX + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +cat >>confdefs.h <<\_ACEOF +#define _ALL_SOURCE 1 +_ACEOF + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +# Check whether --enable-largefile or --disable-largefile was given. +if test "${enable_largefile+set}" = set; then + enableval="$enable_largefile" + +fi; +if test "$enable_largefile" != no; then + + echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5 +echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_largefile_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + CC="$CC -n32" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_largefile_CC=' -n32'; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5 +echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6 + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_file_offset_bits+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_file_offset_bits=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_file_offset_bits=64; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5 +echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6 +if test "$ac_cv_sys_file_offset_bits" != no; then + +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF + +fi +rm -f conftest* + echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5 +echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6 +if test "${ac_cv_sys_large_files+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + while :; do + ac_cv_sys_large_files=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sys_large_files=1; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + break +done +fi +echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5 +echo "${ECHO_T}$ac_cv_sys_large_files" >&6 +if test "$ac_cv_sys_large_files" != no; then + +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF + +fi +rm -f conftest* +fi + + + + + +# Check whether --enable-shared or --disable-shared was given. +if test "${enable_shared+set}" = set; then + enableval="$enable_shared" + p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi; + +# Check whether --enable-static or --disable-static was given. +if test "${enable_static+set}" = set; then + enableval="$enable_static" + p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi; + +# Check whether --enable-fast-install or --disable-fast-install was given. +if test "${enable_fast_install+set}" = set; then + enableval="$enable_fast_install" + p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi; + +echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5 +echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6 +if test "${lt_cv_path_SED+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && break + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +SED=$lt_cv_path_SED + +fi + +echo "$as_me:$LINENO: result: $SED" >&5 +echo "${ECHO_T}$SED" >&6 + + +# Check whether --with-gnu-ld or --without-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then + withval="$with_gnu_ld" + test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi; +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + echo "$as_me:$LINENO: checking for ld used by $CC" >&5 +echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6 + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + echo "$as_me:$LINENO: checking for GNU ld" >&5 +echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking for non-GNU ld" >&5 +echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6 +fi +if test "${lt_cv_path_LD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +echo "${ECHO_T}$LD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 +echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} + { (exit 1); exit 1; }; } +echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 +echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6 +if test "${lt_cv_prog_gnu_ld+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 &5 +echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6 +with_gnu_ld=$lt_cv_prog_gnu_ld + + +echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5 +echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6 +if test "${lt_cv_ld_reload_flag+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_ld_reload_flag='-r' +fi +echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5 +echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6 +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' + +echo "$as_me:$LINENO: checking for BSD-compatible nm" >&5 +echo $ECHO_N "checking for BSD-compatible nm... $ECHO_C" >&6 +if test "${lt_cv_path_NM+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/${ac_tool_prefix}nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + esac + fi + done + IFS="$lt_save_ifs" + test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm +fi +fi +echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5 +echo "${ECHO_T}$lt_cv_path_NM" >&6 +NM="$lt_cv_path_NM" + +echo "$as_me:$LINENO: checking whether ln -s works" >&5 +echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +echo "${ECHO_T}no, using $LN_S" >&6 +fi + +echo "$as_me:$LINENO: checking how to recognise dependent libraries" >&5 +echo $ECHO_N "checking how to recognise dependent libraries... $ECHO_C" >&6 +if test "${lt_cv_deplibs_check_method+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix4* | aix5*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi4*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump'. + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | kfreebsd*-gnu) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case "$host_cpu" in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be Linux ELF. +linux*) + case $host_cpu in + alpha*|hppa*|i*86|ia64*|m68*|mips*|powerpc*|sparc*|s390*|sh*) + lt_cv_deplibs_check_method=pass_all ;; + *) + # glibc up to 2.1.1 does not perform some relocations on ARM + # this will be overridden with pass_all, but let us keep it just in case + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; + esac + lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so` + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +nto-qnx*) + lt_cv_deplibs_check_method=unknown + ;; + +openbsd*) + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object' + else + lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +sco3.2v5*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5 +echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6 +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Check whether --enable-libtool-lock or --disable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then + enableval="$enable_libtool_lock" + +fi; +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line 5370 "configure"' > conftest.$ac_ext + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + case "`/usr/bin/file conftest.o`" in + *32-bit*) + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5 +echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6 +if test "${lt_cv_cc_needs_belf+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + lt_cv_cc_needs_belf=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +lt_cv_cc_needs_belf=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5 +echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6 + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*-cygwin* | *-*-mingw* | *-*-pw32*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_DLLTOOL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + echo "$as_me:$LINENO: result: $DLLTOOL" >&5 +echo "${ECHO_T}$DLLTOOL" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_DLLTOOL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_DLLTOOL" && ac_cv_prog_ac_ct_DLLTOOL="false" +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + echo "$as_me:$LINENO: result: $ac_ct_DLLTOOL" >&5 +echo "${ECHO_T}$ac_ct_DLLTOOL" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + DLLTOOL=$ac_ct_DLLTOOL +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. +set dummy ${ac_tool_prefix}as; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AS+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AS"; then + ac_cv_prog_AS="$AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AS="${ac_tool_prefix}as" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AS=$ac_cv_prog_AS +if test -n "$AS"; then + echo "$as_me:$LINENO: result: $AS" >&5 +echo "${ECHO_T}$AS" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_AS"; then + ac_ct_AS=$AS + # Extract the first word of "as", so it can be a program name with args. +set dummy as; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_AS+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_AS"; then + ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AS="as" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_AS" && ac_cv_prog_ac_ct_AS="false" +fi +fi +ac_ct_AS=$ac_cv_prog_ac_ct_AS +if test -n "$ac_ct_AS"; then + echo "$as_me:$LINENO: result: $ac_ct_AS" >&5 +echo "${ECHO_T}$ac_ct_AS" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + AS=$ac_ct_AS +else + AS="$ac_cv_prog_AS" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_OBJDUMP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + echo "$as_me:$LINENO: result: $OBJDUMP" >&5 +echo "${ECHO_T}$OBJDUMP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_OBJDUMP" && ac_cv_prog_ac_ct_OBJDUMP="false" +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + echo "$as_me:$LINENO: result: $ac_ct_OBJDUMP" >&5 +echo "${ECHO_T}$ac_ct_OBJDUMP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + OBJDUMP=$ac_ct_OBJDUMP +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + + ;; + +esac + +need_locks="$enable_libtool_lock" + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in dlfcn.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cxx_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +depcc="$CXX" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # (even with -Werror). So we grep stderr for any message + # that says an option was ignored. + if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6 +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +ac_ext=f +ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' +ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_f77_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_F77+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$F77"; then + ac_cv_prog_F77="$F77" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_F77="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +F77=$ac_cv_prog_F77 +if test -n "$F77"; then + echo "$as_me:$LINENO: result: $F77" >&5 +echo "${ECHO_T}$F77" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$F77" && break + done +fi +if test -z "$F77"; then + ac_ct_F77=$F77 + for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_F77+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_F77"; then + ac_cv_prog_ac_ct_F77="$ac_ct_F77" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_F77="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_F77=$ac_cv_prog_ac_ct_F77 +if test -n "$ac_ct_F77"; then + echo "$as_me:$LINENO: result: $ac_ct_F77" >&5 +echo "${ECHO_T}$ac_ct_F77" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_F77" && break +done + + F77=$ac_ct_F77 +fi + + +# Provide some information about the compiler. +echo "$as_me:6944:" \ + "checking for Fortran 77 compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +rm -f a.out + +# If we don't use `.F' as extension, the preprocessor is not run on the +# input file. (Note that this only needs to work for GNU compilers.) +ac_save_ext=$ac_ext +ac_ext=F +echo "$as_me:$LINENO: checking whether we are using the GNU Fortran 77 compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU Fortran 77 compiler... $ECHO_C" >&6 +if test "${ac_cv_f77_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF + program main +#ifndef __GNUC__ + choke me +#endif + + end +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_f77_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_f77_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_f77_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_f77_compiler_gnu" >&6 +ac_ext=$ac_save_ext +ac_test_FFLAGS=${FFLAGS+set} +ac_save_FFLAGS=$FFLAGS +FFLAGS= +echo "$as_me:$LINENO: checking whether $F77 accepts -g" >&5 +echo $ECHO_N "checking whether $F77 accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_f77_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + FFLAGS=-g +cat >conftest.$ac_ext <<_ACEOF + program main + + end +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_f77_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_f77_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_f77_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_f77_g" >&5 +echo "${ECHO_T}$ac_cv_prog_f77_g" >&6 +if test "$ac_test_FFLAGS" = set; then + FFLAGS=$ac_save_FFLAGS +elif test $ac_cv_prog_f77_g = yes; then + if test "x$ac_cv_f77_compiler_gnu" = xyes; then + FFLAGS="-g -O2" + else + FFLAGS="-g" + fi +else + if test "x$ac_cv_f77_compiler_gnu" = xyes; then + FFLAGS="-O2" + else + FFLAGS= + fi +fi + +G77=`test $ac_compiler_gnu = yes && echo yes` +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! + +# find the maximum length of command line arguments +echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5 +echo $ECHO_N "checking the maximum length of command line arguments... $ECHO_C" >&6 +if test "${lt_cv_sys_max_cmd_len+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + *) + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while (test "X"`$CONFIG_SHELL $0 --fallback-echo "X$teststring" 2>/dev/null` \ + = "XX$teststring") >/dev/null 2>&1 && + new_result=`expr "X$teststring" : ".*" 2>&1` && + lt_cv_sys_max_cmd_len=$new_result && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + teststring= + # Add a significant safety factor because C++ compilers can tack on massive + # amounts of additional arguments before passing them to the linker. + # It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5 +echo "${ECHO_T}$lt_cv_sys_max_cmd_len" >&6 +else + echo "$as_me:$LINENO: result: none" >&5 +echo "${ECHO_T}none" >&6 +fi + + + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5 +echo $ECHO_N "checking command to parse $NM output from $compiler object... $ECHO_C" >&6 +if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Transform the above into a raw symbol and a C symbol. +symxfrm='\1 \2\3 \3' + +# Transform an extracted symbol line into a proper C declaration +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32*) + symcode='[ABCDGISTW]' + ;; +hpux*) # Its linker distinguishes data from code symbols + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris* | sysv5*) + symcode='[BDRT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Try without a prefix undercore, then with it. +for ac_symprfx in "" "_"; do + + # Write the raw and C identifiers. + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5 + (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if grep ' nm_test_var$' "$nlist" >/dev/null; then + if grep ' nm_test_func$' "$nlist" >/dev/null; then + cat < conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' + + cat <> conftest.$ac_ext +#if defined (__STDC__) && __STDC__ +# define lt_ptr_t void * +#else +# define lt_ptr_t char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr_t address; +} +lt_preloaded_symbols[] = +{ +EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext + cat <<\EOF >> conftest.$ac_ext + {0, (lt_ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -f conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + echo "$as_me:$LINENO: result: failed" >&5 +echo "${ECHO_T}failed" >&6 +else + echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 +fi + +echo "$as_me:$LINENO: checking for objdir" >&5 +echo $ECHO_N "checking for objdir... $ECHO_C" >&6 +if test "${lt_cv_objdir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5 +echo "${ECHO_T}$lt_cv_objdir" >&6 +objdir=$lt_cv_objdir + + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +# Constants: +rm="rm -f" + +# Global variables: +default_ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except M$VC, +# which needs '.lib'). +libext=a +ltmain="$ac_aux_dir/ltmain.sh" +ofile="$default_ofile" +with_gnu_ld="$lt_cv_prog_gnu_ld" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + echo "$as_me:$LINENO: result: $AR" >&5 +echo "${ECHO_T}$AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_AR" && ac_cv_prog_ac_ct_AR="false" +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + echo "$as_me:$LINENO: result: $ac_ct_AR" >&5 +echo "${ECHO_T}$ac_ct_AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + AR=$ac_ct_AR +else + AR="$ac_cv_prog_AR" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +test -z "$AS" && AS=as +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$DLLTOOL" && DLLTOOL=dlltool +test -z "$LD" && LD=ld +test -z "$LN_S" && LN_S="ln -s" +test -z "$MAGIC_CMD" && MAGIC_CMD=file +test -z "$NM" && NM=nm +test -z "$SED" && SED=sed +test -z "$OBJDUMP" && OBJDUMP=objdump +test -z "$RANLIB" && RANLIB=: +test -z "$STRIP" && STRIP=: +test -z "$ac_objext" && ac_objext=o + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds" + ;; + *) + old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + +# Only perform the check for file, if the check method requires it +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5 +echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6 +if test "${lt_cv_path_MAGIC_CMD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 +echo "${ECHO_T}$MAGIC_CMD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + echo "$as_me:$LINENO: checking for file" >&5 +echo $ECHO_N "checking for file... $ECHO_C" >&6 +if test "${lt_cv_path_MAGIC_CMD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`" + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 +echo "${ECHO_T}$MAGIC_CMD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +enable_dlopen=no +enable_win32_dll=yes + +# Check whether --enable-libtool-lock or --disable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then + enableval="$enable_libtool_lock" + +fi; +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + + +# Check whether --with-pic or --without-pic was given. +if test "${with_pic+set}" = set; then + withval="$with_pic" + pic_mode="$withval" +else + pic_mode=default +fi; +test -z "$pic_mode" && pic_mode=default + +# Use C for the default configuration in the libtool script +tagname= +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}\n' + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# +# Check for any special shared library compilation flags. +# +lt_prog_cc_shlib= +if test "$GCC" = no; then + case $host_os in + sco3.2v5*) + lt_prog_cc_shlib='-belf' + ;; + esac +fi +if test -n "$lt_prog_cc_shlib"; then + { echo "$as_me:$LINENO: WARNING: \`$CC' requires \`$lt_prog_cc_shlib' to build shared libraries" >&5 +echo "$as_me: WARNING: \`$CC' requires \`$lt_prog_cc_shlib' to build shared libraries" >&2;} + if echo "$old_CC $old_CFLAGS " | grep "[ ]$lt_prog_cc_shlib[ ]" >/dev/null; then : + else + { echo "$as_me:$LINENO: WARNING: add \`$lt_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&5 +echo "$as_me: WARNING: add \`$lt_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&2;} + lt_cv_prog_cc_can_build_shared=no + fi +fi + + +# +# Check to make sure the static flag actually works. +# +echo "$as_me:$LINENO: checking if $compiler static flag $lt_prog_compiler_static works" >&5 +echo $ECHO_N "checking if $compiler static flag $lt_prog_compiler_static works... $ECHO_C" >&6 +if test "${lt_prog_compiler_static_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_prog_compiler_static" + printf "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + else + lt_prog_compiler_static_works=yes + fi + fi + $rm conftest* + LDFLAGS="$save_LDFLAGS" + +fi +echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works" >&5 +echo "${ECHO_T}$lt_prog_compiler_static_works" >&6 + +if test x"$lt_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + lt_prog_compiler_no_builtin_flag=' -fno-builtin' + + +echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:7978: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:7982: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6 + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + +lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + +echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 +echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + linux*) + case $CC in + icc* | ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + esac + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + sco3.2v5*) + lt_prog_compiler_pic='-Kpic' + lt_prog_compiler_static='-dn' + ;; + + solaris*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic" >&6 + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + +echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic works... $ECHO_C" >&6 +if test "${lt_prog_compiler_pic_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8211: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:8215: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_prog_compiler_pic_works=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_works" >&6 + +if test x"$lt_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 +echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_c_o+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_c_o=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8271: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:8275: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files + $rm out/* && rmdir out + cd .. + rmdir conftest + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6 + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 +echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + echo "$as_me:$LINENO: result: $hard_links" >&5 +echo "${ECHO_T}$hard_links" >&6 + if test "$hard_links" = no; then + { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + +echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 + + runpath_var= + allow_undefined_flag= + enable_shared_with_static_runtimes=no + archive_cmds= + archive_expsym_cmds= + old_archive_From_new_cmds= + old_archive_from_expsyms_cmds= + export_dynamic_flag_spec= + whole_archive_flag_spec= + thread_safe_flag_spec= + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld= + hardcode_libdir_separator= + hardcode_direct=no + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + link_all_deplibs=unknown + hardcode_automatic=no + module_cmds= + module_expsym_cmds= + always_export_symbols=no + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms="_GLOBAL_OFFSET_TABLE_" + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + fi + ;; + + amigaos*) + archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can't use + # them. + ld_shlibs=no + ;; + + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_cmds="$tmp_archive_cmds" + supports_anon_versioning=no + case `$LD -v 2>/dev/null` in + *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + if test $supports_anon_versioning = yes; then + archive_expsym_cmds='$echo "{ global:" > $output_objdir/$libname.ver~ +cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ +$echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + else + archive_expsym_cmds="$tmp_archive_cmds" + fi + else + ld_shlibs=no + fi + ;; + + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = yes; then + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + + if test "$GCC" = yes; then + case $host_os in aix4.012|aix4.012.*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct=yes + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + always_export_symbols=yes + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec=' ' + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds it's shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no + ;; + + bsdi4*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_From_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes=yes + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes ; then + archive_cmds_need_lc=no + case "$host_os" in + rhapsody* | darwin1.[012]) + allow_undefined_flag='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + allow_undefined_flag='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + allow_undefined_flag='-flat_namespace -undefined suppress' + ;; + 10.*) + allow_undefined_flag='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_cmds='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + archive_cmds='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + module_cmds='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + whole_archive_flag_spec='-all_load $convenience' + link_all_deplibs=yes + else + ld_shlibs=no + fi + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + freebsd1*) + ld_shlibs=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | kfreebsd*-gnu) + archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10* | hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' + ;; + *) + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld='+b $libdir' + hardcode_libdir_separator=: + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + ia64*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=no + hardcode_shlibpath_var=no + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + *) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_ld='-rpath $libdir' + fi + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + link_all_deplibs=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + openbsd*) + hardcode_direct=yes + hardcode_shlibpath_var=no + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ + $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + + sco3.2v5*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + no_undefined_flag=' -z text' + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' + else + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4.2uw2*) + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=no + hardcode_shlibpath_var=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*) + no_undefined_flag='${wl}-z ${wl}text' + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv5*) + no_undefined_flag=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + hardcode_libdir_flag_spec= + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $ld_shlibs" >&5 +echo "${ECHO_T}$ld_shlibs" >&6 +test "$ld_shlibs" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 +echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 + (eval $archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + then + archive_cmds_need_lc=no + else + archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5 +echo "${ECHO_T}$archive_cmds_need_lc" >&6 + ;; + esac + fi + ;; +esac + +echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 +echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.01* | freebsdelf3.01*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +knetbsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=yes + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$as_me:$LINENO: result: $dynamic_linker" >&5 +echo "${ECHO_T}$dynamic_linker" >&6 +test "$dynamic_linker" = no && can_build_shared=no + +echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 +echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || \ + test -n "$runpath_var " || \ + test "X$hardcode_automatic"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +echo "$as_me:$LINENO: result: $hardcode_action" >&5 +echo "${ECHO_T}$hardcode_action" >&6 + +if test "$hardcode_action" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + +striplib= +old_striplib= +echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 +echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6 +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + esac +fi + +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + echo "$as_me:$LINENO: checking for shl_load" >&5 +echo $ECHO_N "checking for shl_load... $ECHO_C" >&6 +if test "${ac_cv_func_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define shl_load to an innocuous variant, in case declares shl_load. + For example, HP-UX 11i declares gettimeofday. */ +#define shl_load innocuous_shl_load + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shl_load (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef shl_load + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shl_load) || defined (__stub___shl_load) +choke me +#else +char (*f) () = shl_load; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != shl_load; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 +echo "${ECHO_T}$ac_cv_func_shl_load" >&6 +if test $ac_cv_func_shl_load = yes; then + lt_cv_dlopen="shl_load" +else + echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 +echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +int +main () +{ +shl_load (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 +if test $ac_cv_lib_dld_shl_load = yes; then + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld" +else + echo "$as_me:$LINENO: checking for dlopen" >&5 +echo $ECHO_N "checking for dlopen... $ECHO_C" >&6 +if test "${ac_cv_func_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define dlopen to an innocuous variant, in case declares dlopen. + For example, HP-UX 11i declares gettimeofday. */ +#define dlopen innocuous_dlopen + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char dlopen (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef dlopen + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_dlopen) || defined (__stub___dlopen) +choke me +#else +char (*f) () = dlopen; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != dlopen; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 +echo "${ECHO_T}$ac_cv_func_dlopen" >&6 +if test $ac_cv_func_dlopen = yes; then + lt_cv_dlopen="dlopen" +else + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 +echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6 +if test "${ac_cv_lib_svld_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_svld_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_svld_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6 +if test $ac_cv_lib_svld_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 +echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_dld_link+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dld_link (); +int +main () +{ +dld_link (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_dld_link=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_dld_link=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6 +if test $ac_cv_lib_dld_dld_link = yes; then + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5 +echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self" >&6 + + if test "x$lt_cv_dlopen_self" = xyes; then + LDFLAGS="$LDFLAGS $link_static_flag" + echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5 +echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self_static+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6 + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + +# Report which librarie types wil actually be built +echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 +echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $can_build_shared" >&5 +echo "${ECHO_T}$can_build_shared" >&6 + +echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 +echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6 +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + +aix4* | aix5*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + darwin* | rhapsody*) + if test "$GCC" = yes; then + archive_cmds_need_lc=no + case "$host_os" in + rhapsody* | darwin1.[012]) + allow_undefined_flag='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + allow_undefined_flag='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + allow_undefined_flag='-flat_namespace -undefined suppress' + ;; + 10.*) + allow_undefined_flag='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + output_verbose_link_cmd='echo' + archive_cmds='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring' + module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + whole_archive_flag_spec='-all_load $convenience' + link_all_deplibs=yes + else + ld_shlibs=no + fi + ;; +esac +echo "$as_me:$LINENO: result: $enable_shared" >&5 +echo "${ECHO_T}$enable_shared" >&6 + +echo "$as_me:$LINENO: checking whether to build static libraries" >&5 +echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6 +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes +echo "$as_me:$LINENO: result: $enable_static" >&5 +echo "${ECHO_T}$enable_static" >&6 + +# The else clause should only fire when bootstrapping the +# libtool distribution, otherwise you forgot to ship ltmain.sh +# with your package, and you will get complaints that there are +# no rules to generate ltmain.sh. +if test -f "$ltmain"; then + # See if we are running on zsh, and set the options which allow our commands through + # without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. + for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ + deplibs_check_method reload_flag reload_cmds need_locks \ + lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ + lt_cv_sys_global_symbol_to_c_name_address \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + old_postinstall_cmds old_postuninstall_cmds \ + compiler \ + CC \ + LD \ + lt_prog_compiler_wl \ + lt_prog_compiler_pic \ + lt_prog_compiler_static \ + lt_prog_compiler_no_builtin_flag \ + export_dynamic_flag_spec \ + thread_safe_flag_spec \ + whole_archive_flag_spec \ + enable_shared_with_static_runtimes \ + old_archive_cmds \ + old_archive_from_new_cmds \ + predep_objects \ + postdep_objects \ + predeps \ + postdeps \ + compiler_lib_search_path \ + archive_cmds \ + archive_expsym_cmds \ + postinstall_cmds \ + postuninstall_cmds \ + old_archive_from_expsyms_cmds \ + allow_undefined_flag \ + no_undefined_flag \ + export_symbols_cmds \ + hardcode_libdir_flag_spec \ + hardcode_libdir_flag_spec_ld \ + hardcode_libdir_separator \ + hardcode_automatic \ + module_cmds \ + module_expsym_cmds \ + lt_cv_prog_compiler_c_o \ + exclude_expsyms \ + include_expsyms; do + + case $var in + old_archive_cmds | \ + old_archive_from_new_cmds | \ + archive_cmds | \ + archive_expsym_cmds | \ + module_cmds | \ + module_expsym_cmds | \ + old_archive_from_expsyms_cmds | \ + export_symbols_cmds | \ + extract_expsyms_cmds | reload_cmds | finish_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case $lt_echo in + *'\$0 --fallback-echo"') + lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + +cfgfile="${ofile}T" + trap "$rm \"$cfgfile\"; exit 1" 1 2 15 + $rm -f "$cfgfile" + { echo "$as_me:$LINENO: creating $ofile" >&5 +echo "$as_me: creating $ofile" >&6;} + + cat <<__EOF__ >> "$cfgfile" +#! $SHELL + +# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# +# This file is part of GNU Libtool: +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can 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. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="$SED -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +# The names of the tagged configurations supported by this script. +available_tags= + +# ### BEGIN LIBTOOL CONFIG + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU C compiler? +with_gcc=$GCC + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_LD + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_thread_safe_flag_spec + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_old_archive_cmds +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build and install a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_predep_objects + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_postdep_objects + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_predeps + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$hardcode_automatic + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# ### END LIBTOOL CONFIG + +__EOF__ + + + case $host_os in + aix3*) + cat <<\EOF >> "$cfgfile" + +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +EOF + ;; + esac + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || \ + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + +# Check whether --with-tags or --without-tags was given. +if test "${with_tags+set}" = set; then + withval="$with_tags" + tagnames="$withval" +fi; + +if test -f "$ltmain" && test -n "$tagnames"; then + if test ! -f "${ofile}"; then + { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not exist" >&5 +echo "$as_me: WARNING: output file \`$ofile' does not exist" >&2;} + fi + + if test -z "$LTCC"; then + eval "`$SHELL ${ofile} --config | grep '^LTCC='`" + if test -z "$LTCC"; then + { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not look like a libtool script" >&5 +echo "$as_me: WARNING: output file \`$ofile' does not look like a libtool script" >&2;} + else + { echo "$as_me:$LINENO: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&5 +echo "$as_me: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&2;} + fi + fi + + # Extract list of available tagged configurations in $ofile. + # Note that this assumes the entire list is on one line. + available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` + + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for tagname in $tagnames; do + IFS="$lt_save_ifs" + # Check whether tagname contains only valid characters + case `$echo "X$tagname" | $Xsed -e 's:[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]::g'` in + "") ;; + *) { { echo "$as_me:$LINENO: error: invalid tag name: $tagname" >&5 +echo "$as_me: error: invalid tag name: $tagname" >&2;} + { (exit 1); exit 1; }; } + ;; + esac + + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null + then + { { echo "$as_me:$LINENO: error: tag name \"$tagname\" already exists" >&5 +echo "$as_me: error: tag name \"$tagname\" already exists" >&2;} + { (exit 1); exit 1; }; } + fi + + # Update the list of available tags. + if test -n "$tagname"; then + echo appending configuration tag \"$tagname\" to $ofile + + case $tagname in + CXX) + if test -n "$CXX" && test "X$CXX" != "Xno"; then + ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_flag_spec_ld_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_automatic_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +# Source file extension for C++ test sources. +ac_ext=cc + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(int, char *) { return(0); }\n' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_LD=$LD +lt_save_GCC=$GCC +GCC=$GXX +lt_save_with_gnu_ld=$with_gnu_ld +lt_save_path_LD=$lt_cv_path_LD +if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx +else + unset lt_cv_prog_gnu_ld +fi +if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX +else + unset lt_cv_path_LD +fi +test -z "${LDCXX+set}" || LD=$LDCXX +CC=${CXX-"c++"} +compiler=$CC +compiler_CXX=$CC +cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'` + +# We don't want -fno-exception wen compiling C++ code, so set the +# no_builtin_flag separately +if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' +else + lt_prog_compiler_no_builtin_flag_CXX= +fi + +if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + +# Check whether --with-gnu-ld or --without-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then + withval="$with_gnu_ld" + test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi; +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + echo "$as_me:$LINENO: checking for ld used by $CC" >&5 +echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6 + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + echo "$as_me:$LINENO: checking for GNU ld" >&5 +echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6 +else + echo "$as_me:$LINENO: checking for non-GNU ld" >&5 +echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6 +fi +if test "${lt_cv_path_LD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +echo "${ECHO_T}$LD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 +echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} + { (exit 1); exit 1; }; } +echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 +echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6 +if test "${lt_cv_prog_gnu_ld+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 &5 +echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6 +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ + grep 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + +else + GXX=no + with_gnu_ld=no + wlarc= +fi + +# PORTME: fill in a description of your system's C++ link characteristics +echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 +ld_shlibs_CXX=yes +case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + + if test "$GXX" = yes; then + case $host_os in aix4.012|aix4.012.*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct_CXX=yes + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + always_export_symbols_CXX=yes + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX=' ' + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds it's shared libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs_CXX=no + fi + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes; then + archive_cmds_need_lc_CXX=no + case "$host_os" in + rhapsody* | darwin1.[012]) + allow_undefined_flag_CXX='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + allow_undefined_flag_CXX='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + allow_undefined_flag_CXX='-flat_namespace -undefined suppress' + ;; + 10.*) + allow_undefined_flag_CXX='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_cmds_CXX='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + archive_cmds_CXX='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + module_cmds_CXX='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + whole_archive_flag_spec_CXX='-all_load $convenience' + link_all_deplibs_CXX=yes + else + ld_shlibs_CXX=no + fi + ;; + + dgux*) + case $cc_basename in + ec++) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + freebsd12*) + # C++ shared libraries reported to be fairly broken before switch to ELF + ld_shlibs_CXX=no + ;; + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + gnu*) + ;; + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC) + archive_cmds_CXX='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + case "$host_cpu" in + hppa*64*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld_CXX='+b $libdir' + hardcode_libdir_separator_CXX=: + ;; + ia64*) + hardcode_libdir_flag_spec_CXX='-L$libdir' + ;; + *) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case "$host_cpu" in + hppa*64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + *) + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC) + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds_CXX='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case "$host_cpu" in + ia64*|hppa*64*) + archive_cmds_CXX='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + irix5* | irix6*) + case $cc_basename in + CC) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + ;; + linux*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + hardcode_libdir_flag_spec_CXX='${wl}--rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc) + # Intel C++ + with_gnu_ld=yes + archive_cmds_need_lc_CXX=no + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + cxx) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + esac + ;; + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + mvs*) + case $cc_basename in + cxx) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + osf3*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + osf4* | osf5*) + case $cc_basename in + KCC) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' + ;; + RCC) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry $objdir/so_locations -o $lib~ + $rm $lib.exp' + + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + sco*) + archive_cmds_need_lc_CXX=no + case $cc_basename in + CC) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.0-5 | solaris2.0-5.*) ;; + *) + # The C++ compiler is used as linker so we must use $wl + # flag to pass the commands to the underlying system + # linker. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[LR]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | grep -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + fi + ;; + esac + ;; + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*) + archive_cmds_need_lc_CXX=no + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; +esac +echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 +echo "${ECHO_T}$ld_shlibs_CXX" >&6 +test "$ld_shlibs_CXX" = no && can_build_shared=no + +GCC_CXX="$GXX" +LD_CXX="$LD" + + +cat > conftest.$ac_ext <&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + # The `*' in the case matches for architectures that use `case' in + # $output_verbose_cmd can trigger glob expansion during the loop + # eval without this substitution. + output_verbose_link_cmd="`$echo \"X$output_verbose_link_cmd\" | $Xsed -e \"$no_glob_subst\"`" + + for p in `eval $output_verbose_link_cmd`; do + case $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" \ + || test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p in + -L* | -R*) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + ;; + + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$rm -f confest.$objext + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + +lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + +echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 +echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | os2* | pw32*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix4* | aix5*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68) + # Green Hills C++ Compiler + # _LT_AC_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + dgux*) + case $cc_basename in + ec++) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | kfreebsd*-gnu) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive" + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux*) + case $cc_basename in + KCC) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + icpc) + # Intel C++ + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + cxx) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd*) + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + sco*) + case $cc_basename in + CC) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + *) + ;; + esac + ;; + solaris*) + case $cc_basename in + CC) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + unixware*) + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_CXX" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_CXX" >&6 + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + +echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... $ECHO_C" >&6 +if test "${lt_prog_compiler_pic_works_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:12732: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:12736: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_prog_compiler_pic_works_CXX=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_CXX" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_works_CXX" >&6 + +if test x"$lt_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 +echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:12792: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:12796: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files + $rm out/* && rmdir out + cd .. + rmdir conftest + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_c_o_CXX" >&6 + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 +echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + echo "$as_me:$LINENO: result: $hard_links" >&5 +echo "${ECHO_T}$hard_links" >&6 + if test "$hard_links" = no; then + { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + +echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix4* | aix5*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw*) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 +echo "${ECHO_T}$ld_shlibs_CXX" >&6 +test "$ld_shlibs_CXX" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 +echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { (eval echo "$as_me:$LINENO: \"$archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 + (eval $archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + then + archive_cmds_need_lc_CXX=no + else + archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + echo "$as_me:$LINENO: result: $archive_cmds_need_lc_CXX" >&5 +echo "${ECHO_T}$archive_cmds_need_lc_CXX" >&6 + ;; + esac + fi + ;; +esac + +echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 +echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.01* | freebsdelf3.01*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +knetbsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=yes + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$as_me:$LINENO: result: $dynamic_linker" >&5 +echo "${ECHO_T}$dynamic_linker" >&6 +test "$dynamic_linker" = no && can_build_shared=no + +echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 +echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || \ + test -n "$runpath_var CXX" || \ + test "X$hardcode_automatic_CXX"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct_CXX" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +echo "$as_me:$LINENO: result: $hardcode_action_CXX" >&5 +echo "${ECHO_T}$hardcode_action_CXX" >&6 + +if test "$hardcode_action_CXX" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + +striplib= +old_striplib= +echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 +echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6 +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + esac +fi + +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + echo "$as_me:$LINENO: checking for shl_load" >&5 +echo $ECHO_N "checking for shl_load... $ECHO_C" >&6 +if test "${ac_cv_func_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define shl_load to an innocuous variant, in case declares shl_load. + For example, HP-UX 11i declares gettimeofday. */ +#define shl_load innocuous_shl_load + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shl_load (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef shl_load + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shl_load) || defined (__stub___shl_load) +choke me +#else +char (*f) () = shl_load; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != shl_load; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 +echo "${ECHO_T}$ac_cv_func_shl_load" >&6 +if test $ac_cv_func_shl_load = yes; then + lt_cv_dlopen="shl_load" +else + echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 +echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +int +main () +{ +shl_load (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 +if test $ac_cv_lib_dld_shl_load = yes; then + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld" +else + echo "$as_me:$LINENO: checking for dlopen" >&5 +echo $ECHO_N "checking for dlopen... $ECHO_C" >&6 +if test "${ac_cv_func_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define dlopen to an innocuous variant, in case declares dlopen. + For example, HP-UX 11i declares gettimeofday. */ +#define dlopen innocuous_dlopen + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char dlopen (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef dlopen + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_dlopen) || defined (__stub___dlopen) +choke me +#else +char (*f) () = dlopen; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != dlopen; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 +echo "${ECHO_T}$ac_cv_func_dlopen" >&6 +if test $ac_cv_func_dlopen = yes; then + lt_cv_dlopen="dlopen" +else + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 +echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6 +if test "${ac_cv_lib_svld_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_svld_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_svld_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6 +if test $ac_cv_lib_svld_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 +echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_dld_link+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dld_link (); +int +main () +{ +dld_link (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_dld_link=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_dld_link=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6 +if test $ac_cv_lib_dld_dld_link = yes; then + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5 +echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self" >&6 + + if test "x$lt_cv_dlopen_self" = xyes; then + LDFLAGS="$LDFLAGS $link_static_flag" + echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5 +echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self_static+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6 + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + +# The else clause should only fire when bootstrapping the +# libtool distribution, otherwise you forgot to ship ltmain.sh +# with your package, and you will get complaints that there are +# no rules to generate ltmain.sh. +if test -f "$ltmain"; then + # See if we are running on zsh, and set the options which allow our commands through + # without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. + for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ + deplibs_check_method reload_flag reload_cmds need_locks \ + lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ + lt_cv_sys_global_symbol_to_c_name_address \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + old_postinstall_cmds old_postuninstall_cmds \ + compiler_CXX \ + CC_CXX \ + LD_CXX \ + lt_prog_compiler_wl_CXX \ + lt_prog_compiler_pic_CXX \ + lt_prog_compiler_static_CXX \ + lt_prog_compiler_no_builtin_flag_CXX \ + export_dynamic_flag_spec_CXX \ + thread_safe_flag_spec_CXX \ + whole_archive_flag_spec_CXX \ + enable_shared_with_static_runtimes_CXX \ + old_archive_cmds_CXX \ + old_archive_from_new_cmds_CXX \ + predep_objects_CXX \ + postdep_objects_CXX \ + predeps_CXX \ + postdeps_CXX \ + compiler_lib_search_path_CXX \ + archive_cmds_CXX \ + archive_expsym_cmds_CXX \ + postinstall_cmds_CXX \ + postuninstall_cmds_CXX \ + old_archive_from_expsyms_cmds_CXX \ + allow_undefined_flag_CXX \ + no_undefined_flag_CXX \ + export_symbols_cmds_CXX \ + hardcode_libdir_flag_spec_CXX \ + hardcode_libdir_flag_spec_ld_CXX \ + hardcode_libdir_separator_CXX \ + hardcode_automatic_CXX \ + module_cmds_CXX \ + module_expsym_cmds_CXX \ + lt_cv_prog_compiler_c_o_CXX \ + exclude_expsyms_CXX \ + include_expsyms_CXX; do + + case $var in + old_archive_cmds_CXX | \ + old_archive_from_new_cmds_CXX | \ + archive_cmds_CXX | \ + archive_expsym_cmds_CXX | \ + module_cmds_CXX | \ + module_expsym_cmds_CXX | \ + old_archive_from_expsyms_cmds_CXX | \ + export_symbols_cmds_CXX | \ + extract_expsyms_cmds | reload_cmds | finish_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case $lt_echo in + *'\$0 --fallback-echo"') + lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + +cfgfile="$ofile" + + cat <<__EOF__ >> "$cfgfile" +# ### BEGIN LIBTOOL TAG CONFIG: $tagname + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU C compiler? +with_gcc=$GCC_CXX + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_thread_safe_flag_spec_CXX + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_old_archive_cmds_CXX +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build and install a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_predep_objects_CXX + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_postdep_objects_CXX + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_predeps_CXX + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path_CXX" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# ### END LIBTOOL TAG CONFIG: $tagname + +__EOF__ + + +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC=$lt_save_CC +LDCXX=$LD +LD=$lt_save_LD +GCC=$lt_save_GCC +with_gnu_ldcxx=$with_gnu_ld +with_gnu_ld=$lt_save_with_gnu_ld +lt_cv_path_LDCXX=$lt_cv_path_LD +lt_cv_path_LD=$lt_save_path_LD +lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld +lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld + + else + tagname="" + fi + ;; + + F77) + if test -n "$F77" && test "X$F77" != "Xno"; then + +ac_ext=f +ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' +ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_f77_compiler_gnu + + +archive_cmds_need_lc_F77=no +allow_undefined_flag_F77= +always_export_symbols_F77=no +archive_expsym_cmds_F77= +export_dynamic_flag_spec_F77= +hardcode_direct_F77=no +hardcode_libdir_flag_spec_F77= +hardcode_libdir_flag_spec_ld_F77= +hardcode_libdir_separator_F77= +hardcode_minus_L_F77=no +hardcode_automatic_F77=no +module_cmds_F77= +module_expsym_cmds_F77= +link_all_deplibs_F77=unknown +old_archive_cmds_F77=$old_archive_cmds +no_undefined_flag_F77= +whole_archive_flag_spec_F77= +enable_shared_with_static_runtimes_F77=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +objext_F77=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code=" subroutine t\n return\n end\n" + +# Code to be used in simple link tests +lt_simple_link_test_code=" program t\n end\n" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +CC=${F77-"f77"} +compiler=$CC +compiler_F77=$CC +cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'` + +echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 +echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $can_build_shared" >&5 +echo "${ECHO_T}$can_build_shared" >&6 + +echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 +echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6 +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; +aix4* | aix5*) + test "$enable_shared" = yes && enable_static=no + ;; +esac +echo "$as_me:$LINENO: result: $enable_shared" >&5 +echo "${ECHO_T}$enable_shared" >&6 + +echo "$as_me:$LINENO: checking whether to build static libraries" >&5 +echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6 +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes +echo "$as_me:$LINENO: result: $enable_static" >&5 +echo "${ECHO_T}$enable_static" >&6 + +test "$ld_shlibs_F77" = no && can_build_shared=no + +GCC_F77="$G77" +LD_F77="$LD" + +lt_prog_compiler_wl_F77= +lt_prog_compiler_pic_F77= +lt_prog_compiler_static_F77= + +echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 +echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 + + if test "$GCC" = yes; then + lt_prog_compiler_wl_F77='-Wl,' + lt_prog_compiler_static_F77='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_F77='-Bstatic' + fi + ;; + + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_F77='-m68020 -resident32 -malways-restore-a4' + ;; + + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_F77='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_F77='-fno-common' + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared_F77=no + enable_shared=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_F77=-Kconform_pic + fi + ;; + + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_F77='-fPIC' + ;; + esac + ;; + + *) + lt_prog_compiler_pic_F77='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl_F77='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_F77='-Bstatic' + else + lt_prog_compiler_static_F77='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_F77='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl_F77='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_F77='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static_F77='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl_F77='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static_F77='-non_shared' + ;; + + newsos6) + lt_prog_compiler_pic_F77='-KPIC' + lt_prog_compiler_static_F77='-Bstatic' + ;; + + linux*) + case $CC in + icc* | ecc*) + lt_prog_compiler_wl_F77='-Wl,' + lt_prog_compiler_pic_F77='-KPIC' + lt_prog_compiler_static_F77='-static' + ;; + ccc*) + lt_prog_compiler_wl_F77='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static_F77='-non_shared' + ;; + esac + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl_F77='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static_F77='-non_shared' + ;; + + sco3.2v5*) + lt_prog_compiler_pic_F77='-Kpic' + lt_prog_compiler_static_F77='-dn' + ;; + + solaris*) + lt_prog_compiler_wl_F77='-Wl,' + lt_prog_compiler_pic_F77='-KPIC' + lt_prog_compiler_static_F77='-Bstatic' + ;; + + sunos4*) + lt_prog_compiler_wl_F77='-Qoption ld ' + lt_prog_compiler_pic_F77='-PIC' + lt_prog_compiler_static_F77='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + lt_prog_compiler_wl_F77='-Wl,' + lt_prog_compiler_pic_F77='-KPIC' + lt_prog_compiler_static_F77='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic_F77='-Kconform_pic' + lt_prog_compiler_static_F77='-Bstatic' + fi + ;; + + uts4*) + lt_prog_compiler_pic_F77='-pic' + lt_prog_compiler_static_F77='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared_F77=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_F77" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_F77" >&6 + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_F77"; then + +echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works" >&5 +echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works... $ECHO_C" >&6 +if test "${lt_prog_compiler_pic_works_F77+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_prog_compiler_pic_works_F77=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_F77" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:15078: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:15082: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_prog_compiler_pic_works_F77=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_F77" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_works_F77" >&6 + +if test x"$lt_prog_compiler_pic_works_F77" = xyes; then + case $lt_prog_compiler_pic_F77 in + "" | " "*) ;; + *) lt_prog_compiler_pic_F77=" $lt_prog_compiler_pic_F77" ;; + esac +else + lt_prog_compiler_pic_F77= + lt_prog_compiler_can_build_shared_F77=no +fi + +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_F77= + ;; + *) + lt_prog_compiler_pic_F77="$lt_prog_compiler_pic_F77" + ;; +esac + +echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 +echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_c_o_F77+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_c_o_F77=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:15138: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:15142: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + lt_cv_prog_compiler_c_o_F77=yes + fi + fi + chmod u+w . + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files + $rm out/* && rmdir out + cd .. + rmdir conftest + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_F77" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_c_o_F77" >&6 + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_F77" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 +echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + echo "$as_me:$LINENO: result: $hard_links" >&5 +echo "${ECHO_T}$hard_links" >&6 + if test "$hard_links" = no; then + { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + +echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 + + runpath_var= + allow_undefined_flag_F77= + enable_shared_with_static_runtimes_F77=no + archive_cmds_F77= + archive_expsym_cmds_F77= + old_archive_From_new_cmds_F77= + old_archive_from_expsyms_cmds_F77= + export_dynamic_flag_spec_F77= + whole_archive_flag_spec_F77= + thread_safe_flag_spec_F77= + hardcode_libdir_flag_spec_F77= + hardcode_libdir_flag_spec_ld_F77= + hardcode_libdir_separator_F77= + hardcode_direct_F77=no + hardcode_minus_L_F77=no + hardcode_shlibpath_var_F77=unsupported + link_all_deplibs_F77=unknown + hardcode_automatic_F77=no + module_cmds_F77= + module_expsym_cmds_F77= + always_export_symbols_F77=no + export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms_F77= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms_F77="_GLOBAL_OFFSET_TABLE_" + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs_F77=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs_F77=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + fi + ;; + + amigaos*) + archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_minus_L_F77=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can't use + # them. + ld_shlibs_F77=no + ;; + + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_F77=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_F77='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_F77=no + fi + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, F77) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_F77='-L$libdir' + allow_undefined_flag_F77=unsupported + always_export_symbols_F77=no + enable_shared_with_static_runtimes_F77=yes + export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_F77='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds_F77='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs_F77=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs_F77=no + fi + ;; + + sunos4*) + archive_cmds_F77='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_cmds_F77="$tmp_archive_cmds" + supports_anon_versioning=no + case `$LD -v 2>/dev/null` in + *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + if test $supports_anon_versioning = yes; then + archive_expsym_cmds_F77='$echo "{ global:" > $output_objdir/$libname.ver~ +cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ +$echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + else + archive_expsym_cmds_F77="$tmp_archive_cmds" + fi + else + ld_shlibs_F77=no + fi + ;; + + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs_F77=no + fi + ;; + esac + + if test "$ld_shlibs_F77" = yes; then + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_F77='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_F77='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_F77="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_F77= + fi + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag_F77=unsupported + always_export_symbols_F77=yes + archive_expsym_cmds_F77='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L_F77=yes + if test "$GCC" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct_F77=unsupported + fi + ;; + + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + export_symbols_cmds_F77='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_F77='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_F77='' + hardcode_direct_F77=yes + hardcode_libdir_separator_F77=':' + link_all_deplibs_F77=yes + + if test "$GCC" = yes; then + case $host_os in aix4.012|aix4.012.*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct_F77=yes + else + # We have old collect2 + hardcode_direct_F77=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_F77=yes + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_libdir_separator_F77= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols_F77=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_F77='-berok' + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF + program main + + end +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_f77_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds_F77="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_F77='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_F77="-z nodefs" + archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF + program main + + end +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_f77_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_F77=' ${wl}-bernotok' + allow_undefined_flag_F77=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + always_export_symbols_F77=yes + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_F77=' ' + archive_cmds_need_lc_F77=yes + # This is similar to how AIX traditionally builds it's shared libraries. + archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_minus_L_F77=yes + # see comment about different semantics on the GNU ld section + ld_shlibs_F77=no + ;; + + bsdi4*) + export_dynamic_flag_spec_F77=-rdynamic + ;; + + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_F77=' ' + allow_undefined_flag_F77=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_F77='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_From_new_cmds_F77='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds_F77='lib /OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes_F77=yes + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes ; then + archive_cmds_need_lc_F77=no + case "$host_os" in + rhapsody* | darwin1.[012]) + allow_undefined_flag_F77='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + allow_undefined_flag_F77='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + allow_undefined_flag_F77='-flat_namespace -undefined suppress' + ;; + 10.*) + allow_undefined_flag_F77='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_cmds_F77='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + archive_cmds_F77='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + module_cmds_F77='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + hardcode_direct_F77=no + hardcode_automatic_F77=yes + hardcode_shlibpath_var_F77=unsupported + whole_archive_flag_spec_F77='-all_load $convenience' + link_all_deplibs_F77=yes + else + ld_shlibs_F77=no + fi + ;; + + dgux*) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_shlibpath_var_F77=no + ;; + + freebsd1*) + ld_shlibs_F77=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec_F77='-R$libdir' + hardcode_direct_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_F77=yes + hardcode_minus_L_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | kfreebsd*-gnu) + archive_cmds_F77='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_F77='-R$libdir' + hardcode_direct_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds_F77='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds_F77='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_F77=: + hardcode_direct_F77=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_F77=yes + export_dynamic_flag_spec_F77='${wl}-E' + ;; + + hpux10* | hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds_F77='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' + ;; + *) + archive_cmds_F77='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*) + hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld_F77='+b $libdir' + hardcode_libdir_separator_F77=: + hardcode_direct_F77=no + hardcode_shlibpath_var_F77=no + ;; + ia64*) + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_direct_F77=no + hardcode_shlibpath_var_F77=no + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_F77=yes + ;; + *) + hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_F77=: + hardcode_direct_F77=yes + export_dynamic_flag_spec_F77='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_F77=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_F77='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_ld_F77='-rpath $libdir' + fi + hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_F77=: + link_all_deplibs_F77=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds_F77='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec_F77='-R$libdir' + hardcode_direct_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + newsos6) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_F77=yes + hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_F77=: + hardcode_shlibpath_var_F77=no + ;; + + openbsd*) + hardcode_direct_F77=yes + hardcode_shlibpath_var_F77=no + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' + export_dynamic_flag_spec_F77='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_F77='-R$libdir' + ;; + *) + archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + + os2*) + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_minus_L_F77=yes + allow_undefined_flag_F77=unsupported + archive_cmds_F77='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_From_new_cmds_F77='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag_F77=' -expect_unresolved \*' + archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_F77=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag_F77=' -expect_unresolved \*' + archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_F77='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ + $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec_F77='-rpath $libdir' + fi + hardcode_libdir_separator_F77=: + ;; + + sco3.2v5*) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_F77=no + export_dynamic_flag_spec_F77='${wl}-Bexport' + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + no_undefined_flag_F77=' -z text' + if test "$GCC" = yes; then + archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' + else + archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + fi + hardcode_libdir_flag_spec_F77='-R$libdir' + hardcode_shlibpath_var_F77=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_F77='-z allextract$convenience -z defaultextract' ;; + esac + link_all_deplibs_F77=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds_F77='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds_F77='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_direct_F77=yes + hardcode_minus_L_F77=yes + hardcode_shlibpath_var_F77=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_F77=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds_F77='$CC -r -o $output$reload_objs' + hardcode_direct_F77=no + ;; + motorola) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_F77=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var_F77=no + ;; + + sysv4.3*) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_F77=no + export_dynamic_flag_spec_F77='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_F77=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs_F77=yes + fi + ;; + + sysv4.2uw2*) + archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_F77=yes + hardcode_minus_L_F77=no + hardcode_shlibpath_var_F77=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*) + no_undefined_flag_F77='${wl}-z ${wl}text' + if test "$GCC" = yes; then + archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds_F77='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var_F77=no + ;; + + sysv5*) + no_undefined_flag_F77=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + hardcode_libdir_flag_spec_F77= + hardcode_shlibpath_var_F77=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_F77='-L$libdir' + hardcode_shlibpath_var_F77=no + ;; + + *) + ld_shlibs_F77=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $ld_shlibs_F77" >&5 +echo "${ECHO_T}$ld_shlibs_F77" >&6 +test "$ld_shlibs_F77" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_F77" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_F77=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_F77 in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 +echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_F77 + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_F77 + allow_undefined_flag_F77= + if { (eval echo "$as_me:$LINENO: \"$archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 + (eval $archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + then + archive_cmds_need_lc_F77=no + else + archive_cmds_need_lc_F77=yes + fi + allow_undefined_flag_F77=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + echo "$as_me:$LINENO: result: $archive_cmds_need_lc_F77" >&5 +echo "${ECHO_T}$archive_cmds_need_lc_F77" >&6 + ;; + esac + fi + ;; +esac + +echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 +echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.01* | freebsdelf3.01*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +knetbsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=yes + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$as_me:$LINENO: result: $dynamic_linker" >&5 +echo "${ECHO_T}$dynamic_linker" >&6 +test "$dynamic_linker" = no && can_build_shared=no + +echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 +echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 +hardcode_action_F77= +if test -n "$hardcode_libdir_flag_spec_F77" || \ + test -n "$runpath_var F77" || \ + test "X$hardcode_automatic_F77"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct_F77" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, F77)" != no && + test "$hardcode_minus_L_F77" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_F77=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_F77=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_F77=unsupported +fi +echo "$as_me:$LINENO: result: $hardcode_action_F77" >&5 +echo "${ECHO_T}$hardcode_action_F77" >&6 + +if test "$hardcode_action_F77" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + +striplib= +old_striplib= +echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 +echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6 +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + esac +fi + + + +# The else clause should only fire when bootstrapping the +# libtool distribution, otherwise you forgot to ship ltmain.sh +# with your package, and you will get complaints that there are +# no rules to generate ltmain.sh. +if test -f "$ltmain"; then + # See if we are running on zsh, and set the options which allow our commands through + # without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. + for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ + deplibs_check_method reload_flag reload_cmds need_locks \ + lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ + lt_cv_sys_global_symbol_to_c_name_address \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + old_postinstall_cmds old_postuninstall_cmds \ + compiler_F77 \ + CC_F77 \ + LD_F77 \ + lt_prog_compiler_wl_F77 \ + lt_prog_compiler_pic_F77 \ + lt_prog_compiler_static_F77 \ + lt_prog_compiler_no_builtin_flag_F77 \ + export_dynamic_flag_spec_F77 \ + thread_safe_flag_spec_F77 \ + whole_archive_flag_spec_F77 \ + enable_shared_with_static_runtimes_F77 \ + old_archive_cmds_F77 \ + old_archive_from_new_cmds_F77 \ + predep_objects_F77 \ + postdep_objects_F77 \ + predeps_F77 \ + postdeps_F77 \ + compiler_lib_search_path_F77 \ + archive_cmds_F77 \ + archive_expsym_cmds_F77 \ + postinstall_cmds_F77 \ + postuninstall_cmds_F77 \ + old_archive_from_expsyms_cmds_F77 \ + allow_undefined_flag_F77 \ + no_undefined_flag_F77 \ + export_symbols_cmds_F77 \ + hardcode_libdir_flag_spec_F77 \ + hardcode_libdir_flag_spec_ld_F77 \ + hardcode_libdir_separator_F77 \ + hardcode_automatic_F77 \ + module_cmds_F77 \ + module_expsym_cmds_F77 \ + lt_cv_prog_compiler_c_o_F77 \ + exclude_expsyms_F77 \ + include_expsyms_F77; do + + case $var in + old_archive_cmds_F77 | \ + old_archive_from_new_cmds_F77 | \ + archive_cmds_F77 | \ + archive_expsym_cmds_F77 | \ + module_cmds_F77 | \ + module_expsym_cmds_F77 | \ + old_archive_from_expsyms_cmds_F77 | \ + export_symbols_cmds_F77 | \ + extract_expsyms_cmds | reload_cmds | finish_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case $lt_echo in + *'\$0 --fallback-echo"') + lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + +cfgfile="$ofile" + + cat <<__EOF__ >> "$cfgfile" +# ### BEGIN LIBTOOL TAG CONFIG: $tagname + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_F77 + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_F77 + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_compiler_F77 + +# Is the compiler the GNU C compiler? +with_gcc=$GCC_F77 + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_LD_F77 + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_F77 + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_F77 +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_F77 + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_F77 + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_F77 + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_F77 + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_F77 + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_thread_safe_flag_spec_F77 + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_old_archive_cmds_F77 +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_F77 + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_F77 + +# Commands used to build and install a shared archive. +archive_cmds=$lt_archive_cmds_F77 +archive_expsym_cmds=$lt_archive_expsym_cmds_F77 +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_module_cmds_F77 +module_expsym_cmds=$lt_module_expsym_cmds_F77 + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_predep_objects_F77 + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_postdep_objects_F77 + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_predeps_F77 + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_postdeps_F77 + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_F77 + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_F77 + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_F77 + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_F77 + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_F77 + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_F77 + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_F77 + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct_F77 + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L_F77 + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_F77 + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$hardcode_automatic_F77 + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_F77 + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path_F77" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols_F77 + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_F77 + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_F77 + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_F77 + +# ### END LIBTOOL TAG CONFIG: $tagname + +__EOF__ + + +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + else + tagname="" + fi + ;; + + GCJ) + if test -n "$GCJ" && test "X$GCJ" != "Xno"; then + + + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +objext_GCJ=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}\n" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String argv) {}; }\n' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +CC=${GCJ-"gcj"} +compiler=$CC +compiler_GCJ=$CC + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +archive_cmds_need_lc_GCJ=no + + +lt_prog_compiler_no_builtin_flag_GCJ= + +if test "$GCC" = yes; then + lt_prog_compiler_no_builtin_flag_GCJ=' -fno-builtin' + + +echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:17172: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:17176: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6 + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag_GCJ="$lt_prog_compiler_no_builtin_flag_GCJ -fno-rtti -fno-exceptions" +else + : +fi + +fi + +lt_prog_compiler_wl_GCJ= +lt_prog_compiler_pic_GCJ= +lt_prog_compiler_static_GCJ= + +echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 +echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6 + + if test "$GCC" = yes; then + lt_prog_compiler_wl_GCJ='-Wl,' + lt_prog_compiler_static_GCJ='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_GCJ='-Bstatic' + fi + ;; + + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_GCJ='-m68020 -resident32 -malways-restore-a4' + ;; + + beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_GCJ='-fno-common' + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared_GCJ=no + enable_shared=no + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_GCJ=-Kconform_pic + fi + ;; + + hpux*) + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_GCJ='-fPIC' + ;; + esac + ;; + + *) + lt_prog_compiler_pic_GCJ='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl_GCJ='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_GCJ='-Bstatic' + else + lt_prog_compiler_static_GCJ='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | pw32* | os2*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl_GCJ='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case "$host_cpu" in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_GCJ='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static_GCJ='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl_GCJ='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static_GCJ='-non_shared' + ;; + + newsos6) + lt_prog_compiler_pic_GCJ='-KPIC' + lt_prog_compiler_static_GCJ='-Bstatic' + ;; + + linux*) + case $CC in + icc* | ecc*) + lt_prog_compiler_wl_GCJ='-Wl,' + lt_prog_compiler_pic_GCJ='-KPIC' + lt_prog_compiler_static_GCJ='-static' + ;; + ccc*) + lt_prog_compiler_wl_GCJ='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static_GCJ='-non_shared' + ;; + esac + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl_GCJ='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static_GCJ='-non_shared' + ;; + + sco3.2v5*) + lt_prog_compiler_pic_GCJ='-Kpic' + lt_prog_compiler_static_GCJ='-dn' + ;; + + solaris*) + lt_prog_compiler_wl_GCJ='-Wl,' + lt_prog_compiler_pic_GCJ='-KPIC' + lt_prog_compiler_static_GCJ='-Bstatic' + ;; + + sunos4*) + lt_prog_compiler_wl_GCJ='-Qoption ld ' + lt_prog_compiler_pic_GCJ='-PIC' + lt_prog_compiler_static_GCJ='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + lt_prog_compiler_wl_GCJ='-Wl,' + lt_prog_compiler_pic_GCJ='-KPIC' + lt_prog_compiler_static_GCJ='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic_GCJ='-Kconform_pic' + lt_prog_compiler_static_GCJ='-Bstatic' + fi + ;; + + uts4*) + lt_prog_compiler_pic_GCJ='-pic' + lt_prog_compiler_static_GCJ='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared_GCJ=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_GCJ" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_GCJ" >&6 + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_GCJ"; then + +echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works" >&5 +echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works... $ECHO_C" >&6 +if test "${lt_prog_compiler_pic_works_GCJ+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_prog_compiler_pic_works_GCJ=no + ac_outfile=conftest.$ac_objext + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_GCJ" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:17405: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:17409: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s conftest.err; then + lt_prog_compiler_pic_works_GCJ=yes + fi + fi + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_GCJ" >&5 +echo "${ECHO_T}$lt_prog_compiler_pic_works_GCJ" >&6 + +if test x"$lt_prog_compiler_pic_works_GCJ" = xyes; then + case $lt_prog_compiler_pic_GCJ in + "" | " "*) ;; + *) lt_prog_compiler_pic_GCJ=" $lt_prog_compiler_pic_GCJ" ;; + esac +else + lt_prog_compiler_pic_GCJ= + lt_prog_compiler_can_build_shared_GCJ=no +fi + +fi +case "$host_os" in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_GCJ= + ;; + *) + lt_prog_compiler_pic_GCJ="$lt_prog_compiler_pic_GCJ" + ;; +esac + +echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 +echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6 +if test "${lt_cv_prog_compiler_c_o_GCJ+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + lt_cv_prog_compiler_c_o_GCJ=no + $rm -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:17465: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:17469: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + if test ! -s out/conftest.err; then + lt_cv_prog_compiler_c_o_GCJ=yes + fi + fi + chmod u+w . + $rm conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files + $rm out/* && rmdir out + cd .. + rmdir conftest + $rm conftest* + +fi +echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_GCJ" >&5 +echo "${ECHO_T}$lt_cv_prog_compiler_c_o_GCJ" >&6 + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_GCJ" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 +echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6 + hard_links=yes + $rm conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + echo "$as_me:$LINENO: result: $hard_links" >&5 +echo "${ECHO_T}$hard_links" >&6 + if test "$hard_links" = no; then + { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + +echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6 + + runpath_var= + allow_undefined_flag_GCJ= + enable_shared_with_static_runtimes_GCJ=no + archive_cmds_GCJ= + archive_expsym_cmds_GCJ= + old_archive_From_new_cmds_GCJ= + old_archive_from_expsyms_cmds_GCJ= + export_dynamic_flag_spec_GCJ= + whole_archive_flag_spec_GCJ= + thread_safe_flag_spec_GCJ= + hardcode_libdir_flag_spec_GCJ= + hardcode_libdir_flag_spec_ld_GCJ= + hardcode_libdir_separator_GCJ= + hardcode_direct_GCJ=no + hardcode_minus_L_GCJ=no + hardcode_shlibpath_var_GCJ=unsupported + link_all_deplibs_GCJ=unknown + hardcode_automatic_GCJ=no + module_cmds_GCJ= + module_expsym_cmds_GCJ= + always_export_symbols_GCJ=no + export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms_GCJ= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms_GCJ="_GLOBAL_OFFSET_TABLE_" + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + openbsd*) + with_gnu_ld=no + ;; + esac + + ld_shlibs_GCJ=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # See if GNU ld supports shared libraries. + case $host_os in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs_GCJ=no + cat <&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +EOF + fi + ;; + + amigaos*) + archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_minus_L_GCJ=yes + + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we can't use + # them. + ld_shlibs_GCJ=no + ;; + + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_GCJ=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_GCJ='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_GCJ=no + fi + ;; + + cygwin* | mingw* | pw32*) + # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, GCJ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_GCJ='-L$libdir' + allow_undefined_flag_GCJ=unsupported + always_export_symbols_GCJ=no + enable_shared_with_static_runtimes_GCJ=yes + export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_GCJ='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib' + else + ld_shlibs=no + fi + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds_GCJ='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris* | sysv5*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs_GCJ=no + cat <&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +EOF + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs_GCJ=no + fi + ;; + + sunos4*) + archive_cmds_GCJ='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_cmds_GCJ="$tmp_archive_cmds" + supports_anon_versioning=no + case `$LD -v 2>/dev/null` in + *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + if test $supports_anon_versioning = yes; then + archive_expsym_cmds_GCJ='$echo "{ global:" > $output_objdir/$libname.ver~ +cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ +$echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + else + archive_expsym_cmds_GCJ="$tmp_archive_cmds" + fi + else + ld_shlibs_GCJ=no + fi + ;; + + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs_GCJ=no + fi + ;; + esac + + if test "$ld_shlibs_GCJ" = yes; then + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_GCJ='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_GCJ='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_GCJ="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_GCJ= + fi + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag_GCJ=unsupported + always_export_symbols_GCJ=yes + archive_expsym_cmds_GCJ='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L_GCJ=yes + if test "$GCC" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct_GCJ=unsupported + fi + ;; + + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | grep 'GNU' > /dev/null; then + export_symbols_cmds_GCJ='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_GCJ='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_GCJ='' + hardcode_direct_GCJ=yes + hardcode_libdir_separator_GCJ=':' + link_all_deplibs_GCJ=yes + + if test "$GCC" = yes; then + case $host_os in aix4.012|aix4.012.*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct_GCJ=yes + else + # We have old collect2 + hardcode_direct_GCJ=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_GCJ=yes + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_libdir_separator_GCJ= + fi + esac + shared_flag='-shared' + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols_GCJ=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_GCJ='-berok' + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds_GCJ="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_GCJ='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_GCJ="-z nodefs" + archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an empty executable. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'`; fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + + hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_GCJ=' ${wl}-bernotok' + allow_undefined_flag_GCJ=' ${wl}-berok' + # -bexpall does not export symbols beginning with underscore (_) + always_export_symbols_GCJ=yes + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_GCJ=' ' + archive_cmds_need_lc_GCJ=yes + # This is similar to how AIX traditionally builds it's shared libraries. + archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_minus_L_GCJ=yes + # see comment about different semantics on the GNU ld section + ld_shlibs_GCJ=no + ;; + + bsdi4*) + export_dynamic_flag_spec_GCJ=-rdynamic + ;; + + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_GCJ=' ' + allow_undefined_flag_GCJ=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_GCJ='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_From_new_cmds_GCJ='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds_GCJ='lib /OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes_GCJ=yes + ;; + + darwin* | rhapsody*) + if test "$GXX" = yes ; then + archive_cmds_need_lc_GCJ=no + case "$host_os" in + rhapsody* | darwin1.[012]) + allow_undefined_flag_GCJ='-undefined suppress' + ;; + *) # Darwin 1.3 on + if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then + allow_undefined_flag_GCJ='-flat_namespace -undefined suppress' + else + case ${MACOSX_DEPLOYMENT_TARGET} in + 10.[012]) + allow_undefined_flag_GCJ='-flat_namespace -undefined suppress' + ;; + 10.*) + allow_undefined_flag_GCJ='-undefined dynamic_lookup' + ;; + esac + fi + ;; + esac + lt_int_apple_cc_single_mod=no + output_verbose_link_cmd='echo' + if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then + lt_int_apple_cc_single_mod=yes + fi + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_cmds_GCJ='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + else + archive_cmds_GCJ='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + fi + module_cmds_GCJ='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' + # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's + if test "X$lt_int_apple_cc_single_mod" = Xyes ; then + archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + else + archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' + hardcode_direct_GCJ=no + hardcode_automatic_GCJ=yes + hardcode_shlibpath_var_GCJ=unsupported + whole_archive_flag_spec_GCJ='-all_load $convenience' + link_all_deplibs_GCJ=yes + else + ld_shlibs_GCJ=no + fi + ;; + + dgux*) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_shlibpath_var_GCJ=no + ;; + + freebsd1*) + ld_shlibs_GCJ=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec_GCJ='-R$libdir' + hardcode_direct_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_GCJ=yes + hardcode_minus_L_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | kfreebsd*-gnu) + archive_cmds_GCJ='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_GCJ='-R$libdir' + hardcode_direct_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds_GCJ='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds_GCJ='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_GCJ=: + hardcode_direct_GCJ=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_GCJ=yes + export_dynamic_flag_spec_GCJ='${wl}-E' + ;; + + hpux10* | hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case "$host_cpu" in + hppa*64*|ia64*) + archive_cmds_GCJ='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags' + ;; + *) + archive_cmds_GCJ='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + case "$host_cpu" in + hppa*64*) + hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld_GCJ='+b $libdir' + hardcode_libdir_separator_GCJ=: + hardcode_direct_GCJ=no + hardcode_shlibpath_var_GCJ=no + ;; + ia64*) + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_direct_GCJ=no + hardcode_shlibpath_var_GCJ=no + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_GCJ=yes + ;; + *) + hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_GCJ=: + hardcode_direct_GCJ=yes + export_dynamic_flag_spec_GCJ='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L_GCJ=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_GCJ='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_ld_GCJ='-rpath $libdir' + fi + hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_GCJ=: + link_all_deplibs_GCJ=yes + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds_GCJ='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec_GCJ='-R$libdir' + hardcode_direct_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + newsos6) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_GCJ=yes + hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_GCJ=: + hardcode_shlibpath_var_GCJ=no + ;; + + openbsd*) + hardcode_direct_GCJ=yes + hardcode_shlibpath_var_GCJ=no + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' + export_dynamic_flag_spec_GCJ='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_GCJ='-R$libdir' + ;; + *) + archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + + os2*) + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_minus_L_GCJ=yes + allow_undefined_flag_GCJ=unsupported + archive_cmds_GCJ='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_From_new_cmds_GCJ='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag_GCJ=' -expect_unresolved \*' + archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + fi + hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_GCJ=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag_GCJ=' -expect_unresolved \*' + archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_GCJ='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ + $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec_GCJ='-rpath $libdir' + fi + hardcode_libdir_separator_GCJ=: + ;; + + sco3.2v5*) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_GCJ=no + export_dynamic_flag_spec_GCJ='${wl}-Bexport' + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ;; + + solaris*) + no_undefined_flag_GCJ=' -z text' + if test "$GCC" = yes; then + archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' + else + archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + fi + hardcode_libdir_flag_spec_GCJ='-R$libdir' + hardcode_shlibpath_var_GCJ=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_GCJ='-z allextract$convenience -z defaultextract' ;; + esac + link_all_deplibs_GCJ=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds_GCJ='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds_GCJ='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_direct_GCJ=yes + hardcode_minus_L_GCJ=yes + hardcode_shlibpath_var_GCJ=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_GCJ=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds_GCJ='$CC -r -o $output$reload_objs' + hardcode_direct_GCJ=no + ;; + motorola) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_GCJ=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var_GCJ=no + ;; + + sysv4.3*) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_GCJ=no + export_dynamic_flag_spec_GCJ='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var_GCJ=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs_GCJ=yes + fi + ;; + + sysv4.2uw2*) + archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct_GCJ=yes + hardcode_minus_L_GCJ=no + hardcode_shlibpath_var_GCJ=no + hardcode_runpath_var=yes + runpath_var=LD_RUN_PATH + ;; + + sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*) + no_undefined_flag_GCJ='${wl}-z ${wl}text' + if test "$GCC" = yes; then + archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds_GCJ='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var_GCJ=no + ;; + + sysv5*) + no_undefined_flag_GCJ=' -z text' + # $CC -shared without GNU ld will not create a library from C++ + # object files and a static libstdc++, better avoid it by now + archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' + hardcode_libdir_flag_spec_GCJ= + hardcode_shlibpath_var_GCJ=no + runpath_var='LD_RUN_PATH' + ;; + + uts4*) + archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec_GCJ='-L$libdir' + hardcode_shlibpath_var_GCJ=no + ;; + + *) + ld_shlibs_GCJ=no + ;; + esac + fi + +echo "$as_me:$LINENO: result: $ld_shlibs_GCJ" >&5 +echo "${ECHO_T}$ld_shlibs_GCJ" >&6 +test "$ld_shlibs_GCJ" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_GCJ" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_GCJ=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_GCJ in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 +echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6 + $rm conftest* + printf "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_GCJ + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_GCJ + allow_undefined_flag_GCJ= + if { (eval echo "$as_me:$LINENO: \"$archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 + (eval $archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + then + archive_cmds_need_lc_GCJ=no + else + archive_cmds_need_lc_GCJ=yes + fi + allow_undefined_flag_GCJ=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $rm conftest* + echo "$as_me:$LINENO: result: $archive_cmds_need_lc_GCJ" >&5 +echo "${ECHO_T}$archive_cmds_need_lc_GCJ" >&6 + ;; + esac + fi + ;; +esac + +echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 +echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6 +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix4* | aix5*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi4*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $rm \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)' + # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. + if test "$GCC" = yes; then + sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` + else + sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' + fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +kfreebsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +freebsd*) + objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout` + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.01* | freebsdelf3.01*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + *) # from 3.2 on + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case "$host_cpu" in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +knetbsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='GNU ld.so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +nto-qnx*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +openbsd*) + version_type=sunos + need_lib_prefix=no + need_version=yes + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + export_dynamic_flag_spec='${wl}-Blargedynsym' + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$as_me:$LINENO: result: $dynamic_linker" >&5 +echo "${ECHO_T}$dynamic_linker" >&6 +test "$dynamic_linker" = no && can_build_shared=no + +echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 +echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6 +hardcode_action_GCJ= +if test -n "$hardcode_libdir_flag_spec_GCJ" || \ + test -n "$runpath_var GCJ" || \ + test "X$hardcode_automatic_GCJ"="Xyes" ; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct_GCJ" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, GCJ)" != no && + test "$hardcode_minus_L_GCJ" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_GCJ=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_GCJ=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_GCJ=unsupported +fi +echo "$as_me:$LINENO: result: $hardcode_action_GCJ" >&5 +echo "${ECHO_T}$hardcode_action_GCJ" >&6 + +if test "$hardcode_action_GCJ" = relink; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + +striplib= +old_striplib= +echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 +echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6 +if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + ;; + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + esac +fi + +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + echo "$as_me:$LINENO: checking for shl_load" >&5 +echo $ECHO_N "checking for shl_load... $ECHO_C" >&6 +if test "${ac_cv_func_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define shl_load to an innocuous variant, in case declares shl_load. + For example, HP-UX 11i declares gettimeofday. */ +#define shl_load innocuous_shl_load + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shl_load (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef shl_load + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shl_load) || defined (__stub___shl_load) +choke me +#else +char (*f) () = shl_load; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != shl_load; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 +echo "${ECHO_T}$ac_cv_func_shl_load" >&6 +if test $ac_cv_func_shl_load = yes; then + lt_cv_dlopen="shl_load" +else + echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 +echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_shl_load+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load (); +int +main () +{ +shl_load (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_shl_load=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_shl_load=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6 +if test $ac_cv_lib_dld_shl_load = yes; then + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld" +else + echo "$as_me:$LINENO: checking for dlopen" >&5 +echo $ECHO_N "checking for dlopen... $ECHO_C" >&6 +if test "${ac_cv_func_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define dlopen to an innocuous variant, in case declares dlopen. + For example, HP-UX 11i declares gettimeofday. */ +#define dlopen innocuous_dlopen + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char dlopen (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef dlopen + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_dlopen) || defined (__stub___dlopen) +choke me +#else +char (*f) () = dlopen; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != dlopen; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 +echo "${ECHO_T}$ac_cv_func_dlopen" >&6 +if test $ac_cv_func_dlopen = yes; then + lt_cv_dlopen="dlopen" +else + echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 +echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6 +if test "${ac_cv_lib_svld_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_svld_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_svld_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6 +if test $ac_cv_lib_svld_dlopen = yes; then + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 +echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6 +if test "${ac_cv_lib_dld_dld_link+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dld_link (); +int +main () +{ +dld_link (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dld_dld_link=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dld_dld_link=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 +echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6 +if test $ac_cv_lib_dld_dld_link = yes; then + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5 +echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self" >&6 + + if test "x$lt_cv_dlopen_self" = xyes; then + LDFLAGS="$LDFLAGS $link_static_flag" + echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5 +echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6 +if test "${lt_cv_dlopen_self_static+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext < +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" void exit (int); +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + + exit (status); +} +EOF + if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5 +echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6 + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + +# The else clause should only fire when bootstrapping the +# libtool distribution, otherwise you forgot to ship ltmain.sh +# with your package, and you will get complaints that there are +# no rules to generate ltmain.sh. +if test -f "$ltmain"; then + # See if we are running on zsh, and set the options which allow our commands through + # without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. + for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ + deplibs_check_method reload_flag reload_cmds need_locks \ + lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ + lt_cv_sys_global_symbol_to_c_name_address \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + old_postinstall_cmds old_postuninstall_cmds \ + compiler_GCJ \ + CC_GCJ \ + LD_GCJ \ + lt_prog_compiler_wl_GCJ \ + lt_prog_compiler_pic_GCJ \ + lt_prog_compiler_static_GCJ \ + lt_prog_compiler_no_builtin_flag_GCJ \ + export_dynamic_flag_spec_GCJ \ + thread_safe_flag_spec_GCJ \ + whole_archive_flag_spec_GCJ \ + enable_shared_with_static_runtimes_GCJ \ + old_archive_cmds_GCJ \ + old_archive_from_new_cmds_GCJ \ + predep_objects_GCJ \ + postdep_objects_GCJ \ + predeps_GCJ \ + postdeps_GCJ \ + compiler_lib_search_path_GCJ \ + archive_cmds_GCJ \ + archive_expsym_cmds_GCJ \ + postinstall_cmds_GCJ \ + postuninstall_cmds_GCJ \ + old_archive_from_expsyms_cmds_GCJ \ + allow_undefined_flag_GCJ \ + no_undefined_flag_GCJ \ + export_symbols_cmds_GCJ \ + hardcode_libdir_flag_spec_GCJ \ + hardcode_libdir_flag_spec_ld_GCJ \ + hardcode_libdir_separator_GCJ \ + hardcode_automatic_GCJ \ + module_cmds_GCJ \ + module_expsym_cmds_GCJ \ + lt_cv_prog_compiler_c_o_GCJ \ + exclude_expsyms_GCJ \ + include_expsyms_GCJ; do + + case $var in + old_archive_cmds_GCJ | \ + old_archive_from_new_cmds_GCJ | \ + archive_cmds_GCJ | \ + archive_expsym_cmds_GCJ | \ + module_cmds_GCJ | \ + module_expsym_cmds_GCJ | \ + old_archive_from_expsyms_cmds_GCJ | \ + export_symbols_cmds_GCJ | \ + extract_expsyms_cmds | reload_cmds | finish_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case $lt_echo in + *'\$0 --fallback-echo"') + lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + +cfgfile="$ofile" + + cat <<__EOF__ >> "$cfgfile" +# ### BEGIN LIBTOOL TAG CONFIG: $tagname + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_GCJ + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_GCJ + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_compiler_GCJ + +# Is the compiler the GNU C compiler? +with_gcc=$GCC_GCJ + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_LD_GCJ + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_GCJ + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_GCJ +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_GCJ + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_GCJ + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_GCJ + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_GCJ + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_GCJ + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_thread_safe_flag_spec_GCJ + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_old_archive_cmds_GCJ +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_GCJ + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_GCJ + +# Commands used to build and install a shared archive. +archive_cmds=$lt_archive_cmds_GCJ +archive_expsym_cmds=$lt_archive_expsym_cmds_GCJ +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_module_cmds_GCJ +module_expsym_cmds=$lt_module_expsym_cmds_GCJ + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_predep_objects_GCJ + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_postdep_objects_GCJ + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_predeps_GCJ + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_postdeps_GCJ + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_GCJ + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_GCJ + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_GCJ + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_GCJ + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_GCJ + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_GCJ + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_GCJ + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct_GCJ + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L_GCJ + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_GCJ + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$hardcode_automatic_GCJ + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_GCJ + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path_GCJ" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols_GCJ + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_GCJ + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_GCJ + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_GCJ + +# ### END LIBTOOL TAG CONFIG: $tagname + +__EOF__ + + +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + else + tagname="" + fi + ;; + + RC) + + + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +objext_RC=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +CC=${RC-"windres"} +compiler=$CC +compiler_RC=$CC +lt_cv_prog_compiler_c_o_RC=yes + +# The else clause should only fire when bootstrapping the +# libtool distribution, otherwise you forgot to ship ltmain.sh +# with your package, and you will get complaints that there are +# no rules to generate ltmain.sh. +if test -f "$ltmain"; then + # See if we are running on zsh, and set the options which allow our commands through + # without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + # Now quote all the things that may contain metacharacters while being + # careful not to overquote the AC_SUBSTed values. We take copies of the + # variables and quote the copies for generation of the libtool script. + for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \ + SED SHELL STRIP \ + libname_spec library_names_spec soname_spec extract_expsyms_cmds \ + old_striplib striplib file_magic_cmd finish_cmds finish_eval \ + deplibs_check_method reload_flag reload_cmds need_locks \ + lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ + lt_cv_sys_global_symbol_to_c_name_address \ + sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ + old_postinstall_cmds old_postuninstall_cmds \ + compiler_RC \ + CC_RC \ + LD_RC \ + lt_prog_compiler_wl_RC \ + lt_prog_compiler_pic_RC \ + lt_prog_compiler_static_RC \ + lt_prog_compiler_no_builtin_flag_RC \ + export_dynamic_flag_spec_RC \ + thread_safe_flag_spec_RC \ + whole_archive_flag_spec_RC \ + enable_shared_with_static_runtimes_RC \ + old_archive_cmds_RC \ + old_archive_from_new_cmds_RC \ + predep_objects_RC \ + postdep_objects_RC \ + predeps_RC \ + postdeps_RC \ + compiler_lib_search_path_RC \ + archive_cmds_RC \ + archive_expsym_cmds_RC \ + postinstall_cmds_RC \ + postuninstall_cmds_RC \ + old_archive_from_expsyms_cmds_RC \ + allow_undefined_flag_RC \ + no_undefined_flag_RC \ + export_symbols_cmds_RC \ + hardcode_libdir_flag_spec_RC \ + hardcode_libdir_flag_spec_ld_RC \ + hardcode_libdir_separator_RC \ + hardcode_automatic_RC \ + module_cmds_RC \ + module_expsym_cmds_RC \ + lt_cv_prog_compiler_c_o_RC \ + exclude_expsyms_RC \ + include_expsyms_RC; do + + case $var in + old_archive_cmds_RC | \ + old_archive_from_new_cmds_RC | \ + archive_cmds_RC | \ + archive_expsym_cmds_RC | \ + module_cmds_RC | \ + module_expsym_cmds_RC | \ + old_archive_from_expsyms_cmds_RC | \ + export_symbols_cmds_RC | \ + extract_expsyms_cmds | reload_cmds | finish_cmds | \ + postinstall_cmds | postuninstall_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | \ + sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) + # Double-quote double-evaled strings. + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + ;; + esac + done + + case $lt_echo in + *'\$0 --fallback-echo"') + lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` + ;; + esac + +cfgfile="$ofile" + + cat <<__EOF__ >> "$cfgfile" +# ### BEGIN LIBTOOL TAG CONFIG: $tagname + +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_RC + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_RC + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# The host system. +host_alias=$host_alias +host=$host + +# An echo program that does not interpret backslashes. +echo=$lt_echo + +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS + +# A C compiler. +LTCC=$lt_LTCC + +# A language-specific compiler. +CC=$lt_compiler_RC + +# Is the compiler the GNU C compiler? +with_gcc=$GCC_RC + +# An ERE matcher. +EGREP=$lt_EGREP + +# The linker used to build libraries. +LD=$lt_LD_RC + +# Whether we need hard or soft links. +LN_S=$lt_LN_S + +# A BSD-compatible nm program. +NM=$lt_NM + +# A symbol stripping program +STRIP=$lt_STRIP + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=$MAGIC_CMD + +# Used on cygwin: DLL creation program. +DLLTOOL="$DLLTOOL" + +# Used on cygwin: object dumper. +OBJDUMP="$OBJDUMP" + +# Used on cygwin: assembler. +AS="$AS" + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_RC + +# Object file suffix (normally "o"). +objext="$ac_objext" + +# Old archive suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally ".so"). +shrext_cmds='$shrext_cmds' + +# Executable file suffix (normally ""). +exeext="$exeext" + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_RC +pic_mode=$pic_mode + +# What is the maximum length of a command? +max_cmd_len=$lt_cv_sys_max_cmd_len + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_RC + +# Must we lock files when doing compilation ? +need_locks=$lt_need_locks + +# Do we need the lib prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_RC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_RC + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_RC + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_RC + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec=$lt_thread_safe_flag_spec_RC + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Commands used to build and install an old-style archive. +RANLIB=$lt_RANLIB +old_archive_cmds=$lt_old_archive_cmds_RC +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_RC + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_RC + +# Commands used to build and install a shared archive. +archive_cmds=$lt_archive_cmds_RC +archive_expsym_cmds=$lt_archive_expsym_cmds_RC +postinstall_cmds=$lt_postinstall_cmds +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds=$lt_module_cmds_RC +module_expsym_cmds=$lt_module_expsym_cmds_RC + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects=$lt_predep_objects_RC + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects=$lt_postdep_objects_RC + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps=$lt_predeps_RC + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps=$lt_postdeps_RC + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_RC + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd=$lt_file_magic_cmd + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_RC + +# Flag that forces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_RC + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval=$lt_finish_eval + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_RC + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_RC + +# If ld is used when linking, flag to hardcode \$libdir into +# a binary during linking. This must work even if \$libdir does +# not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_RC + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_RC + +# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct_RC + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L_RC + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_RC + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=$hardcode_automatic_RC + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="$variables_saved_for_relink" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_RC + +# Compile-time system search path for libraries +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path="$fix_srcfile_path_RC" + +# Set to yes if exported symbols are required. +always_export_symbols=$always_export_symbols_RC + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_RC + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_RC + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_RC + +# ### END LIBTOOL TAG CONFIG: $tagname + +__EOF__ + + +else + # If there is no Makefile yet, we rely on a make rule to execute + # `config.status --recheck' to rerun these tests and create the + # libtool script then. + ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` + if test -f "$ltmain_in"; then + test -f Makefile && make "$ltmain" + fi +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC="$lt_save_CC" + + ;; + + *) + { { echo "$as_me:$LINENO: error: Unsupported tag name: $tagname" >&5 +echo "$as_me: error: Unsupported tag name: $tagname" >&2;} + { (exit 1); exit 1; }; } + ;; + esac + + # Append the new tag name to the list of available tags. + if test -n "$tagname" ; then + available_tags="$available_tags $tagname" + fi + fi + done + IFS="$lt_save_ifs" + + # Now substitute the updated list of available tags. + if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then + mv "${ofile}T" "$ofile" + chmod +x "$ofile" + else + rm -f "${ofile}T" + { { echo "$as_me:$LINENO: error: unable to update list of available tagged configurations." >&5 +echo "$as_me: error: unable to update list of available tagged configurations." >&2;} + { (exit 1); exit 1; }; } + fi +fi + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + +# Prevent multiple expansion + + + + + + + + + + + + + + + + + + + + + +echo "$as_me:$LINENO: checking if we need -no-undefined" >&5 +echo $ECHO_N "checking if we need -no-undefined... $ECHO_C" >&6 +case $host in + *-*-cygwin | *-*-mingw* | *-*-pw32*) + need_no_undefined=yes + ;; + *) + need_no_undefined=no + ;; +esac +echo "$as_me:$LINENO: result: $need_no_undefined" >&5 +echo "${ECHO_T}$need_no_undefined" >&6 + + +if test x$need_no_undefined = xyes; then + NO_UNDEFINED_TRUE= + NO_UNDEFINED_FALSE='#' +else + NO_UNDEFINED_TRUE='#' + NO_UNDEFINED_FALSE= +fi + + +echo "$as_me:$LINENO: checking if we need -mimpure-text" >&5 +echo $ECHO_N "checking if we need -mimpure-text... $ECHO_C" >&6 +mimpure=no +case $host in + *-*-solaris2*) + if test "$GCC" = "yes"; then + mimpure="yes" + fi + ;; + *) + ;; +esac +echo "$as_me:$LINENO: result: $mimpure" >&5 +echo "${ECHO_T}$mimpure" >&6 + + +if test x$mimpure = xyes; then + MIMPURE_TRUE= + MIMPURE_FALSE='#' +else + MIMPURE_TRUE='#' + MIMPURE_FALSE= +fi + + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +echo "$as_me:$LINENO: checking whether to support http" >&5 +echo $ECHO_N "checking whether to support http... $ECHO_C" >&6 +# Check whether --enable-http or --disable-http was given. +if test "${enable_http+set}" = set; then + enableval="$enable_http" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_HTTP 1 +_ACEOF + + { echo "$as_me:$LINENO: WARNING: disable HTTP disables FTP over proxy and GOPHER too" >&5 +echo "$as_me: WARNING: disable HTTP disables FTP over proxy and GOPHER too" >&2;} + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_GOPHER 1 +_ACEOF + + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support ftp" >&5 +echo $ECHO_N "checking whether to support ftp... $ECHO_C" >&6 +# Check whether --enable-ftp or --disable-ftp was given. +if test "${enable_ftp+set}" = set; then + enableval="$enable_ftp" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_FTP 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support gopher" >&5 +echo $ECHO_N "checking whether to support gopher... $ECHO_C" >&6 +# Check whether --enable-gopher or --disable-gopher was given. +if test "${enable_gopher+set}" = set; then + enableval="$enable_gopher" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_GOPHER 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support file" >&5 +echo $ECHO_N "checking whether to support file... $ECHO_C" >&6 +# Check whether --enable-file or --disable-file was given. +if test "${enable_file+set}" = set; then + enableval="$enable_file" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_FILE 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support ldap" >&5 +echo $ECHO_N "checking whether to support ldap... $ECHO_C" >&6 +# Check whether --enable-ldap or --disable-ldap was given. +if test "${enable_ldap+set}" = set; then + enableval="$enable_ldap" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_LDAP 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support dict" >&5 +echo $ECHO_N "checking whether to support dict... $ECHO_C" >&6 +# Check whether --enable-dict or --disable-dict was given. +if test "${enable_dict+set}" = set; then + enableval="$enable_dict" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_DICT 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; +echo "$as_me:$LINENO: checking whether to support telnet" >&5 +echo $ECHO_N "checking whether to support telnet... $ECHO_C" >&6 +# Check whether --enable-telnet or --disable-telnet was given. +if test "${enable_telnet+set}" = set; then + enableval="$enable_telnet" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +cat >>confdefs.h <<\_ACEOF +#define CURL_DISABLE_TELNET 1 +_ACEOF + + + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi; + + +echo "$as_me:$LINENO: checking whether to provide built-in manual" >&5 +echo $ECHO_N "checking whether to provide built-in manual... $ECHO_C" >&6 +# Check whether --enable-manual or --disable-manual was given. +if test "${enable_manual+set}" = set; then + enableval="$enable_manual" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + USE_MANUAL="1" + ;; + esac +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + USE_MANUAL="1" + +fi; + + + +echo "$as_me:$LINENO: checking for gethostbyname" >&5 +echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_func_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define gethostbyname to an innocuous variant, in case declares gethostbyname. + For example, HP-UX 11i declares gettimeofday. */ +#define gethostbyname innocuous_gethostbyname + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef gethostbyname + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +char (*f) () = gethostbyname; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != gethostbyname; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 +if test $ac_cv_func_gethostbyname = yes; then + HAVE_GETHOSTBYNAME="1" + +else + echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_nsl_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 +if test $ac_cv_lib_nsl_gethostbyname = yes; then + HAVE_GETHOSTBYNAME="1" + LIBS="$LIBS -lnsl" + +fi + + +fi + + +if test "$HAVE_GETHOSTBYNAME" != "1" +then + echo "$as_me:$LINENO: checking for gethostbyname in -lsocket" >&5 +echo $ECHO_N "checking for gethostbyname in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_socket_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_socket_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_gethostbyname" >&6 +if test $ac_cv_lib_socket_gethostbyname = yes; then + HAVE_GETHOSTBYNAME="1" + LIBS="$LIBS -lsocket" + +fi + +fi + +if test "$HAVE_GETHOSTBYNAME" != "1" +then + echo "$as_me:$LINENO: checking for gethostbyname with both nsl and socket libs" >&5 +echo $ECHO_N "checking for gethostbyname with both nsl and socket libs... $ECHO_C" >&6 + my_ac_save_LIBS=$LIBS + LIBS="-lnsl -lsocket $LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +gethostbyname(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + HAVE_GETHOSTBYNAME="1" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + LIBS=$my_ac_save_LIBS + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +if test "$HAVE_GETHOSTBYNAME" != "1" +then + echo "$as_me:$LINENO: checking for gethostbyname in ws2_32" >&5 +echo $ECHO_N "checking for gethostbyname in ws2_32... $ECHO_C" >&6 + my_ac_save_LIBS=$LIBS + LIBS="-lws2_32 $LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +gethostbyname("www.dummysite.com"); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + HAVE_GETHOSTBYNAME="1" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + LIBS=$my_ac_save_LIBS + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +if test "$HAVE_GETHOSTBYNAME" = "1"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETHOSTBYNAME 1 +_ACEOF + +else + { { echo "$as_me:$LINENO: error: couldn't find libraries for gethostbyname()" >&5 +echo "$as_me: error: couldn't find libraries for gethostbyname()" >&2;} + { (exit 1); exit 1; }; } +fi + +echo "$as_me:$LINENO: checking for strcasecmp" >&5 +echo $ECHO_N "checking for strcasecmp... $ECHO_C" >&6 +if test "${ac_cv_func_strcasecmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define strcasecmp to an innocuous variant, in case declares strcasecmp. + For example, HP-UX 11i declares gettimeofday. */ +#define strcasecmp innocuous_strcasecmp + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char strcasecmp (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef strcasecmp + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strcasecmp (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_strcasecmp) || defined (__stub___strcasecmp) +choke me +#else +char (*f) () = strcasecmp; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != strcasecmp; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_strcasecmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_strcasecmp=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_strcasecmp" >&5 +echo "${ECHO_T}$ac_cv_func_strcasecmp" >&6 +if test $ac_cv_func_strcasecmp = yes; then + : +else + +echo "$as_me:$LINENO: checking for strcasecmp in -lresolve" >&5 +echo $ECHO_N "checking for strcasecmp in -lresolve... $ECHO_C" >&6 +if test "${ac_cv_lib_resolve_strcasecmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lresolve $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strcasecmp (); +int +main () +{ +strcasecmp (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_resolve_strcasecmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_resolve_strcasecmp=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_resolve_strcasecmp" >&5 +echo "${ECHO_T}$ac_cv_lib_resolve_strcasecmp" >&6 +if test $ac_cv_lib_resolve_strcasecmp = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRESOLVE 1 +_ACEOF + + LIBS="-lresolve $LIBS" + +fi + +fi + + +if test "$ac_cv_lib_resolve_strcasecmp" = "$ac_cv_func_strcasecmp"; then + echo "$as_me:$LINENO: checking for strcasecmp in -lresolve" >&5 +echo $ECHO_N "checking for strcasecmp in -lresolve... $ECHO_C" >&6 +if test "${ac_cv_lib_resolve_strcasecmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lresolve -lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strcasecmp (); +int +main () +{ +strcasecmp (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_resolve_strcasecmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_resolve_strcasecmp=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_resolve_strcasecmp" >&5 +echo "${ECHO_T}$ac_cv_lib_resolve_strcasecmp" >&6 +if test $ac_cv_lib_resolve_strcasecmp = yes; then + LIBS="-lresolve $LIBS" +fi + +fi + +echo "$as_me:$LINENO: checking for connect" >&5 +echo $ECHO_N "checking for connect... $ECHO_C" >&6 +if test "${ac_cv_func_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define connect to an innocuous variant, in case declares connect. + For example, HP-UX 11i declares gettimeofday. */ +#define connect innocuous_connect + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef connect + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +char (*f) () = connect; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != connect; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5 +echo "${ECHO_T}$ac_cv_func_connect" >&6 +if test $ac_cv_func_connect = yes; then + : +else + +echo "$as_me:$LINENO: checking for connect in -lsocket" >&5 +echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +int +main () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_socket_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 +if test $ac_cv_lib_socket_connect = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSOCKET 1 +_ACEOF + + LIBS="-lsocket $LIBS" + +fi + +fi + + +echo "$as_me:$LINENO: checking for dlclose" >&5 +echo $ECHO_N "checking for dlclose... $ECHO_C" >&6 +if test "${ac_cv_func_dlclose+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define dlclose to an innocuous variant, in case declares dlclose. + For example, HP-UX 11i declares gettimeofday. */ +#define dlclose innocuous_dlclose + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char dlclose (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef dlclose + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlclose (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_dlclose) || defined (__stub___dlclose) +choke me +#else +char (*f) () = dlclose; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != dlclose; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_dlclose=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_dlclose=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_dlclose" >&5 +echo "${ECHO_T}$ac_cv_func_dlclose" >&6 +if test $ac_cv_func_dlclose = yes; then + : +else + +echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBDL 1 +_ACEOF + + LIBS="-ldl $LIBS" + +fi + +fi + + +echo "$as_me:$LINENO: checking whether to use libgcc" >&5 +echo $ECHO_N "checking whether to use libgcc... $ECHO_C" >&6 +# Check whether --enable-libgcc or --disable-libgcc was given. +if test "${enable_libgcc+set}" = set; then + enableval="$enable_libgcc" + case "$enableval" in + yes) + LIBS="$LIBS -lgcc" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + *) echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + esac +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi; + + +echo "$as_me:$LINENO: checking for timeGetTime in winmm" >&5 +echo $ECHO_N "checking for timeGetTime in winmm... $ECHO_C" >&6 +my_ac_save_LIBS=$LIBS +LIBS="-lwinmm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + #include + +int +main () +{ +timeGetTime(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + LIBS=$my_ac_save_LIBS + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +echo "$as_me:$LINENO: checking whether to enable ipv6" >&5 +echo $ECHO_N "checking whether to enable ipv6... $ECHO_C" >&6 +# Check whether --enable-ipv6 or --disable-ipv6 was given. +if test "${enable_ipv6+set}" = set; then + enableval="$enable_ipv6" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ipv6=no + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ipv6=yes + ;; + esac +else + if test "$cross_compiling" = yes; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ipv6=no + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + /* is AF_INET6 available? */ +#include +#include +main() +{ + if (socket(AF_INET6, SOCK_STREAM, 0) < 0) + exit(1); + else + exit(0); +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ipv6=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ipv6=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi; + +if test "$ipv6" = "yes"; then + curl_ipv6_msg="enabled" + + + echo "$as_me:$LINENO: checking for working getaddrinfo" >&5 +echo $ECHO_N "checking for working getaddrinfo... $ECHO_C" >&6 +if test "${ac_cv_working_getaddrinfo+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + if test "$cross_compiling" = yes; then + + ac_cv_working_getaddrinfo="yes" + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#include + +int main(void) +{ + struct addrinfo hints, *ai; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo("127.0.0.1", "8080", &hints, &ai); + if (error) { + return 1; + } + return 0; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + ac_cv_working_getaddrinfo="yes" + +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) + + ac_cv_working_getaddrinfo="no" + +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_working_getaddrinfo" >&5 +echo "${ECHO_T}$ac_cv_working_getaddrinfo" >&6 +if test "$ac_cv_working_getaddrinfo" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETADDRINFO 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define ENABLE_IPV6 1 +_ACEOF + + + IPV6_ENABLED=1 + +fi + + + + echo "$as_me:$LINENO: checking for working NI_WITHSCOPEID" >&5 +echo $ECHO_N "checking for working NI_WITHSCOPEID... $ECHO_C" >&6 +if test "${ac_cv_working_ni_withscopeid+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + if test "$cross_compiling" = yes; then + ac_cv_working_ni_withscopeid="yes" + +else + cat >conftest.$ac_ext <<_ACEOF + +#include +#include +#include +#include +int main() +{ +#ifdef NI_WITHSCOPEID + struct sockaddr_storage ss; + int sslen = sizeof(ss); + int rc; + char hbuf[NI_MAXHOST]; + int fd = socket(AF_INET6, SOCK_STREAM, 0); + if(fd < 0) { + perror("socket()"); + return 1; /* couldn't create socket of either kind */ + } + + rc = getsockname(fd, (struct sockaddr *)&ss, &sslen); + if(rc) { + perror("getsockname()"); + return 2; + } + + rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), + NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID); + + if(rc) { + printf("rc = %s\n", gai_strerror(rc)); + return 3; + } + + return 0; /* everything works fine, use NI_WITHSCOPEID! */ +#else + return 4; /* we don't seem to have the definition, don't use it */ +#endif +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_working_ni_withscopeid="yes" +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) + ac_cv_working_ni_withscopeid="no" +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +fi +echo "$as_me:$LINENO: result: $ac_cv_working_ni_withscopeid" >&5 +echo "${ECHO_T}$ac_cv_working_ni_withscopeid" >&6 +if test "$ac_cv_working_ni_withscopeid" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_NI_WITHSCOPEID 1 +_ACEOF + +fi + + +fi + +# Check whether --enable-nonblocking or --disable-nonblocking was given. +if test "${enable_nonblocking+set}" = set; then + enableval="$enable_nonblocking" + + if test "$enableval" = "no" ; then + { echo "$as_me:$LINENO: WARNING: non-blocking sockets disabled" >&5 +echo "$as_me: WARNING: non-blocking sockets disabled" >&2;} + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DISABLED_NONBLOCKING 1 +_ACEOF + + else + + echo "$as_me:$LINENO: checking non-blocking sockets style" >&5 +echo $ECHO_N "checking non-blocking sockets style... $ECHO_C" >&6 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for O_NONBLOCK test */ +#include +#include +#include + +int +main () +{ + +/* try to compile O_NONBLOCK */ + +#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4) +# define PLATFORM_AIX_V3 +#endif + +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#error "O_NONBLOCK does not work on this platform" +#endif + int socket; + int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="O_NONBLOCK" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_O_NONBLOCK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for FIONBIO test */ +#include +#include + +int +main () +{ + +/* FIONBIO source test (old-style unix) */ + int socket; + int flags = ioctl(socket, FIONBIO, &flags); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="FIONBIO" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FIONBIO 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for ioctlsocket test (cygwin?) */ +#include + +int +main () +{ + +/* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="ioctlsocket" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_IOCTLSOCKET 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for IoctlSocket test (Amiga?) */ +#include + +int +main () +{ + +/* IoctlSocket source code */ + int socket; + int flags = IoctlSocket(socket, FIONBIO, (long)1); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="IoctlSocket" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_IOCTLSOCKET_CASE 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for SO_NONBLOCK test (BeOS) */ +#include +#include +#include + +int +main () +{ + +/* SO_NONBLOCK source code */ + long b = 1; + int socket; + int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="SO_NONBLOCK" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SO_NONBLOCK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +nonblock="nada" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DISABLED_NONBLOCKING 1 +_ACEOF + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: result: $nonblock" >&5 +echo "${ECHO_T}$nonblock" >&6 + + if test "$nonblock" = "nada"; then + { echo "$as_me:$LINENO: WARNING: non-block sockets disabled" >&5 +echo "$as_me: WARNING: non-block sockets disabled" >&2;} + fi + + fi + +else + + + echo "$as_me:$LINENO: checking non-blocking sockets style" >&5 +echo $ECHO_N "checking non-blocking sockets style... $ECHO_C" >&6 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for O_NONBLOCK test */ +#include +#include +#include + +int +main () +{ + +/* try to compile O_NONBLOCK */ + +#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4) +# define PLATFORM_AIX_V3 +#endif + +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#error "O_NONBLOCK does not work on this platform" +#endif + int socket; + int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="O_NONBLOCK" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_O_NONBLOCK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for FIONBIO test */ +#include +#include + +int +main () +{ + +/* FIONBIO source test (old-style unix) */ + int socket; + int flags = ioctl(socket, FIONBIO, &flags); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="FIONBIO" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FIONBIO 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for ioctlsocket test (cygwin?) */ +#include + +int +main () +{ + +/* ioctlsocket source code */ + int socket; + unsigned long flags = ioctlsocket(socket, FIONBIO, &flags); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="ioctlsocket" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_IOCTLSOCKET 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for IoctlSocket test (Amiga?) */ +#include + +int +main () +{ + +/* IoctlSocket source code */ + int socket; + int flags = IoctlSocket(socket, FIONBIO, (long)1); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="IoctlSocket" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_IOCTLSOCKET_CASE 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* headers for SO_NONBLOCK test (BeOS) */ +#include +#include +#include + +int +main () +{ + +/* SO_NONBLOCK source code */ + long b = 1; + int socket; + int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +nonblock="SO_NONBLOCK" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SO_NONBLOCK 1 +_ACEOF + + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +nonblock="nada" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DISABLED_NONBLOCKING 1 +_ACEOF + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + echo "$as_me:$LINENO: result: $nonblock" >&5 +echo "${ECHO_T}$nonblock" >&6 + + if test "$nonblock" = "nada"; then + { echo "$as_me:$LINENO: WARNING: non-block sockets disabled" >&5 +echo "$as_me: WARNING: non-block sockets disabled" >&2;} + fi + + +fi; + + + +# Check whether --with-egd-socket or --without-egd-socket was given. +if test "${with_egd_socket+set}" = set; then + withval="$with_egd_socket" + EGD_SOCKET="$withval" + +fi; +if test -n "$EGD_SOCKET" ; then + +cat >>confdefs.h <<_ACEOF +#define EGD_SOCKET "$EGD_SOCKET" +_ACEOF + +fi + + +# Check whether --with-random or --without-random was given. +if test "${with_random+set}" = set; then + withval="$with_random" + RANDOM_FILE="$withval" +else + + echo "$as_me:$LINENO: checking for \"/dev/urandom\"" >&5 +echo $ECHO_N "checking for \"/dev/urandom\"... $ECHO_C" >&6 +if test "${ac_cv_file___dev_urandom_+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + test "$cross_compiling" = yes && + { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 +echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} + { (exit 1); exit 1; }; } +if test -r ""/dev/urandom""; then + ac_cv_file___dev_urandom_=yes +else + ac_cv_file___dev_urandom_=no +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_file___dev_urandom_" >&5 +echo "${ECHO_T}$ac_cv_file___dev_urandom_" >&6 +if test $ac_cv_file___dev_urandom_ = yes; then + RANDOM_FILE="/dev/urandom" +fi + + + +fi; +if test -n "$RANDOM_FILE" ; then + + +cat >>confdefs.h <<_ACEOF +#define RANDOM_FILE "$RANDOM_FILE" +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking if argv can be written to" >&5 +echo $ECHO_N "checking if argv can be written to... $ECHO_C" >&6 +if test "${curl_cv_writable_argv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + +if test "$cross_compiling" = yes; then + curl_cv_writable_argv=cross +else + cat >conftest.$ac_ext <<_ACEOF + +int main(int argc, char ** argv) { + argv[0][0] = ' '; + return (argv[0][0] == ' ')?0:1; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + curl_cv_writable_argv=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +curl_cv_writable_argv=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +fi + +case $curl_cv_writable_argv in +yes) + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WRITABLE_ARGV 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; +no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; +*) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + { echo "$as_me:$LINENO: WARNING: the previous check could not be made default was used" >&5 +echo "$as_me: WARNING: the previous check could not be made default was used" >&2;} + ;; +esac + + + +# Check whether --with-krb4-includes or --without-krb4-includes was given. +if test "${with_krb4_includes+set}" = set; then + withval="$with_krb4_includes" + + CPPFLAGS="$CPPFLAGS -I$withval" + KRB4INC="$withval" + want_krb4=yes + +fi; + + +# Check whether --with-krb4-libs or --without-krb4-libs was given. +if test "${with_krb4_libs+set}" = set; then + withval="$with_krb4_libs" + + LDFLAGS="$LDFLAGS -L$withval" + KRB4LIB="$withval" + want_krb4=yes + +fi; + + +OPT_KRB4=off + +# Check whether --with-krb4 or --without-krb4 was given. +if test "${with_krb4+set}" = set; then + withval="$with_krb4" + + OPT_KRB4="$withval" + if test X"$OPT_KRB4" != Xyes + then + LDFLAGS="$LDFLAGS -L$OPT_KRB4/lib$libsuff" + KRB4LIB="$OPT_KRB4/lib$libsuff" + CPPFLAGS="$CPPFLAGS -I$OPT_KRB4/include" + KRB4INC="$OPT_KRB4/include" + fi + want_krb4="yes" + +fi; + +echo "$as_me:$LINENO: checking if Kerberos4 support is requested" >&5 +echo $ECHO_N "checking if Kerberos4 support is requested... $ECHO_C" >&6 + +if test "$want_krb4" = yes +then + if test "$ipv6" = "yes"; then + echo krb4 is not compatible with IPv6 + exit 1 + fi + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + + + echo "$as_me:$LINENO: checking where to look for Kerberos4" >&5 +echo $ECHO_N "checking where to look for Kerberos4... $ECHO_C" >&6 + if test X"$OPT_KRB4" = Xyes + then + echo "$as_me:$LINENO: result: defaults" >&5 +echo "${ECHO_T}defaults" >&6 + else + echo "$as_me:$LINENO: result: libs in $KRB4LIB, headers in $KRB4INC" >&5 +echo "${ECHO_T}libs in $KRB4LIB, headers in $KRB4INC" >&6 + fi + + echo "$as_me:$LINENO: checking for des_pcbc_encrypt in -ldes" >&5 +echo $ECHO_N "checking for des_pcbc_encrypt in -ldes... $ECHO_C" >&6 +if test "${ac_cv_lib_des_des_pcbc_encrypt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldes $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char des_pcbc_encrypt (); +int +main () +{ +des_pcbc_encrypt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_des_des_pcbc_encrypt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_des_des_pcbc_encrypt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_des_des_pcbc_encrypt" >&5 +echo "${ECHO_T}$ac_cv_lib_des_des_pcbc_encrypt" >&6 +if test $ac_cv_lib_des_des_pcbc_encrypt = yes; then + + +for ac_header in des.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + echo "$as_me:$LINENO: checking for res_search" >&5 +echo $ECHO_N "checking for res_search... $ECHO_C" >&6 +if test "${ac_cv_func_res_search+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define res_search to an innocuous variant, in case declares res_search. + For example, HP-UX 11i declares gettimeofday. */ +#define res_search innocuous_res_search + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char res_search (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef res_search + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char res_search (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_res_search) || defined (__stub___res_search) +choke me +#else +char (*f) () = res_search; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != res_search; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_res_search=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_res_search=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_res_search" >&5 +echo "${ECHO_T}$ac_cv_func_res_search" >&6 +if test $ac_cv_func_res_search = yes; then + : +else + +echo "$as_me:$LINENO: checking for res_search in -lresolv" >&5 +echo $ECHO_N "checking for res_search in -lresolv... $ECHO_C" >&6 +if test "${ac_cv_lib_resolv_res_search+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lresolv $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char res_search (); +int +main () +{ +res_search (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_resolv_res_search=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_resolv_res_search=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_resolv_res_search" >&5 +echo "${ECHO_T}$ac_cv_lib_resolv_res_search" >&6 +if test $ac_cv_lib_resolv_res_search = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRESOLV 1 +_ACEOF + + LIBS="-lresolv $LIBS" + +fi + +fi + + + echo "$as_me:$LINENO: checking for krb_net_read in -lkrb" >&5 +echo $ECHO_N "checking for krb_net_read in -lkrb... $ECHO_C" >&6 +if test "${ac_cv_lib_krb_krb_net_read+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lkrb $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char krb_net_read (); +int +main () +{ +krb_net_read (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_krb_krb_net_read=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_krb_krb_net_read=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_krb_krb_net_read" >&5 +echo "${ECHO_T}$ac_cv_lib_krb_krb_net_read" >&6 +if test $ac_cv_lib_krb_krb_net_read = yes; then + + +for ac_header in krb.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + LIBS="-lkrb -lcom_err -ldes $LIBS" + + +for ac_func in krb_get_our_ip_for_realm +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_KRB4 1 +_ACEOF + + + KRB4_ENABLED=1 + + + curl_krb4_msg="enabled" + + +for ac_func in strlcpy +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +fi + + +fi + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +# Check whether --with-spnego or --without-spnego was given. +if test "${with_spnego+set}" = set; then + withval="$with_spnego" + SPNEGO_ROOT="$withval" + want_spnego="yes" + +fi; +echo "$as_me:$LINENO: checking if SPNEGO support is requested" >&5 +echo $ECHO_N "checking if SPNEGO support is requested... $ECHO_C" >&6 +if test x"$want_spnego" = xyes; then + + if test X"$SPNEGO_ROOT" = Xyes; then + { { echo "$as_me:$LINENO: error: FBOpenSSL libs and/or directories were not found where specified!" >&5 +echo "$as_me: error: FBOpenSSL libs and/or directories were not found where specified!" >&2;} + { (exit 1); exit 1; }; } + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + else + if test -z "$SPNEGO_LIB_DIR"; then + LDFLAGS="$LDFLAGS -L$SPNEGO_ROOT -lfbopenssl" + else + LDFLAGS="$LDFLAGS $SPNEGO_LIB_DIR" + fi + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SPNEGO 1 +_ACEOF + + curl_spnego_msg="enabled" + fi +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +# Check whether --with-gssapi-includes or --without-gssapi-includes was given. +if test "${with_gssapi_includes+set}" = set; then + withval="$with_gssapi_includes" + GSSAPI_INCS="-I$withval" + want_gss="yes" + +fi; + + +# Check whether --with-gssapi-libs or --without-gssapi-libs was given. +if test "${with_gssapi_libs+set}" = set; then + withval="$with_gssapi_libs" + GSSAPI_LIBS="-L$withval -lgssapi" + want_gss="yes" + +fi; + + +# Check whether --with-gssapi or --without-gssapi was given. +if test "${with_gssapi+set}" = set; then + withval="$with_gssapi" + GSSAPI_ROOT="$withval" + want_gss="yes" + +fi; + +echo "$as_me:$LINENO: checking if GSSAPI support is requested" >&5 +echo $ECHO_N "checking if GSSAPI support is requested... $ECHO_C" >&6 +if test x"$want_gss" = xyes; then + if test -z "$GSSAPI_INCS"; then + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + GSSAPI_INCS=`$GSSAPI_ROOT/bin/krb5-config --cflags gssapi` + else + GSSAPI_INCS="-I$GSSAPI_ROOT/include" + fi + fi + CPPFLAGS="$CPPFLAGS $GSSAPI_INCS" + + if test -z "$GSSAPI_LIB_DIR"; then + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + gss_ldflags=`$GSSAPI_ROOT/bin/krb5-config --libs gssapi` + LDFLAGS="$LDFLAGS $gss_ldflags" + else + LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff -lgssapi" + fi + else + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + fi + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GSSAPI 1 +_ACEOF + + + curl_gss_msg="enabled" + + if test -n "$GSSAPI_INCS"; then + # cut off the preceding -I from the include path + GSSAPI_INCS=`echo $GSSAPI_INCS | sed -e s/^-I//g` + fi + + if test -f "$GSSAPI_INCS/gssapi.h"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GSSHEIMDAL 1 +_ACEOF + + else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GSSMIT 1 +_ACEOF + + fi + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + +OPT_SSL=off +ca="no" + +# Check whether --with-ssl or --without-ssl was given. +if test "${with_ssl+set}" = set; then + withval="$with_ssl" + OPT_SSL=$withval +fi; + +if test X"$OPT_SSL" = Xno +then + { echo "$as_me:$LINENO: WARNING: SSL disabled, you will not be able to use HTTPS, FTPS, NTLM and more" >&5 +echo "$as_me: WARNING: SSL disabled, you will not be able to use HTTPS, FTPS, NTLM and more" >&2;} +else + + CLEANLDFLAGS="$LDFLAGS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLIBS="$LIBS" + + case "$OPT_SSL" in + yes) + PKGTEST="yes" + EXTRA_SSL=/usr/local/ssl ;; + off) + PKGTEST="yes" + EXTRA_SSL= ;; + *) + PKGTEST="no" + EXTRA_SSL=$OPT_SSL + LDFLAGS="$LDFLAGS -L$EXTRA_SSL/lib$libsuff" + CPPFLAGS="$CPPFLAGS -I$EXTRA_SSL/include/openssl -I$EXTRA_SSL/include" + ;; + esac + + if test "$PKGTEST" = "yes"; then + + + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PKGCONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PKGCONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKGCONFIG="$PKGCONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/bin:/usr/local/bin" +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKGCONFIG="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_PKGCONFIG" && ac_cv_path_PKGCONFIG="no" + ;; +esac +fi +PKGCONFIG=$ac_cv_path_PKGCONFIG + +if test -n "$PKGCONFIG"; then + echo "$as_me:$LINENO: result: $PKGCONFIG" >&5 +echo "${ECHO_T}$PKGCONFIG" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + if test "$PKGCONFIG" != "no" ; then + echo "$as_me:$LINENO: checking OpenSSL options with pkg-config" >&5 +echo $ECHO_N "checking OpenSSL options with pkg-config... $ECHO_C" >&6 + + $PKGCONFIG --exists openssl + SSL_EXISTS=$? + + if test "$SSL_EXISTS" -eq "0"; then + SSL_LIBS=`$PKGCONFIG --libs-only-l openssl 2>/dev/null` + SSL_LDFLAGS=`$PKGCONFIG --libs-only-L openssl 2>/dev/null` + SSL_CPPFLAGS=`$PKGCONFIG --cflags-only-I openssl 2>/dev/null` + + LIBS="$LIBS $SSL_LIBS" + CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" + LDFLAGS="$LDFLAGS $SSL_LDFLAGS" + echo "$as_me:$LINENO: result: found" >&5 +echo "${ECHO_T}found" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + fi + fi + fi + + echo "$as_me:$LINENO: checking for CRYPTO_lock in -lcrypto" >&5 +echo $ECHO_N "checking for CRYPTO_lock in -lcrypto... $ECHO_C" >&6 +if test "${ac_cv_lib_crypto_CRYPTO_lock+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char CRYPTO_lock (); +int +main () +{ +CRYPTO_lock (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_crypto_CRYPTO_lock=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_crypto_CRYPTO_lock=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_CRYPTO_lock" >&5 +echo "${ECHO_T}$ac_cv_lib_crypto_CRYPTO_lock" >&6 +if test $ac_cv_lib_crypto_CRYPTO_lock = yes; then + + HAVECRYPTO="yes" + +else + + LDFLAGS="$CLEANLDFLAGS -L$EXTRA_SSL/lib$libsuff" + CPPFLAGS="$CLEANCPPFLAGS -I$EXTRA_SSL/include/openssl -I$EXTRA_SSL/include" + echo "$as_me:$LINENO: checking for CRYPTO_add_lock in -lcrypto" >&5 +echo $ECHO_N "checking for CRYPTO_add_lock in -lcrypto... $ECHO_C" >&6 +if test "${ac_cv_lib_crypto_CRYPTO_add_lock+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char CRYPTO_add_lock (); +int +main () +{ +CRYPTO_add_lock (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_crypto_CRYPTO_add_lock=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_crypto_CRYPTO_add_lock=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_CRYPTO_add_lock" >&5 +echo "${ECHO_T}$ac_cv_lib_crypto_CRYPTO_add_lock" >&6 +if test $ac_cv_lib_crypto_CRYPTO_add_lock = yes; then + + HAVECRYPTO="yes" +else + + LDFLAGS="$CLEANLDFLAGS" + CPPFLAGS="$CLEANCPPFLAGS" + LIBS="$CLEANLIBS" + +fi + + +fi + + + + if test "$HAVECRYPTO" = "yes"; then + + echo "$as_me:$LINENO: checking for gdi32" >&5 +echo $ECHO_N "checking for gdi32... $ECHO_C" >&6 + my_ac_save_LIBS=$LIBS + LIBS="-lgdi32 $LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + #include +int +main () +{ +GdiFlush(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + LIBS=$my_ac_save_LIBS + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +echo "$as_me:$LINENO: checking for CRYPTO_add_lock in -lcrypto" >&5 +echo $ECHO_N "checking for CRYPTO_add_lock in -lcrypto... $ECHO_C" >&6 +if test "${ac_cv_lib_crypto_CRYPTO_add_lock+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcrypto $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char CRYPTO_add_lock (); +int +main () +{ +CRYPTO_add_lock (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_crypto_CRYPTO_add_lock=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_crypto_CRYPTO_add_lock=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_CRYPTO_add_lock" >&5 +echo "${ECHO_T}$ac_cv_lib_crypto_CRYPTO_add_lock" >&6 +if test $ac_cv_lib_crypto_CRYPTO_add_lock = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCRYPTO 1 +_ACEOF + + LIBS="-lcrypto $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for SSL_connect in -lssl" >&5 +echo $ECHO_N "checking for SSL_connect in -lssl... $ECHO_C" >&6 +if test "${ac_cv_lib_ssl_SSL_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lssl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char SSL_connect (); +int +main () +{ +SSL_connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ssl_SSL_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ssl_SSL_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_ssl_SSL_connect" >&6 +if test $ac_cv_lib_ssl_SSL_connect = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSSL 1 +_ACEOF + + LIBS="-lssl $LIBS" + +fi + + + if test "$ac_cv_lib_ssl_SSL_connect" != yes; then + echo "$as_me:$LINENO: checking for ssl with RSAglue/rsaref libs in use" >&5 +echo $ECHO_N "checking for ssl with RSAglue/rsaref libs in use... $ECHO_C" >&6; + OLIBS=$LIBS + LIBS="$LIBS -lRSAglue -lrsaref" + +echo "$as_me:$LINENO: checking for SSL_connect in -lssl" >&5 +echo $ECHO_N "checking for SSL_connect in -lssl... $ECHO_C" >&6 +if test "${ac_cv_lib_ssl_SSL_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lssl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char SSL_connect (); +int +main () +{ +SSL_connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ssl_SSL_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ssl_SSL_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ssl_SSL_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_ssl_SSL_connect" >&6 +if test $ac_cv_lib_ssl_SSL_connect = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSSL 1 +_ACEOF + + LIBS="-lssl $LIBS" + +fi + + if test "$ac_cv_lib_ssl_SSL_connect" != yes; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + LIBS=$OLIBS + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + fi + fi + + + + + + + + +for ac_header in openssl/x509.h openssl/rsa.h openssl/crypto.h \ + openssl/pem.h openssl/ssl.h openssl/err.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + curl_ssl_msg="enabled" + OPENSSL_ENABLED=1 +fi + +done + + + if test $ac_cv_header_openssl_x509_h = no; then + + + + + + +for ac_header in x509.h rsa.h crypto.h pem.h ssl.h err.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + curl_ssl_msg="enabled" + OPENSSL_ENABLED=1 +fi + +done + + fi + + echo "$as_me:$LINENO: checking for ENGINE_init" >&5 +echo $ECHO_N "checking for ENGINE_init... $ECHO_C" >&6 +if test "${ac_cv_func_ENGINE_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define ENGINE_init to an innocuous variant, in case declares ENGINE_init. + For example, HP-UX 11i declares gettimeofday. */ +#define ENGINE_init innocuous_ENGINE_init + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char ENGINE_init (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef ENGINE_init + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char ENGINE_init (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_ENGINE_init) || defined (__stub___ENGINE_init) +choke me +#else +char (*f) () = ENGINE_init; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != ENGINE_init; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_ENGINE_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_ENGINE_init=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_ENGINE_init" >&5 +echo "${ECHO_T}$ac_cv_func_ENGINE_init" >&6 +if test $ac_cv_func_ENGINE_init = yes; then + +for ac_header in openssl/engine.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +fi + + + + + echo "$as_me:$LINENO: checking CA cert bundle install path" >&5 +echo $ECHO_N "checking CA cert bundle install path... $ECHO_C" >&6 + + +# Check whether --with-ca-bundle or --without-ca-bundle was given. +if test "${with_ca_bundle+set}" = set; then + withval="$with_ca_bundle" + ca="$withval" +else + + if test "x$prefix" != xNONE; then + ca="\${prefix}/share/curl/curl-ca-bundle.crt" + else + ca="$ac_default_prefix/share/curl/curl-ca-bundle.crt" + fi + +fi; + + if test X"$OPT_SSL" = Xno; then + ca="no" + fi + + if test "x$ca" != "xno"; then + CURL_CA_BUNDLE='"'$ca'"' + + fi + echo "$as_me:$LINENO: result: $ca" >&5 +echo "${ECHO_T}$ca" >&6 + + + + + + +for ac_func in RAND_status \ + RAND_screen \ + RAND_egd \ + CRYPTO_cleanup_all_ex_data +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + fi + + if test X"$OPT_SSL" != Xoff && + test "$OPENSSL_ENABLED" != "1"; then + { { echo "$as_me:$LINENO: error: OpenSSL libs and/or directories were not found where specified!" >&5 +echo "$as_me: error: OpenSSL libs and/or directories were not found where specified!" >&2;} + { (exit 1); exit 1; }; } + fi + +fi + + + +if test x$ca != xno; then + CABUNDLE_TRUE= + CABUNDLE_FALSE='#' +else + CABUNDLE_TRUE='#' + CABUNDLE_FALSE= +fi + + + + +_cppflags=$CPPFLAGS +_ldflags=$LDFLAGS +OPT_ZLIB="/usr/local" + +# Check whether --with-zlib or --without-zlib was given. +if test "${with_zlib+set}" = set; then + withval="$with_zlib" + OPT_ZLIB="$withval" +fi; + +case "$OPT_ZLIB" in + no) + { echo "$as_me:$LINENO: WARNING: zlib disabled" >&5 +echo "$as_me: WARNING: zlib disabled" >&2;} ;; + *) + + echo "$as_me:$LINENO: checking for inflateEnd in -lz" >&5 +echo $ECHO_N "checking for inflateEnd in -lz... $ECHO_C" >&6 +if test "${ac_cv_lib_z_inflateEnd+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char inflateEnd (); +int +main () +{ +inflateEnd (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_z_inflateEnd=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_z_inflateEnd=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_z_inflateEnd" >&5 +echo "${ECHO_T}$ac_cv_lib_z_inflateEnd" >&6 +if test $ac_cv_lib_z_inflateEnd = yes; then + HAVE_LIBZ="1" +else + if test -d "$OPT_ZLIB"; then + CPPFLAGS="$CPPFLAGS -I$OPT_ZLIB/include" + LDFLAGS="$LDFLAGS -L$OPT_ZLIB/lib$libsuff" + fi +fi + + + if test "${ac_cv_header_zlib_h+set}" = set; then + echo "$as_me:$LINENO: checking for zlib.h" >&5 +echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6 +if test "${ac_cv_header_zlib_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5 +echo "${ECHO_T}$ac_cv_header_zlib_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking zlib.h usability" >&5 +echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking zlib.h presence" >&5 +echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for zlib.h" >&5 +echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6 +if test "${ac_cv_header_zlib_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_zlib_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5 +echo "${ECHO_T}$ac_cv_header_zlib_h" >&6 + +fi +if test $ac_cv_header_zlib_h = yes; then + + HAVE_ZLIB_H="1" + if test "$HAVE_LIBZ" != "1"; then + echo "$as_me:$LINENO: checking for gzread in -lz" >&5 +echo $ECHO_N "checking for gzread in -lz... $ECHO_C" >&6 +if test "${ac_cv_lib_z_gzread+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gzread (); +int +main () +{ +gzread (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_z_gzread=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_z_gzread=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_z_gzread" >&5 +echo "${ECHO_T}$ac_cv_lib_z_gzread" >&6 +if test $ac_cv_lib_z_gzread = yes; then + + HAVE_LIBZ="1" + +else + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags +fi + + fi + +else + + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + +fi + + + + if test "$HAVE_LIBZ" = "1" && test "$HAVE_ZLIB_H" != "1" + then + { echo "$as_me:$LINENO: WARNING: configure found only the libz lib, not the header file!" >&5 +echo "$as_me: WARNING: configure found only the libz lib, not the header file!" >&2;} + elif test "$HAVE_LIBZ" != "1" && test "$HAVE_ZLIB_H" = "1" + then + { echo "$as_me:$LINENO: WARNING: configure found only the libz header file, not the lib!" >&5 +echo "$as_me: WARNING: configure found only the libz header file, not the lib!" >&2;} + elif test "$HAVE_LIBZ" = "1" && test "$HAVE_ZLIB_H" = "1" + then + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ZLIB_H 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + + LIBS="$LIBS -lz" + + AMFIXLIB="1" + { echo "$as_me:$LINENO: found both libz and libz.h header" >&5 +echo "$as_me: found both libz and libz.h header" >&6;} + curl_zlib_msg="enabled" + fi + ;; +esac + + + +if test x"$AMFIXLIB" = x1; then + HAVE_LIBZ_TRUE= + HAVE_LIBZ_FALSE='#' +else + HAVE_LIBZ_TRUE='#' + HAVE_LIBZ_FALSE= +fi + + +echo "$as_me:$LINENO: checking whether to build with libidn" >&5 +echo $ECHO_N "checking whether to build with libidn... $ECHO_C" >&6 + +# Check whether --with-libidn or --without-libidn was given. +if test "${with_libidn+set}" = set; then + withval="$with_libidn" + LIBIDN="$withval" +fi; + +case "$LIBIDN" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + + idn="" + if test "x$LIBIDN" != "xyes"; then + oldLDFLAGS=$LDFLAGS + oldCPPFLAGS=$CPPFLAGS + LDFLAGS="$LDFLAGS -L$LIBIDN/lib" + CPPFLAGS="$CPPFLAGS -I$LIBIDN/include" + idn="yes" + +echo "$as_me:$LINENO: checking for idna_to_ascii_4i in -lidn" >&5 +echo $ECHO_N "checking for idna_to_ascii_4i in -lidn... $ECHO_C" >&6 +if test "${ac_cv_lib_idn_idna_to_ascii_4i+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lidn $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char idna_to_ascii_4i (); +int +main () +{ +idna_to_ascii_4i (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_idn_idna_to_ascii_4i=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_idn_idna_to_ascii_4i=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_idn_idna_to_ascii_4i" >&5 +echo "${ECHO_T}$ac_cv_lib_idn_idna_to_ascii_4i" >&6 +if test $ac_cv_lib_idn_idna_to_ascii_4i = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBIDN 1 +_ACEOF + + LIBS="-lidn $LIBS" + +else + idn="" + LDFLAGS=$oldLDFLAGS + CPPFLAGS=$oldCPPFLAGS +fi + + fi + + if test "x$idn" != "xyes"; then + +echo "$as_me:$LINENO: checking for idna_to_ascii_lz in -lidn" >&5 +echo $ECHO_N "checking for idna_to_ascii_lz in -lidn... $ECHO_C" >&6 +if test "${ac_cv_lib_idn_idna_to_ascii_lz+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lidn $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char idna_to_ascii_lz (); +int +main () +{ +idna_to_ascii_lz (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_idn_idna_to_ascii_lz=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_idn_idna_to_ascii_lz=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_idn_idna_to_ascii_lz" >&5 +echo "${ECHO_T}$ac_cv_lib_idn_idna_to_ascii_lz" >&6 +if test $ac_cv_lib_idn_idna_to_ascii_lz = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBIDN 1 +_ACEOF + + LIBS="-lidn $LIBS" + +else + idn="" +fi + + fi + + if test "x$idn" = "xyes"; then + curl_idn_msg="enabled" + + + +for ac_func in idn_free idna_strerror tld_strerror +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +for ac_header in idn-free.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + fi + + ;; +esac + +OPT_THREAD=on + +echo "$as_me:$LINENO: checking AIX 4.3 or later" >&5 +echo $ECHO_N "checking AIX 4.3 or later... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF + +#if defined(_AIX) && defined(_AIX43) +printf("just fine"); +#else +#error "this is not AIX 4.3 or later" +#endif + +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + RECENTAIX=yes + OPT_THREAD=off +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_ext + +# Check whether --enable-thread or --disable-thread was given. +if test "${enable_thread+set}" = set; then + enableval="$enable_thread" + case "$enableval" in + no) + OPT_THREAD=off + { echo "$as_me:$LINENO: WARNING: libcurl will not get built using thread-safe functions" >&5 +echo "$as_me: WARNING: libcurl will not get built using thread-safe functions" >&2;} + ;; + *) + ;; + esac + + +fi; + +if test X"$OPT_THREAD" = Xoff +then + +cat >>confdefs.h <<\_ACEOF +#define DISABLED_THREADSAFE 1 +_ACEOF + +else + if test "$ipv6" != "yes"; then + + +for ac_func in gethostbyname_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking if gethostbyname_r takes 3 arguments" >&5 +echo $ECHO_N "checking if gethostbyname_r takes 3 arguments... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *, struct hostent *, struct hostent_data *); +int +main () +{ + +struct hostent_data data; +gethostbyname_r(NULL, NULL, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETHOSTBYNAME_R_3 1 +_ACEOF + + ac_cv_gethostbyname_args=3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking if gethostbyname_r with -D_REENTRANT takes 3 arguments" >&5 +echo $ECHO_N "checking if gethostbyname_r with -D_REENTRANT takes 3 arguments... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _REENTRANT + +#include +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *,struct hostent *, struct hostent_data *); +int +main () +{ + +struct hostent_data data; +gethostbyname_r(NULL, NULL, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETHOSTBYNAME_R_3 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define NEED_REENTRANT 1 +_ACEOF + + ac_cv_gethostbyname_args=3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking if gethostbyname_r takes 5 arguments" >&5 +echo $ECHO_N "checking if gethostbyname_r takes 5 arguments... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#undef NULL +#define NULL (void *)0 + +struct hostent * +gethostbyname_r(const char *, struct hostent *, char *, int, int *); +int +main () +{ + +gethostbyname_r(NULL, NULL, NULL, 0, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETHOSTBYNAME_R_5 1 +_ACEOF + + ac_cv_gethostbyname_args=5 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking if gethostbyname_r takes 6 arguments" >&5 +echo $ECHO_N "checking if gethostbyname_r takes 6 arguments... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#undef NULL +#define NULL (void *)0 + +int +gethostbyname_r(const char *, struct hostent *, char *, size_t, +struct hostent **, int *); +int +main () +{ + +gethostbyname_r(NULL, NULL, NULL, 0, NULL, NULL); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GETHOSTBYNAME_R_6 1 +_ACEOF + + ac_cv_gethostbyname_args=6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + have_missing_r_funcs="$have_missing_r_funcs gethostbyname_r" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +done + + +if test "$ac_cv_func_gethostbyname_r" = "yes"; then + if test "$ac_cv_gethostbyname_args" = "0"; then + { { echo "$as_me:$LINENO: error: couldn't figure out how to use gethostbyname_r()" >&5 +echo "$as_me: error: couldn't figure out how to use gethostbyname_r()" >&2;} + { (exit 1); exit 1; }; } + fi +fi + + + fi + + + +for ac_func in inet_ntoa_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking whether inet_ntoa_r is declared" >&5 +echo $ECHO_N "checking whether inet_ntoa_r is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "inet_ntoa_r" >/dev/null 2>&1; then + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_INET_NTOA_R_DECL 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking whether inet_ntoa_r with -D_REENTRANT is declared" >&5 +echo $ECHO_N "checking whether inet_ntoa_r with -D_REENTRANT is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _REENTRANT +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "inet_ntoa_r" >/dev/null 2>&1; then + + +cat >>confdefs.h <<\_ACEOF +#define HAVE_INET_NTOA_R_DECL 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define NEED_REENTRANT 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +fi +rm -f conftest* + +fi +done + + + + + +for ac_func in localtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking whether localtime_r is declared" >&5 +echo $ECHO_N "checking whether localtime_r is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "localtime_r" >/dev/null 2>&1; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking whether localtime_r with -D_REENTRANT is declared" >&5 +echo $ECHO_N "checking whether localtime_r with -D_REENTRANT is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _REENTRANT +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "localtime_r" >/dev/null 2>&1; then + + cat >>confdefs.h <<\_ACEOF +#define NEED_REENTRANT 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +fi +rm -f conftest* + +fi +done + + + + + +for ac_func in strerror_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking whether strerror_r is declared" >&5 +echo $ECHO_N "checking whether strerror_r is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "strerror_r" >/dev/null 2>&1; then + + strerror_r="yes" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking whether strerror_r with -D_THREAD_SAFE is declared" >&5 +echo $ECHO_N "checking whether strerror_r with -D_THREAD_SAFE is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _THREAD_SAFE +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "strerror_r" >/dev/null 2>&1; then + + strerror_r="yes" + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +fi +rm -f conftest* + +fi +done + + + if test "x$strerror_r" = "xyes"; then + + echo "$as_me:$LINENO: checking whether strerror_r is declared" >&5 +echo $ECHO_N "checking whether strerror_r is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_strerror_r+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + + +int +main () +{ +#ifndef strerror_r + char *p = (char *) strerror_r; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_strerror_r=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_strerror_r=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_strerror_r" >&5 +echo "${ECHO_T}$ac_cv_have_decl_strerror_r" >&6 +if test $ac_cv_have_decl_strerror_r = yes; then + : +else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_NO_STRERROR_R_DECL 1 +_ACEOF + + +fi + + + echo "$as_me:$LINENO: checking for a glibc strerror_r API" >&5 +echo $ECHO_N "checking for a glibc strerror_r API... $ECHO_C" >&6 + if test "$cross_compiling" = yes; then + { echo "$as_me:$LINENO: cannot determine strerror_r() style: edit lib/config.h manually!" >&5 +echo "$as_me: cannot determine strerror_r() style: edit lib/config.h manually!" >&6;} + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + char *string = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* this should've returned a string */ + if(!string || !string[0]) + return 99; + return 0; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + GLIBC_STRERROR_R="1" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GLIBC_STRERROR_R 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + if test -z "$GLIBC_STRERROR_R"; then + + echo "$as_me:$LINENO: checking for a POSIX strerror_r API" >&5 +echo $ECHO_N "checking for a POSIX strerror_r API... $ECHO_C" >&6 + if test "$cross_compiling" = yes; then + { echo "$as_me:$LINENO: cannot determine strerror_r() style: edit lib/config.h manually!" >&5 +echo "$as_me: cannot determine strerror_r() style: edit lib/config.h manually!" >&6;} + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + int error = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* This should've returned zero, and written an error string in the + buffer.*/ + if(!buffer[0] || error) + return 99; + return 0; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_POSIX_STRERROR_R 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + fi + fi + + + +for ac_func in gmtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +fi + + + +if test "x$RECENTAIX" = "xyes"; then + + +cat >>confdefs.h <<\_ACEOF +#define _THREAD_SAFE 1 +_ACEOF + + + echo "$as_me:$LINENO: checking if this is the xlc compiler" >&5 +echo $ECHO_N "checking if this is the xlc compiler... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +__xlC__ +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "^__xlC__" >/dev/null 2>&1; then + XLC="no" + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +else + XLC="yes" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + CFLAGS="$CFLAGS -qthreaded" + +fi +rm -f conftest* + + + + + +for ac_func in localtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking whether localtime_r is declared" >&5 +echo $ECHO_N "checking whether localtime_r is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "localtime_r" >/dev/null 2>&1; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking whether localtime_r with -D_REENTRANT is declared" >&5 +echo $ECHO_N "checking whether localtime_r with -D_REENTRANT is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _REENTRANT +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "localtime_r" >/dev/null 2>&1; then + + cat >>confdefs.h <<\_ACEOF +#define NEED_REENTRANT 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +fi +rm -f conftest* + +fi +done + + + + + +for ac_func in strerror_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + echo "$as_me:$LINENO: checking whether strerror_r is declared" >&5 +echo $ECHO_N "checking whether strerror_r is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "strerror_r" >/dev/null 2>&1; then + + strerror_r="yes" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + echo "$as_me:$LINENO: checking whether strerror_r with -D_THREAD_SAFE is declared" >&5 +echo $ECHO_N "checking whether strerror_r with -D_THREAD_SAFE is declared... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#define _THREAD_SAFE +#include +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "strerror_r" >/dev/null 2>&1; then + + strerror_r="yes" + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest* + +fi +rm -f conftest* + +fi +done + + + if test "x$strerror_r" = "xyes"; then + + echo "$as_me:$LINENO: checking whether strerror_r is declared" >&5 +echo $ECHO_N "checking whether strerror_r is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_strerror_r+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + + +int +main () +{ +#ifndef strerror_r + char *p = (char *) strerror_r; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_strerror_r=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_strerror_r=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_strerror_r" >&5 +echo "${ECHO_T}$ac_cv_have_decl_strerror_r" >&6 +if test $ac_cv_have_decl_strerror_r = yes; then + : +else + +cat >>confdefs.h <<\_ACEOF +#define HAVE_NO_STRERROR_R_DECL 1 +_ACEOF + + +fi + + + echo "$as_me:$LINENO: checking for a glibc strerror_r API" >&5 +echo $ECHO_N "checking for a glibc strerror_r API... $ECHO_C" >&6 + if test "$cross_compiling" = yes; then + { echo "$as_me:$LINENO: cannot determine strerror_r() style: edit lib/config.h manually!" >&5 +echo "$as_me: cannot determine strerror_r() style: edit lib/config.h manually!" >&6;} + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + char *string = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* this should've returned a string */ + if(!string || !string[0]) + return 99; + return 0; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + GLIBC_STRERROR_R="1" + +cat >>confdefs.h <<\_ACEOF +#define HAVE_GLIBC_STRERROR_R 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + if test -z "$GLIBC_STRERROR_R"; then + + echo "$as_me:$LINENO: checking for a POSIX strerror_r API" >&5 +echo $ECHO_N "checking for a POSIX strerror_r API... $ECHO_C" >&6 + if test "$cross_compiling" = yes; then + { echo "$as_me:$LINENO: cannot determine strerror_r() style: edit lib/config.h manually!" >&5 +echo "$as_me: cannot determine strerror_r() style: edit lib/config.h manually!" >&6;} + +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +int +main () { + char buffer[1024]; /* big enough to play with */ + int error = + strerror_r(EACCES, buffer, sizeof(buffer)); + /* This should've returned zero, and written an error string in the + buffer.*/ + if(!buffer[0] || error) + return 99; + return 0; +} + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_POSIX_STRERROR_R 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + fi + fi + + + +for ac_func in gmtime_r +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +fi + + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +for ac_header in sys/types.h \ + sys/time.h \ + sys/select.h \ + sys/socket.h \ + sys/ioctl.h \ + assert.h \ + unistd.h \ + malloc.h \ + stdlib.h \ + limits.h \ + arpa/inet.h \ + net/if.h \ + netinet/in.h \ + netinet/tcp.h \ + netdb.h \ + sys/sockio.h \ + sys/stat.h \ + sys/param.h \ + termios.h \ + termio.h \ + sgtty.h \ + fcntl.h \ + dlfcn.h \ + alloca.h \ + winsock.h \ + time.h \ + io.h \ + pwd.h \ + utime.h \ + sys/utime.h \ + sys/poll.h \ + libgen.h \ + setjmp.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif + + + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for curl_off_t" >&5 +echo $ECHO_N "checking for curl_off_t... $ECHO_C" >&6 +if test "${ac_cv_type_curl_off_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +if ((curl_off_t *) 0) + return 0; +if (sizeof (curl_off_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_curl_off_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_curl_off_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_curl_off_t" >&5 +echo "${ECHO_T}$ac_cv_type_curl_off_t" >&6 + +echo "$as_me:$LINENO: checking size of curl_off_t" >&5 +echo $ECHO_N "checking size of curl_off_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_curl_off_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_curl_off_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (curl_off_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (curl_off_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (curl_off_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (curl_off_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (curl_off_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_curl_off_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (curl_off_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (curl_off_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include "$srcdir/include/curl/curl.h" + + +long longval () { return (long) (sizeof (curl_off_t)); } +unsigned long ulongval () { return (long) (sizeof (curl_off_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (curl_off_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (curl_off_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (curl_off_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_curl_off_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (curl_off_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (curl_off_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_curl_off_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_curl_off_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_curl_off_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_CURL_OFF_T $ac_cv_sizeof_curl_off_t +_ACEOF + + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 + +echo "$as_me:$LINENO: checking size of size_t" >&5 +echo $ECHO_N "checking size of size_t... $ECHO_C" >&6 +if test "${ac_cv_sizeof_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_size_t" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (size_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_size_t=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (size_t)); } +unsigned long ulongval () { return (long) (sizeof (size_t)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (size_t))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (size_t)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (size_t)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_size_t=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (size_t), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_size_t=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_size_t" >&5 +echo "${ECHO_T}$ac_cv_sizeof_size_t" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t +_ACEOF + + +echo "$as_me:$LINENO: checking for long" >&5 +echo $ECHO_N "checking for long... $ECHO_C" >&6 +if test "${ac_cv_type_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long *) 0) + return 0; +if (sizeof (long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long" >&5 +echo "${ECHO_T}$ac_cv_type_long" >&6 + +echo "$as_me:$LINENO: checking size of long" >&5 +echo $ECHO_N "checking size of long... $ECHO_C" >&6 +if test "${ac_cv_sizeof_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$ac_cv_type_long" = yes; then + # The cast to unsigned long works around a bug in the HP C Compiler + # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects + # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. + # This bug is HP SR number 8606223364. + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_lo=$ac_mid; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo= ac_hi= +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long) (sizeof (long))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_hi=$ac_mid +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_lo=`expr '(' $ac_mid ')' + 1` +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_long=$ac_lo;; +'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } ;; +esac +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +long longval () { return (long) (sizeof (long)); } +unsigned long ulongval () { return (long) (sizeof (long)); } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + exit (1); + if (((long) (sizeof (long))) < 0) + { + long i = longval (); + if (i != ((long) (sizeof (long)))) + exit (1); + fprintf (f, "%ld\n", i); + } + else + { + unsigned long i = ulongval (); + if (i != ((long) (sizeof (long)))) + exit (1); + fprintf (f, "%lu\n", i); + } + exit (ferror (f) || fclose (f) != 0); + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_long=`cat conftest.val` +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +{ { echo "$as_me:$LINENO: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute sizeof (long), 77 +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.val +else + ac_cv_sizeof_long=0 +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_sizeof_long" >&5 +echo "${ECHO_T}$ac_cv_sizeof_long" >&6 +cat >>confdefs.h <<_ACEOF +#define SIZEOF_LONG $ac_cv_sizeof_long +_ACEOF + + + +echo "$as_me:$LINENO: checking for long long" >&5 +echo $ECHO_N "checking for long long... $ECHO_C" >&6 +if test "${ac_cv_type_long_long+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((long long *) 0) + return 0; +if (sizeof (long long)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_long_long=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_long_long=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_long_long" >&5 +echo "${ECHO_T}$ac_cv_type_long_long" >&6 +if test $ac_cv_type_long_long = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LONGLONG 1 +_ACEOF + + longlong="yes" + +fi + + +if test "xyes" = "x$longlong"; then + echo "$as_me:$LINENO: checking if numberLL works" >&5 +echo $ECHO_N "checking if numberLL works... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +long long val = 1000LL; +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LL 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + +# check for ssize_t +echo "$as_me:$LINENO: checking for ssize_t" >&5 +echo $ECHO_N "checking for ssize_t... $ECHO_C" >&6 +if test "${ac_cv_type_ssize_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((ssize_t *) 0) + return 0; +if (sizeof (ssize_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_ssize_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_ssize_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_ssize_t" >&5 +echo "${ECHO_T}$ac_cv_type_ssize_t" >&6 +if test $ac_cv_type_ssize_t = yes; then + : +else + +cat >>confdefs.h <<\_ACEOF +#define ssize_t int +_ACEOF + +fi + + + + echo "$as_me:$LINENO: checking for socklen_t" >&5 +echo $ECHO_N "checking for socklen_t... $ECHO_C" >&6 +if test "${ac_cv_type_socklen_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +if ((socklen_t *) 0) + return 0; +if (sizeof (socklen_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_socklen_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_socklen_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_socklen_t" >&5 +echo "${ECHO_T}$ac_cv_type_socklen_t" >&6 +if test $ac_cv_type_socklen_t = yes; then + : +else + + echo "$as_me:$LINENO: checking for socklen_t equivalent" >&5 +echo $ECHO_N "checking for socklen_t equivalent... $ECHO_C" >&6 + if test "${curl_cv_socklen_t_equiv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + curl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t unsigned long "unsigned long"; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + + int getpeername (int, $arg2 *, $t *); + +int +main () +{ + + $t len; + getpeername(0,0,&len); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + curl_cv_socklen_t_equiv="$t" + break + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + done + + if test "x$curl_cv_socklen_t_equiv" = x; then + { { echo "$as_me:$LINENO: error: Cannot find a type to use in place of socklen_t" >&5 +echo "$as_me: error: Cannot find a type to use in place of socklen_t" >&2;} + { (exit 1); exit 1; }; } + fi + +fi + + echo "$as_me:$LINENO: result: $curl_cv_socklen_t_equiv" >&5 +echo "${ECHO_T}$curl_cv_socklen_t_equiv" >&6 + +cat >>confdefs.h <<_ACEOF +#define socklen_t $curl_cv_socklen_t_equiv +_ACEOF + +fi + + + + echo "$as_me:$LINENO: checking for in_addr_t" >&5 +echo $ECHO_N "checking for in_addr_t... $ECHO_C" >&6 +if test "${ac_cv_type_in_addr_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((in_addr_t *) 0) + return 0; +if (sizeof (in_addr_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_in_addr_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_in_addr_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_in_addr_t" >&5 +echo "${ECHO_T}$ac_cv_type_in_addr_t" >&6 +if test $ac_cv_type_in_addr_t = yes; then + : +else + + echo "$as_me:$LINENO: checking for in_addr_t equivalent" >&5 +echo $ECHO_N "checking for in_addr_t equivalent... $ECHO_C" >&6 + if test "${curl_cv_in_addr_t_equiv+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + curl_cv_in_addr_t_equiv= + for t in "unsigned long" int size_t unsigned long; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #ifdef HAVE_SYS_TYPES_H + #include + #endif + #ifdef HAVE_SYS_SOCKET_H + #include + #endif + #ifdef HAVE_ARPA_INET_H + #include + #endif + +int +main () +{ + + $t data = inet_addr ("1.2.3.4"); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + curl_cv_in_addr_t_equiv="$t" + break + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + + if test "x$curl_cv_in_addr_t_equiv" = x; then + { { echo "$as_me:$LINENO: error: Cannot find a type to use in place of in_addr_t" >&5 +echo "$as_me: error: Cannot find a type to use in place of in_addr_t" >&2;} + { (exit 1); exit 1; }; } + fi + +fi + + echo "$as_me:$LINENO: result: $curl_cv_in_addr_t_equiv" >&5 +echo "${ECHO_T}$curl_cv_in_addr_t_equiv" >&6 + +cat >>confdefs.h <<_ACEOF +#define in_addr_t $curl_cv_in_addr_t_equiv +_ACEOF + +fi + + + + + +for ac_header in sys/select.h sys/socket.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------------------------------------ ## +## Report this to a suitable curl mailing list => http://curl.haxx.se/mail/ ## +## ------------------------------------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking types of arguments for select" >&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG1 $1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG234 ($2) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG5 ($3) +_ACEOF + +rm -f conftest* + + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in strtoll \ + socket \ + select \ + strdup \ + strstr \ + strtok_r \ + strftime \ + uname \ + strcasecmp \ + stricmp \ + strcmpi \ + gethostbyaddr \ + gettimeofday \ + inet_addr \ + inet_ntoa \ + inet_pton \ + tcsetattr \ + tcgetattr \ + perror \ + closesocket \ + siginterrupt \ + sigaction \ + signal \ + getpass_r \ + strlcat \ + getpwuid \ + geteuid \ + dlopen \ + utime \ + sigsetjmp \ + basename \ + poll +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + func="$ac_func" + echo "$as_me:$LINENO: checking deeper for $func" >&5 +echo $ECHO_N "checking deeper for $func... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + $func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes!" >&5 +echo "${ECHO_T}yes!" >&6 + eval "ac_cv_func_$func=yes" + def=`echo "HAVE_$func" | tr 'a-z' 'A-Z'` + +cat >>confdefs.h <<_ACEOF +#define $def 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: but still no" >&5 +echo "${ECHO_T}but still no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + +fi +done + + +if test "$ac_cv_func_sigsetjmp" != "yes"; then + echo "$as_me:$LINENO: checking for sigsetjmp defined as macro" >&5 +echo $ECHO_N "checking for sigsetjmp defined as macro... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +sigjmp_buf jmpenv; + sigsetjmp(jmpenv, 1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SIGSETJMP 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +echo "$as_me:$LINENO: checking whether basename is declared" >&5 +echo $ECHO_N "checking whether basename is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_basename+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif + + +int +main () +{ +#ifndef basename + char *p = (char *) basename; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_basename=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_basename=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_basename" >&5 +echo "${ECHO_T}$ac_cv_have_decl_basename" >&6 +if test $ac_cv_have_decl_basename = yes; then + : +else + +cat >>confdefs.h <<\_ACEOF +#define NEED_BASENAME_PROTO 1 +_ACEOF + +fi + + +if test "$ac_cv_func_poll" = "yes"; then + echo "$as_me:$LINENO: checking if poll works with NULL inputs" >&5 +echo $ECHO_N "checking if poll works with NULL inputs... $ECHO_C" >&6 + if test "$cross_compiling" = yes; then + echo "$as_me:$LINENO: result: cross-compiling assumes yes" >&5 +echo "${ECHO_T}cross-compiling assumes yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_POLL_FINE 1 +_ACEOF + + +else + cat >conftest.$ac_ext <<_ACEOF + +#ifdef HAVE_SYS_POLL_H +#include +#endif + + int main(void) + { + /* make this return 0 == timeout since there's nothing to read from */ + return poll((void *)0, 0, 10 /*ms*/); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define HAVE_POLL_FINE 1 +_ACEOF + +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi fi + + + +# Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PERL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PERL in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/local/bin/perl:/usr/bin/:/usr/local/bin " +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +PERL=$ac_cv_path_PERL + +if test -n "$PERL"; then + echo "$as_me:$LINENO: result: $PERL" >&5 +echo "${ECHO_T}$PERL" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +for ac_prog in gnroff nroff +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_NROFF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $NROFF in + [\\/]* | ?:[\\/]*) + ac_cv_path_NROFF="$NROFF" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_dummy="$PATH:/usr/bin/:/usr/local/bin " +for as_dir in $as_dummy +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_NROFF="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +NROFF=$ac_cv_path_NROFF + +if test -n "$NROFF"; then + echo "$as_me:$LINENO: result: $NROFF" >&5 +echo "${ECHO_T}$NROFF" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$NROFF" && break +done + + + +if test -n "$NROFF"; then + + echo "$as_me:$LINENO: checking how to use *nroff to get plain text from man pages" >&5 +echo $ECHO_N "checking how to use *nroff to get plain text from man pages... $ECHO_C" >&6 + MANOPT="-man" + mancheck=`echo foo | $NROFF $MANOPT 2>/dev/null` + if test -z "$mancheck"; then + MANOPT="-mandoc" + mancheck=`echo foo | $NROFF $MANOPT 2>/dev/null` + if test -z "$mancheck"; then + MANOPT="" + echo "$as_me:$LINENO: result: failed" >&5 +echo "${ECHO_T}failed" >&6 + { echo "$as_me:$LINENO: WARNING: found no *nroff option to get plaintext from man pages" >&5 +echo "$as_me: WARNING: found no *nroff option to get plaintext from man pages" >&2;} + else + echo "$as_me:$LINENO: result: $MANOPT" >&5 +echo "${ECHO_T}$MANOPT" >&6 + fi + else + echo "$as_me:$LINENO: result: $MANOPT" >&5 +echo "${ECHO_T}$MANOPT" >&6 + fi + +fi + +if test -z "$MANOPT" +then + { echo "$as_me:$LINENO: WARNING: disabling built-in manual" >&5 +echo "$as_me: WARNING: disabling built-in manual" >&2;} + USE_MANUAL="no"; +fi + + +if test "$USE_MANUAL" = "1"; then + +cat >>confdefs.h <<\_ACEOF +#define USE_MANUAL 1 +_ACEOF + + curl_manual_msg="enabled" +fi + + + +if test x"$USE_MANUAL" = x1; then + USE_MANUAL_TRUE= + USE_MANUAL_FALSE='#' +else + USE_MANUAL_TRUE='#' + USE_MANUAL_FALSE= +fi + + +echo "$as_me:$LINENO: checking whether to enable ares" >&5 +echo $ECHO_N "checking whether to enable ares... $ECHO_C" >&6 +# Check whether --enable-ares or --disable-ares was given. +if test "${enable_ares+set}" = set; then + enableval="$enable_ares" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + + if test "x$IPV6_ENABLED" = "x1"; then + { { echo "$as_me:$LINENO: error: ares doesn't work with ipv6, disable ipv6 to use ares" >&5 +echo "$as_me: error: ares doesn't work with ipv6, disable ipv6 to use ares" >&2;} + { (exit 1); exit 1; }; } + fi + + +cat >>confdefs.h <<\_ACEOF +#define USE_ARES 1 +_ACEOF + + HAVE_ARES="1" + + curl_ares_msg="enabled" + + LIBS="$LIBS -lcares" + + if test "x$enableval" = "xyes" ; then + if test -d "$srcdir/ares"; then + aresembedded="yes" + + +subdirs="$subdirs ares" + + aresinc=`cd $srcdir/ares && pwd` + CPPFLAGS="$CPPFLAGS -I$aresinc" + + pwd=`pwd` + LDFLAGS="$LDFLAGS -L$pwd/ares" + fi + else + CPPFLAGS="$CPPFLAGS -I$enableval/include" + LDFLAGS="$LDFLAGS -L$enableval/lib" + fi + + if test -z "$aresembedded"; then + echo "$as_me:$LINENO: checking that c-ares is good and recent enough" >&5 +echo $ECHO_N "checking that c-ares is good and recent enough... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF + +#include +/* provide a set of dummy functions in case c-ares was built with debug */ +void curl_dofree() { } +void curl_sclose() { } +void curl_domalloc() { } + +int main(void) +{ + ares_channel channel; + ares_cancel(channel); + return 0; +} + +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + { { echo "$as_me:$LINENO: error: c-ares library defective or too old" >&5 +echo "$as_me: error: c-ares library defective or too old" >&2;} + { (exit 1); exit 1; }; } + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi + ;; + esac +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi; + +echo "$as_me:$LINENO: checking whether to enable debug options" >&5 +echo $ECHO_N "checking whether to enable debug options... $ECHO_C" >&6 +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + case "$enableval" in + no) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + *) echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + + CPPFLAGS="$CPPFLAGS -DCURLDEBUG" + CFLAGS="$CFLAGS -g" + + + if test "$GCC" = "yes"; then + + echo "$as_me:$LINENO: checking gcc version" >&5 +echo $ECHO_N "checking gcc version... $ECHO_C" >&6 + gccver=`$CC -dumpversion` + num1=`echo $gccver | cut -d . -f1` + num2=`echo $gccver | cut -d . -f2` + gccnum=`(expr $num1 "*" 100 + $num2) 2>/dev/null` + echo "$as_me:$LINENO: result: $gccver" >&5 +echo "${ECHO_T}$gccver" >&6 + + echo "$as_me:$LINENO: checking if this is icc in disguise" >&5 +echo $ECHO_N "checking if this is icc in disguise... $ECHO_C" >&6 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +__INTEL_COMPILER +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "^__INTEL_COMPILER" >/dev/null 2>&1; then + ICC="no" + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +else + ICC="yes" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +fi +rm -f conftest* + + + if test "$ICC" = "yes"; then + + + WARN="-wd279,269,981,1418,1419" + + if test "$gccnum" -gt "600"; then + WARN="-Wall $WARN" + fi + else WARN="-W -Wall -Wwrite-strings -pedantic -Wno-long-long -Wpointer-arith -Wnested-externs -Winline -Wmissing-declarations -Wmissing-prototypes -Wsign-compare" + + + if test "$gccnum" -gt "295"; then + WARN="$WARN -Wundef" + fi + + if test "$gccnum" -ge "296"; then + WARN="$WARN -Wfloat-equal" + fi + + if test "$gccnum" -gt "296"; then + WARN="$WARN -Wno-format-nonliteral" + fi + + if test "$gccnum" -ge "303"; then + WARN="$WARN -Wendif-labels -Wstrict-prototypes" + fi + + if test "$gccnum" -ge "304"; then + # try -Wunreachable-code on gcc 3.4 + WARN="$WARN -Wunreachable-code" + fi + + for flag in $CPPFLAGS; do + case "$flag" in + -I*) + add=`echo $flag | sed 's/^-I/-isystem /g'` + WARN="$WARN $add" + ;; + esac + done + + fi + CFLAGS="$CFLAGS $WARN" + + { echo "$as_me:$LINENO: Added this set of compiler options: $WARN" >&5 +echo "$as_me: Added this set of compiler options: $WARN" >&6;} + + else + { echo "$as_me:$LINENO: Added no extra compiler options" >&5 +echo "$as_me: Added no extra compiler options" >&6;} + + fi + NEWFLAGS="" + for flag in $CFLAGS; do + case "$flag" in + -O*) + ;; + *) + NEWFLAGS="$NEWFLAGS $flag" + ;; + esac + done + CFLAGS=$NEWFLAGS + + + ;; + esac + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi; + + + +if test x$cross_compiling = xyes; then + CROSSCOMPILING_TRUE= + CROSSCOMPILING_FALSE='#' +else + CROSSCOMPILING_TRUE='#' + CROSSCOMPILING_FALSE= +fi + + + ac_config_files="$ac_config_files Makefile docs/Makefile docs/examples/Makefile docs/libcurl/Makefile include/Makefile include/curl/Makefile src/Makefile lib/Makefile tests/Makefile tests/data/Makefile tests/server/Makefile tests/libtest/Makefile packages/Makefile packages/Win32/Makefile packages/Win32/cygwin/Makefile packages/Linux/Makefile packages/Linux/RPM/Makefile packages/Linux/RPM/curl.spec packages/Linux/RPM/curl-ssl.spec packages/Solaris/Makefile packages/DOS/Makefile packages/EPM/curl.list packages/EPM/Makefile packages/vms/Makefile curl-config" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${NO_UNDEFINED_TRUE}" && test -z "${NO_UNDEFINED_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"NO_UNDEFINED\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"NO_UNDEFINED\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${MIMPURE_TRUE}" && test -z "${MIMPURE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MIMPURE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MIMPURE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${CABUNDLE_TRUE}" && test -z "${CABUNDLE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"CABUNDLE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"CABUNDLE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${HAVE_LIBZ_TRUE}" && test -z "${HAVE_LIBZ_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"HAVE_LIBZ\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"HAVE_LIBZ\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${USE_MANUAL_TRUE}" && test -z "${USE_MANUAL_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"USE_MANUAL\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"USE_MANUAL\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${CROSSCOMPILING_TRUE}" && test -z "${CROSSCOMPILING_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"CROSSCOMPILING\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"CROSSCOMPILING\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by curl $as_me -, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +curl config.status - +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "docs/Makefile" ) CONFIG_FILES="$CONFIG_FILES docs/Makefile" ;; + "docs/examples/Makefile" ) CONFIG_FILES="$CONFIG_FILES docs/examples/Makefile" ;; + "docs/libcurl/Makefile" ) CONFIG_FILES="$CONFIG_FILES docs/libcurl/Makefile" ;; + "include/Makefile" ) CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "include/curl/Makefile" ) CONFIG_FILES="$CONFIG_FILES include/curl/Makefile" ;; + "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; + "tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/data/Makefile" ) CONFIG_FILES="$CONFIG_FILES tests/data/Makefile" ;; + "tests/server/Makefile" ) CONFIG_FILES="$CONFIG_FILES tests/server/Makefile" ;; + "tests/libtest/Makefile" ) CONFIG_FILES="$CONFIG_FILES tests/libtest/Makefile" ;; + "packages/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Makefile" ;; + "packages/Win32/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Win32/Makefile" ;; + "packages/Win32/cygwin/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Win32/cygwin/Makefile" ;; + "packages/Linux/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Linux/Makefile" ;; + "packages/Linux/RPM/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Linux/RPM/Makefile" ;; + "packages/Linux/RPM/curl.spec" ) CONFIG_FILES="$CONFIG_FILES packages/Linux/RPM/curl.spec" ;; + "packages/Linux/RPM/curl-ssl.spec" ) CONFIG_FILES="$CONFIG_FILES packages/Linux/RPM/curl-ssl.spec" ;; + "packages/Solaris/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/Solaris/Makefile" ;; + "packages/DOS/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/DOS/Makefile" ;; + "packages/EPM/curl.list" ) CONFIG_FILES="$CONFIG_FILES packages/EPM/curl.list" ;; + "packages/EPM/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/EPM/Makefile" ;; + "packages/vms/Makefile" ) CONFIG_FILES="$CONFIG_FILES packages/vms/Makefile" ;; + "curl-config" ) CONFIG_FILES="$CONFIG_FILES curl-config" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "lib/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS lib/config.h" ;; + "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@SED@,$SED,;t t +s,@AR@,$AR,;t t +s,@ac_ct_AR@,$ac_ct_AR,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@AMTAR@,$AMTAR,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@mkdir_p@,$mkdir_p,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@VERSIONNUM@,$VERSIONNUM,;t t +s,@PKGADD_PKG@,$PKGADD_PKG,;t t +s,@PKGADD_NAME@,$PKGADD_NAME,;t t +s,@PKGADD_VENDOR@,$PKGADD_VENDOR,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@LN_S@,$LN_S,;t t +s,@ECHO@,$ECHO,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@DLLTOOL@,$DLLTOOL,;t t +s,@ac_ct_DLLTOOL@,$ac_ct_DLLTOOL,;t t +s,@AS@,$AS,;t t +s,@ac_ct_AS@,$ac_ct_AS,;t t +s,@OBJDUMP@,$OBJDUMP,;t t +s,@ac_ct_OBJDUMP@,$ac_ct_OBJDUMP,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@CXXDEPMODE@,$CXXDEPMODE,;t t +s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t +s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@F77@,$F77,;t t +s,@FFLAGS@,$FFLAGS,;t t +s,@ac_ct_F77@,$ac_ct_F77,;t t +s,@LIBTOOL@,$LIBTOOL,;t t +s,@NO_UNDEFINED_TRUE@,$NO_UNDEFINED_TRUE,;t t +s,@NO_UNDEFINED_FALSE@,$NO_UNDEFINED_FALSE,;t t +s,@MIMPURE_TRUE@,$MIMPURE_TRUE,;t t +s,@MIMPURE_FALSE@,$MIMPURE_FALSE,;t t +s,@CURL_DISABLE_HTTP@,$CURL_DISABLE_HTTP,;t t +s,@CURL_DISABLE_GOPHER@,$CURL_DISABLE_GOPHER,;t t +s,@CURL_DISABLE_FTP@,$CURL_DISABLE_FTP,;t t +s,@CURL_DISABLE_FILE@,$CURL_DISABLE_FILE,;t t +s,@CURL_DISABLE_LDAP@,$CURL_DISABLE_LDAP,;t t +s,@CURL_DISABLE_DICT@,$CURL_DISABLE_DICT,;t t +s,@CURL_DISABLE_TELNET@,$CURL_DISABLE_TELNET,;t t +s,@IPV6_ENABLED@,$IPV6_ENABLED,;t t +s,@RANDOM_FILE@,$RANDOM_FILE,;t t +s,@KRB4_ENABLED@,$KRB4_ENABLED,;t t +s,@PKGCONFIG@,$PKGCONFIG,;t t +s,@OPENSSL_ENABLED@,$OPENSSL_ENABLED,;t t +s,@CURL_CA_BUNDLE@,$CURL_CA_BUNDLE,;t t +s,@CABUNDLE_TRUE@,$CABUNDLE_TRUE,;t t +s,@CABUNDLE_FALSE@,$CABUNDLE_FALSE,;t t +s,@HAVE_LIBZ@,$HAVE_LIBZ,;t t +s,@HAVE_LIBZ_TRUE@,$HAVE_LIBZ_TRUE,;t t +s,@HAVE_LIBZ_FALSE@,$HAVE_LIBZ_FALSE,;t t +s,@PERL@,$PERL,;t t +s,@NROFF@,$NROFF,;t t +s,@MANOPT@,$MANOPT,;t t +s,@USE_MANUAL_TRUE@,$USE_MANUAL_TRUE,;t t +s,@USE_MANUAL_FALSE@,$USE_MANUAL_FALSE,;t t +s,@HAVE_ARES@,$HAVE_ARES,;t t +s,@subdirs@,$subdirs,;t t +s,@CROSSCOMPILING_TRUE@,$CROSSCOMPILING_TRUE,;t t +s,@CROSSCOMPILING_FALSE@,$CROSSCOMPILING_FALSE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + +# +# CONFIG_SUBDIRS section. +# +if test "$no_recursion" != yes; then + + # Remove --cache-file and --srcdir arguments so they do not pile up. + ac_sub_configure_args= + ac_prev= + for ac_arg in $ac_configure_args; do + if test -n "$ac_prev"; then + ac_prev= + continue + fi + case $ac_arg in + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ + | --c=*) + ;; + --config-cache | -C) + ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + ;; + *) ac_sub_configure_args="$ac_sub_configure_args $ac_arg" ;; + esac + done + + # Always prepend --prefix to ensure using the same prefix + # in subdir configurations. + ac_sub_configure_args="--prefix=$prefix $ac_sub_configure_args" + + ac_popdir=`pwd` + for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue + + # Do not complain, so a configure script can configure whichever + # parts of a large source tree are present. + test -d $srcdir/$ac_dir || continue + + { echo "$as_me:$LINENO: configuring in $ac_dir" >&5 +echo "$as_me: configuring in $ac_dir" >&6;} + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + cd $ac_dir + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + ac_sub_configure="$SHELL '$ac_srcdir/configure.gnu'" + elif test -f $ac_srcdir/configure; then + ac_sub_configure="$SHELL '$ac_srcdir/configure'" + elif test -f $ac_srcdir/configure.in; then + ac_sub_configure=$ac_configure + else + { echo "$as_me:$LINENO: WARNING: no configuration information is in $ac_dir" >&5 +echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;} + ac_sub_configure= + fi + + # The recursion is here. + if test -n "$ac_sub_configure"; then + # Make the cache file name correct relative to the subdirectory. + case $cache_file in + [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;; + *) # Relative path. + ac_sub_cache_file=$ac_top_builddir$cache_file ;; + esac + + { echo "$as_me:$LINENO: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5 +echo "$as_me: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;} + # The eval makes quoting arguments work. + eval $ac_sub_configure $ac_sub_configure_args \ + --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir || + { { echo "$as_me:$LINENO: error: $ac_sub_configure failed for $ac_dir" >&5 +echo "$as_me: error: $ac_sub_configure failed for $ac_dir" >&2;} + { (exit 1); exit 1; }; } + fi + + cd $ac_popdir + done +fi + + +{ echo "$as_me:$LINENO: Configured to build curl/libcurl: + + curl version: ${VERSION} + Host setup: ${host} + Install prefix: ${prefix} + Compiler: ${CC} + SSL support: ${curl_ssl_msg} + zlib support: ${curl_zlib_msg} + krb4 support: ${curl_krb4_msg} + GSSAPI support: ${curl_gss_msg} + SNPEGO support: ${curl_spnego_msg} + c-ares support: ${curl_ares_msg} + ipv6 support: ${curl_ipv6_msg} + IDN support: ${curl_idn_msg} + Build libcurl: Shared=${enable_shared}, Static=${enable_static} + Built-in manual: ${curl_manual_msg} +" >&5 +echo "$as_me: Configured to build curl/libcurl: + + curl version: ${VERSION} + Host setup: ${host} + Install prefix: ${prefix} + Compiler: ${CC} + SSL support: ${curl_ssl_msg} + zlib support: ${curl_zlib_msg} + krb4 support: ${curl_krb4_msg} + GSSAPI support: ${curl_gss_msg} + SNPEGO support: ${curl_spnego_msg} + c-ares support: ${curl_ares_msg} + ipv6 support: ${curl_ipv6_msg} + IDN support: ${curl_idn_msg} + Build libcurl: Shared=${enable_shared}, Static=${enable_static} + Built-in manual: ${curl_manual_msg} +" >&6;} diff --git a/src/curl-7.12.2/configure.ac b/src/curl-7.12.2/configure.ac new file mode 100644 index 0000000..ee869e2 --- /dev/null +++ b/src/curl-7.12.2/configure.ac @@ -0,0 +1,1479 @@ +dnl $Id: configure.ac,v 1.106 2004/10/10 03:22:45 bagder Exp $ +dnl Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) + +dnl We don't know the version number "staticly" so we use a dash here +AC_INIT(curl, [-], [a suitable curl mailing list => http://curl.haxx.se/mail/]) + +dnl configure script copyright +AC_COPYRIGHT([Copyright (c) 1998 - 2004 Daniel Stenberg, +This configure script may be copied, distributed and modified under the +terms of the curl license; see COPYING for more details]) + +AC_CONFIG_SRCDIR([lib/urldata.h]) +AM_CONFIG_HEADER(lib/config.h src/config.h) +AM_MAINTAINER_MODE + +dnl SED is needed by some of the tools +AC_PATH_PROG( SED, sed, sed-was-not-found-by-configure, + $PATH:/usr/bin:/usr/local/bin) +AC_SUBST(SED) + +if test "x$SED" = "xsed-was-not-found-by-configure"; then + AC_MSG_WARN([sed was not found, this may ruin your chances to build fine]) +fi + +dnl AR is used by libtool, and try the odd Solaris path too +dnl we use AC_CHECK_TOOL since this should make a library for the target +dnl platform +AC_CHECK_TOOL(AR, ar, + ar-was-not-found-by-configure, + $PATH:/usr/bin:/usr/local/bin:/usr/ccs/bin) +AC_SUBST(AR) +if test "x$AR" = "xar-was-not-found-by-configure"; then + AC_MSG_WARN([ar was not found, this may ruin your chances to build fine]) +fi + +dnl figure out the libcurl version +VERSION=`$SED -ne 's/^#define LIBCURL_VERSION "\(.*\)"/\1/p' ${srcdir}/include/curl/curlver.h` +AM_INIT_AUTOMAKE(curl,$VERSION) +AC_MSG_CHECKING([curl version]) +AC_MSG_RESULT($VERSION) + +dnl +dnl we extract the numerical version for curl-config only +VERSIONNUM=`$SED -ne 's/^#define LIBCURL_VERSION_NUM 0x\(.*\)/\1/p' ${srcdir}/include/curl/curlver.h` +AC_SUBST(VERSIONNUM) + +dnl Solaris pkgadd support definitions +PKGADD_PKG="HAXXcurl" +PKGADD_NAME="cURL - a client that groks URLs" +PKGADD_VENDOR="curl.haxx.se" +AC_SUBST(PKGADD_PKG) +AC_SUBST(PKGADD_NAME) +AC_SUBST(PKGADD_VENDOR) + +dnl +dnl initialize all the info variables to 'no' + curl_ssl_msg="no (--with-ssl)" + curl_zlib_msg="no (--with-zlib)" + curl_krb4_msg="no (--with-krb4*)" + curl_gss_msg="no (--with-gssapi)" + curl_spnego_msg="no (--with-spnego)" + curl_ares_msg="no (--enable-ares)" + curl_ipv6_msg="no (--enable-ipv6)" + curl_idn_msg="no (--with-libidn)" + curl_manual_msg="no (--enable-manual)" + +dnl +dnl Detect the canonical host and target build environment +dnl + +AC_CANONICAL_HOST +dnl Get system canonical name +AC_DEFINE_UNQUOTED(OS, "${host}", [cpu-machine-OS]) + +dnl Check for AIX weirdos +AC_AIX + +dnl Checks for programs. +AC_PROG_CC + +dnl check for how to do large files +AC_SYS_LARGEFILE + +dnl check for cygwin stuff +AC_LIBTOOL_WIN32_DLL + +dnl libtool setup +AC_PROG_LIBTOOL + +AC_MSG_CHECKING([if we need -no-undefined]) +case $host in + *-*-cygwin | *-*-mingw* | *-*-pw32*) + need_no_undefined=yes + ;; + *) + need_no_undefined=no + ;; +esac +AC_MSG_RESULT($need_no_undefined) +AM_CONDITIONAL(NO_UNDEFINED, test x$need_no_undefined = xyes) + +AC_MSG_CHECKING([if we need -mimpure-text]) +mimpure=no +case $host in + *-*-solaris2*) + if test "$GCC" = "yes"; then + mimpure="yes" + fi + ;; + *) + ;; +esac +AC_MSG_RESULT($mimpure) +AM_CONDITIONAL(MIMPURE, test x$mimpure = xyes) + +dnl The install stuff has already been taken care of by the automake stuff +dnl AC_PROG_INSTALL +AC_PROG_MAKE_SET + +dnl ************************************************************ +dnl switch off particular protocols +dnl +AC_MSG_CHECKING([whether to support http]) +AC_ARG_ENABLE(http, +AC_HELP_STRING([--enable-http],[Enable HTTP support]) +AC_HELP_STRING([--disable-http],[Disable HTTP support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_HTTP, 1, [to disable HTTP]) + AC_MSG_WARN([disable HTTP disables FTP over proxy and GOPHER too]) + AC_DEFINE(CURL_DISABLE_GOPHER, 1, [to disable GOPHER]) + AC_SUBST(CURL_DISABLE_HTTP) + AC_SUBST(CURL_DISABLE_GOPHER) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support ftp]) +AC_ARG_ENABLE(ftp, +AC_HELP_STRING([--enable-ftp],[Enable FTP support]) +AC_HELP_STRING([--disable-ftp],[Disable FTP support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_FTP, 1, [to disable FTP]) + AC_SUBST(CURL_DISABLE_FTP) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support gopher]) +AC_ARG_ENABLE(gopher, +AC_HELP_STRING([--enable-gopher],[Enable GOPHER support]) +AC_HELP_STRING([--disable-gopher],[Disable GOPHER support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_GOPHER, 1, [to disable GOPHER]) + AC_SUBST(CURL_DISABLE_GOPHER) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support file]) +AC_ARG_ENABLE(file, +AC_HELP_STRING([--enable-file],[Enable FILE support]) +AC_HELP_STRING([--disable-file],[Disable FILE support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_FILE, 1, [to disable FILE]) + AC_SUBST(CURL_DISABLE_FILE) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support ldap]) +AC_ARG_ENABLE(ldap, +AC_HELP_STRING([--enable-ldap],[Enable LDAP support]) +AC_HELP_STRING([--disable-ldap],[Disable LDAP support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_LDAP, 1, [to disable LDAP]) + AC_SUBST(CURL_DISABLE_LDAP) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support dict]) +AC_ARG_ENABLE(dict, +AC_HELP_STRING([--enable-dict],[Enable DICT support]) +AC_HELP_STRING([--disable-dict],[Disable DICT support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_DICT, 1, [to disable DICT]) + AC_SUBST(CURL_DISABLE_DICT) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) +AC_MSG_CHECKING([whether to support telnet]) +AC_ARG_ENABLE(telnet, +AC_HELP_STRING([--enable-telnet],[Enable TELNET support]) +AC_HELP_STRING([--disable-telnet],[Disable TELNET support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_TELNET, 1, [to disable TELNET]) + AC_SUBST(CURL_DISABLE_TELNET) + ;; + *) AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) + +dnl ********************************************************************** +dnl Check for built-in manual +dnl ********************************************************************** + +AC_MSG_CHECKING([whether to provide built-in manual]) +AC_ARG_ENABLE(manual, +AC_HELP_STRING([--enable-manual],[Enable built-in manual]) +AC_HELP_STRING([--disable-manual],[Disable built-in manual]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + ;; + *) AC_MSG_RESULT(yes) + USE_MANUAL="1" + ;; + esac ], + AC_MSG_RESULT(yes) + USE_MANUAL="1" +) +dnl The actual use of the USE_MANUAL variable is done much later in this +dnl script to allow other actions to disable it as well. + + +dnl ********************************************************************** +dnl Checks for libraries. +dnl ********************************************************************** + +dnl gethostbyname without lib or in the nsl lib? +AC_CHECK_FUNC(gethostbyname, + [HAVE_GETHOSTBYNAME="1" + ], + [ AC_CHECK_LIB(nsl, gethostbyname, + [HAVE_GETHOSTBYNAME="1" + LIBS="$LIBS -lnsl" + ]) + ]) + +if test "$HAVE_GETHOSTBYNAME" != "1" +then + dnl gethostbyname in the socket lib? + AC_CHECK_LIB(socket, gethostbyname, + [HAVE_GETHOSTBYNAME="1" + LIBS="$LIBS -lsocket" + ]) +fi + +dnl At least one system has been identified to require BOTH nsl and socket +dnl libs at the same time to link properly. +if test "$HAVE_GETHOSTBYNAME" != "1" +then + AC_MSG_CHECKING([for gethostbyname with both nsl and socket libs]) + my_ac_save_LIBS=$LIBS + LIBS="-lnsl -lsocket $LIBS" + AC_TRY_LINK( , + [gethostbyname();], + [ dnl found it! + HAVE_GETHOSTBYNAME="1" + AC_MSG_RESULT([yes])], + [ dnl failed! + AC_MSG_RESULT([no]) + dnl restore LIBS + LIBS=$my_ac_save_LIBS] + ) +fi + +if test "$HAVE_GETHOSTBYNAME" != "1" +then + dnl This is for Msys/Mingw + AC_MSG_CHECKING([for gethostbyname in ws2_32]) + my_ac_save_LIBS=$LIBS + LIBS="-lws2_32 $LIBS" + AC_TRY_LINK([#include ], + [gethostbyname("www.dummysite.com");], + [ dnl worked! + AC_MSG_RESULT([yes]) + HAVE_GETHOSTBYNAME="1"], + [ dnl failed, restore LIBS + LIBS=$my_ac_save_LIBS + AC_MSG_RESULT(no)] + ) +fi + +if test "$HAVE_GETHOSTBYNAME" = "1"; then + AC_DEFINE(HAVE_GETHOSTBYNAME, 1, [If you have gethostbyname]) +else + AC_MSG_ERROR([couldn't find libraries for gethostbyname()]) +fi + +dnl resolve lib? +AC_CHECK_FUNC(strcasecmp, , [ AC_CHECK_LIB(resolve, strcasecmp) ]) + +if test "$ac_cv_lib_resolve_strcasecmp" = "$ac_cv_func_strcasecmp"; then + AC_CHECK_LIB(resolve, strcasecmp, + [LIBS="-lresolve $LIBS"], + , + -lnsl) +fi + +dnl socket lib? +AC_CHECK_FUNC(connect, , [ AC_CHECK_LIB(socket, connect) ]) + +dnl dl lib? +AC_CHECK_FUNC(dlclose, , [ AC_CHECK_LIB(dl, dlopen) ]) + +AC_MSG_CHECKING([whether to use libgcc]) +AC_ARG_ENABLE(libgcc, +AC_HELP_STRING([--enable-libgcc],[use libgcc when linking]), +[ case "$enableval" in + yes) + LIBS="$LIBS -lgcc" + AC_MSG_RESULT(yes) + ;; + *) AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +dnl ********************************************************************** +dnl Check for the presence of the winmm library. +dnl ********************************************************************** + +AC_MSG_CHECKING([for timeGetTime in winmm]) +my_ac_save_LIBS=$LIBS +LIBS="-lwinmm $LIBS" +AC_TRY_LINK([#include + #include + ], + [timeGetTime();], + [ dnl worked! + AC_MSG_RESULT([yes]) + ], + [ dnl failed, restore LIBS + LIBS=$my_ac_save_LIBS + AC_MSG_RESULT(no)] + ) + +dnl ********************************************************************** +dnl Checks for IPv6 +dnl ********************************************************************** + +AC_MSG_CHECKING([whether to enable ipv6]) +AC_ARG_ENABLE(ipv6, +AC_HELP_STRING([--enable-ipv6],[Enable ipv6 (with ipv4) support]) +AC_HELP_STRING([--disable-ipv6],[Disable ipv6 support]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + ipv6=no + ;; + *) AC_MSG_RESULT(yes) + ipv6=yes + ;; + esac ], + + AC_TRY_RUN([ /* is AF_INET6 available? */ +#include +#include +main() +{ + if (socket(AF_INET6, SOCK_STREAM, 0) < 0) + exit(1); + else + exit(0); +} +], + AC_MSG_RESULT(yes) + ipv6=yes, + AC_MSG_RESULT(no) + ipv6=no, + AC_MSG_RESULT(no) + ipv6=no +)) + +if test "$ipv6" = "yes"; then + curl_ipv6_msg="enabled" + + CURL_CHECK_WORKING_GETADDRINFO + + CURL_CHECK_NI_WITHSCOPEID +fi + +dnl ********************************************************************** +dnl Check how non-blocking sockets are set +dnl ********************************************************************** +AC_ARG_ENABLE(nonblocking, +AC_HELP_STRING([--enable-nonblocking],[Enable detecting how to do it]) +AC_HELP_STRING([--disable-nonblocking],[Disable non-blocking socket detection]), +[ + if test "$enableval" = "no" ; then + AC_MSG_WARN([non-blocking sockets disabled]) + AC_DEFINE(HAVE_DISABLED_NONBLOCKING, 1, + [to disable NON-BLOCKING connections]) + else + CURL_CHECK_NONBLOCKING_SOCKET + fi +], +[ + CURL_CHECK_NONBLOCKING_SOCKET +]) + +dnl ********************************************************************** +dnl Check for the random seed preferences +dnl ********************************************************************** + +AC_ARG_WITH(egd-socket, +AC_HELP_STRING([--with-egd-socket=FILE], + [Entropy Gathering Daemon socket pathname]), + [ EGD_SOCKET="$withval" ] +) +if test -n "$EGD_SOCKET" ; then + AC_DEFINE_UNQUOTED(EGD_SOCKET, "$EGD_SOCKET", + [your Entropy Gathering Daemon socket pathname] ) +fi + +dnl Check for user-specified random device +AC_ARG_WITH(random, +AC_HELP_STRING([--with-random=FILE],[read randomness from FILE (default=/dev/urandom)]), + [ RANDOM_FILE="$withval" ], + [ + dnl Check for random device + AC_CHECK_FILE("/dev/urandom", [ RANDOM_FILE="/dev/urandom"] ) + ] +) +if test -n "$RANDOM_FILE" ; then + AC_SUBST(RANDOM_FILE) + AC_DEFINE_UNQUOTED(RANDOM_FILE, "$RANDOM_FILE", + [a suitable file to read random data from]) +fi + +dnl ********************************************************************** +dnl Check if the operating system allows programs to write to their own argv[] +dnl ********************************************************************** + +AC_MSG_CHECKING([if argv can be written to]) +AC_CACHE_VAL(curl_cv_writable_argv, [ +AC_RUN_IFELSE([[ +int main(int argc, char ** argv) { + argv[0][0] = ' '; + return (argv[0][0] == ' ')?0:1; +} + ]], + curl_cv_writable_argv=yes, + curl_cv_writable_argv=no, + curl_cv_writable_argv=cross) +]) +case $curl_cv_writable_argv in +yes) + AC_DEFINE(HAVE_WRITABLE_ARGV, 1, [Define this symbol if your OS supports changing the contents of argv]) + AC_MSG_RESULT(yes) + ;; +no) + AC_MSG_RESULT(no) + ;; +*) + AC_MSG_RESULT(no) + AC_MSG_WARN([the previous check could not be made default was used]) + ;; +esac + +dnl ********************************************************************** +dnl Check for the presence of Kerberos4 libraries and headers +dnl ********************************************************************** + +AC_ARG_WITH(krb4-includes, +AC_HELP_STRING([--with-krb4-includes=DIR], + [Specify location of kerberos4 headers]),[ + CPPFLAGS="$CPPFLAGS -I$withval" + KRB4INC="$withval" + want_krb4=yes + ]) + +AC_ARG_WITH(krb4-libs, +AC_HELP_STRING([--with-krb4-libs=DIR],[Specify location of kerberos4 libs]),[ + LDFLAGS="$LDFLAGS -L$withval" + KRB4LIB="$withval" + want_krb4=yes + ]) + + +OPT_KRB4=off +AC_ARG_WITH(krb4,dnl +AC_HELP_STRING([--with-krb4=DIR],[where to look for Kerberos4]),[ + OPT_KRB4="$withval" + if test X"$OPT_KRB4" != Xyes + then + LDFLAGS="$LDFLAGS -L$OPT_KRB4/lib$libsuff" + KRB4LIB="$OPT_KRB4/lib$libsuff" + CPPFLAGS="$CPPFLAGS -I$OPT_KRB4/include" + KRB4INC="$OPT_KRB4/include" + fi + want_krb4="yes" + ]) + +AC_MSG_CHECKING([if Kerberos4 support is requested]) + +if test "$want_krb4" = yes +then + if test "$ipv6" = "yes"; then + echo krb4 is not compatible with IPv6 + exit 1 + fi + AC_MSG_RESULT(yes) + + dnl Check for & handle argument to --with-krb4 + + AC_MSG_CHECKING(where to look for Kerberos4) + if test X"$OPT_KRB4" = Xyes + then + AC_MSG_RESULT([defaults]) + else + AC_MSG_RESULT([libs in $KRB4LIB, headers in $KRB4INC]) + fi + + dnl Check for DES library + AC_CHECK_LIB(des, des_pcbc_encrypt, + [ + AC_CHECK_HEADERS(des.h) + + dnl resolv lib? + AC_CHECK_FUNC(res_search, , [AC_CHECK_LIB(resolv, res_search)]) + + dnl Check for the Kerberos4 library + AC_CHECK_LIB(krb, krb_net_read, + [ + dnl Check for header files + AC_CHECK_HEADERS(krb.h) + + dnl we found the required libraries, add to LIBS + LIBS="-lkrb -lcom_err -ldes $LIBS" + + dnl Check for function krb_get_our_ip_for_realm + dnl this is needed for NAT networks + AC_CHECK_FUNCS(krb_get_our_ip_for_realm) + + dnl add define KRB4 + AC_DEFINE(HAVE_KRB4, 1, + [if you have the Kerberos4 libraries (including -ldes)]) + + dnl substitute it too! + KRB4_ENABLED=1 + AC_SUBST(KRB4_ENABLED) + + curl_krb4_msg="enabled" + + dnl the krb4 stuff needs a strlcpy() + AC_CHECK_FUNCS(strlcpy) + + ]) + ]) +else + AC_MSG_RESULT(no) +fi + +dnl ********************************************************************** +dnl Check for FBopenssl(SPNEGO) libraries +dnl ********************************************************************** + +AC_ARG_WITH(spnego, + AC_HELP_STRING([--with-spnego=DIR], + [Specify location of SPNEGO library fbopenssl]), + [ SPNEGO_ROOT="$withval" + want_spnego="yes" ] +) +AC_MSG_CHECKING([if SPNEGO support is requested]) +if test x"$want_spnego" = xyes; then + + if test X"$SPNEGO_ROOT" = Xyes; then + AC_MSG_ERROR([FBOpenSSL libs and/or directories were not found where specified!]) + AC_MSG_RESULT(no) + else + if test -z "$SPNEGO_LIB_DIR"; then + LDFLAGS="$LDFLAGS -L$SPNEGO_ROOT -lfbopenssl" + else + LDFLAGS="$LDFLAGS $SPNEGO_LIB_DIR" + fi + + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SPNEGO, 1, + [Define this if you have the SPNEGO library fbopenssl]) + curl_spnego_msg="enabled" + fi +else + AC_MSG_RESULT(no) +fi + +dnl ********************************************************************** +dnl Check for GSS-API libraries +dnl ********************************************************************** + +AC_ARG_WITH(gssapi-includes, + AC_HELP_STRING([--with-gssapi-includes=DIR], + [Specify location of GSSAPI header]), + [ GSSAPI_INCS="-I$withval" + want_gss="yes" ] +) + +AC_ARG_WITH(gssapi-libs, + AC_HELP_STRING([--with-gssapi-libs=DIR], + [Specify location of GSSAPI libs]), + [ GSSAPI_LIBS="-L$withval -lgssapi" + want_gss="yes" ] +) + +AC_ARG_WITH(gssapi, + AC_HELP_STRING([--with-gssapi=DIR], + [Where to look for GSSAPI]), + [ GSSAPI_ROOT="$withval" + want_gss="yes" ] +) + +AC_MSG_CHECKING([if GSSAPI support is requested]) +if test x"$want_gss" = xyes; then + if test -z "$GSSAPI_INCS"; then + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + GSSAPI_INCS=`$GSSAPI_ROOT/bin/krb5-config --cflags gssapi` + else + GSSAPI_INCS="-I$GSSAPI_ROOT/include" + fi + fi + CPPFLAGS="$CPPFLAGS $GSSAPI_INCS" + + if test -z "$GSSAPI_LIB_DIR"; then + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + gss_ldflags=`$GSSAPI_ROOT/bin/krb5-config --libs gssapi` + LDFLAGS="$LDFLAGS $gss_ldflags" + else + LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff -lgssapi" + fi + else + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + fi + + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GSSAPI, 1, [if you have the gssapi libraries]) + + curl_gss_msg="enabled" + + if test -n "$GSSAPI_INCS"; then + # cut off the preceding -I from the include path + GSSAPI_INCS=`echo $GSSAPI_INCS | sed -e s/^-I//g` + fi + + if test -f "$GSSAPI_INCS/gssapi.h"; then + AC_DEFINE(HAVE_GSSHEIMDAL, 1, [if you have the Heimdal gssapi libraries]) + else + AC_DEFINE(HAVE_GSSMIT, 1, [if you have the MIT gssapi libraries]) + fi + +else + AC_MSG_RESULT(no) +fi + +dnl ********************************************************************** +dnl Check for the presence of SSL libraries and headers +dnl ********************************************************************** + +dnl Default to compiler & linker defaults for SSL files & libraries. +OPT_SSL=off +dnl Default to no CA bundle +ca="no" +AC_ARG_WITH(ssl,dnl +AC_HELP_STRING([--with-ssl=PATH],[where to look for SSL, PATH points to the SSL installation (default: /usr/local/ssl)]) +AC_HELP_STRING([--without-ssl], [disable SSL]), + OPT_SSL=$withval) + +if test X"$OPT_SSL" = Xno +then + AC_MSG_WARN([SSL disabled, you will not be able to use HTTPS, FTPS, NTLM and more]) +else + + dnl backup the pre-ssl variables + CLEANLDFLAGS="$LDFLAGS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLIBS="$LIBS" + + case "$OPT_SSL" in + yes) + dnl --with-ssl (without path) used + PKGTEST="yes" + EXTRA_SSL=/usr/local/ssl ;; + off) + dnl no --with-ssl option given, just check default places + PKGTEST="yes" + EXTRA_SSL= ;; + *) + dnl check the given --with-ssl spot + PKGTEST="no" + EXTRA_SSL=$OPT_SSL + LDFLAGS="$LDFLAGS -L$EXTRA_SSL/lib$libsuff" + CPPFLAGS="$CPPFLAGS -I$EXTRA_SSL/include/openssl -I$EXTRA_SSL/include" + ;; + esac + + if test "$PKGTEST" = "yes"; then + + dnl Detect the pkg-config tool, as it may have extra info about the + dnl openssl installation we can use. I *believe* this is what we are + dnl expected to do on really recent Redhat Linux hosts. + + AC_PATH_PROG( PKGCONFIG, pkg-config, no, $PATH:/usr/bin:/usr/local/bin) + if test "$PKGCONFIG" != "no" ; then + AC_MSG_CHECKING([OpenSSL options with pkg-config]) + + $PKGCONFIG --exists openssl + SSL_EXISTS=$? + + if test "$SSL_EXISTS" -eq "0"; then + SSL_LIBS=`$PKGCONFIG --libs-only-l openssl 2>/dev/null` + SSL_LDFLAGS=`$PKGCONFIG --libs-only-L openssl 2>/dev/null` + SSL_CPPFLAGS=`$PKGCONFIG --cflags-only-I openssl 2>/dev/null` + + dnl use the values pkg-config reported + LIBS="$LIBS $SSL_LIBS" + CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" + LDFLAGS="$LDFLAGS $SSL_LDFLAGS" + AC_MSG_RESULT([found]) + else + AC_MSG_RESULT([no]) + fi + fi + fi + + AC_CHECK_LIB(crypto, CRYPTO_lock,[ + HAVECRYPTO="yes" + ],[ + LDFLAGS="$CLEANLDFLAGS -L$EXTRA_SSL/lib$libsuff" + CPPFLAGS="$CLEANCPPFLAGS -I$EXTRA_SSL/include/openssl -I$EXTRA_SSL/include" + AC_CHECK_LIB(crypto, CRYPTO_add_lock,[ + HAVECRYPTO="yes" ], [ + LDFLAGS="$CLEANLDFLAGS" + CPPFLAGS="$CLEANCPPFLAGS" + LIBS="$CLEANLIBS" + ]) + ]) + + + if test "$HAVECRYPTO" = "yes"; then + dnl This is only reasonable to do if crypto actually is there: check for + dnl SSL libs NOTE: it is important to do this AFTER the crypto lib + + dnl This is for Msys/Mingw + AC_MSG_CHECKING([for gdi32]) + my_ac_save_LIBS=$LIBS + LIBS="-lgdi32 $LIBS" + AC_TRY_LINK([#include + #include ], + [GdiFlush();], + [ dnl worked! + AC_MSG_RESULT([yes])], + [ dnl failed, restore LIBS + LIBS=$my_ac_save_LIBS + AC_MSG_RESULT(no)] + ) + + AC_CHECK_LIB(crypto, CRYPTO_add_lock) + AC_CHECK_LIB(ssl, SSL_connect) + + if test "$ac_cv_lib_ssl_SSL_connect" != yes; then + dnl we didn't find the SSL lib, try the RSAglue/rsaref stuff + AC_MSG_CHECKING(for ssl with RSAglue/rsaref libs in use); + OLIBS=$LIBS + LIBS="$LIBS -lRSAglue -lrsaref" + AC_CHECK_LIB(ssl, SSL_connect) + if test "$ac_cv_lib_ssl_SSL_connect" != yes; then + dnl still no SSL_connect + AC_MSG_RESULT(no) + LIBS=$OLIBS + else + AC_MSG_RESULT(yes) + fi + fi + + + dnl Check for SSLeay headers + AC_CHECK_HEADERS(openssl/x509.h openssl/rsa.h openssl/crypto.h \ + openssl/pem.h openssl/ssl.h openssl/err.h, + curl_ssl_msg="enabled" + OPENSSL_ENABLED=1) + + if test $ac_cv_header_openssl_x509_h = no; then + AC_CHECK_HEADERS(x509.h rsa.h crypto.h pem.h ssl.h err.h, + curl_ssl_msg="enabled" + OPENSSL_ENABLED=1) + fi + + dnl If the ENGINE library seems to be around, check for the OpenSSL engine + dnl header, it is kind of "separated" from the main SSL check + AC_CHECK_FUNC(ENGINE_init, [ AC_CHECK_HEADERS(openssl/engine.h) ]) + + AC_SUBST(OPENSSL_ENABLED) + + AC_MSG_CHECKING([CA cert bundle install path]) + + AC_ARG_WITH(ca-bundle, +AC_HELP_STRING([--with-ca-bundle=FILE], [File name to install the CA bundle as]) +AC_HELP_STRING([--without-ca-bundle], [Don't install the CA bundle]), + [ ca="$withval" ], + [ + if test "x$prefix" != xNONE; then + ca="\${prefix}/share/curl/curl-ca-bundle.crt" + else + ca="$ac_default_prefix/share/curl/curl-ca-bundle.crt" + fi + ] ) + + if test X"$OPT_SSL" = Xno; then + ca="no" + fi + + if test "x$ca" != "xno"; then + CURL_CA_BUNDLE='"'$ca'"' + AC_SUBST(CURL_CA_BUNDLE) + fi + AC_MSG_RESULT([$ca]) + + dnl these can only exist if openssl exists + + AC_CHECK_FUNCS( RAND_status \ + RAND_screen \ + RAND_egd \ + CRYPTO_cleanup_all_ex_data ) + + fi + + if test X"$OPT_SSL" != Xoff && + test "$OPENSSL_ENABLED" != "1"; then + AC_MSG_ERROR([OpenSSL libs and/or directories were not found where specified!]) + fi + +fi + +AM_CONDITIONAL(CABUNDLE, test x$ca != xno) + +dnl ********************************************************************** +dnl Check for the presence of ZLIB libraries and headers +dnl ********************************************************************** + +dnl Check for & handle argument to --with-zlib. + +_cppflags=$CPPFLAGS +_ldflags=$LDFLAGS +OPT_ZLIB="/usr/local" +AC_ARG_WITH(zlib, +AC_HELP_STRING([--with-zlib=PATH],[search for zlib in PATH]) +AC_HELP_STRING([--without-zlib],[disable use of zlib]), + [OPT_ZLIB="$withval"]) + +case "$OPT_ZLIB" in + no) + AC_MSG_WARN([zlib disabled]) ;; + *) + dnl check for the lib first without setting any new path, since many + dnl people have it in the default path + + AC_CHECK_LIB(z, inflateEnd, + dnl libz found, set the variable + [HAVE_LIBZ="1"], + dnl if no lib found, try to add the given library + [if test -d "$OPT_ZLIB"; then + CPPFLAGS="$CPPFLAGS -I$OPT_ZLIB/include" + LDFLAGS="$LDFLAGS -L$OPT_ZLIB/lib$libsuff" + fi]) + + AC_CHECK_HEADER(zlib.h, + [ + dnl zlib.h was found + HAVE_ZLIB_H="1" + dnl if the lib wasn't found already, try again with the new paths + if test "$HAVE_LIBZ" != "1"; then + AC_CHECK_LIB(z, gzread, + [ + dnl the lib was found! + HAVE_LIBZ="1" + ], + [ CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags]) + fi + ], + [ + dnl zlib.h was not found, restore the flags + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags] + ) + + if test "$HAVE_LIBZ" = "1" && test "$HAVE_ZLIB_H" != "1" + then + AC_MSG_WARN([configure found only the libz lib, not the header file!]) + elif test "$HAVE_LIBZ" != "1" && test "$HAVE_ZLIB_H" = "1" + then + AC_MSG_WARN([configure found only the libz header file, not the lib!]) + elif test "$HAVE_LIBZ" = "1" && test "$HAVE_ZLIB_H" = "1" + then + dnl both header and lib were found! + AC_SUBST(HAVE_LIBZ) + AC_DEFINE(HAVE_ZLIB_H, 1, [if you have the zlib.h header file]) + AC_DEFINE(HAVE_LIBZ, 1, [if zlib is available]) + + LIBS="$LIBS -lz" + + dnl replace 'HAVE_LIBZ' in the automake makefile.ams + AMFIXLIB="1" + AC_MSG_NOTICE([found both libz and libz.h header]) + curl_zlib_msg="enabled" + fi + ;; +esac + +dnl set variable for use in automakefile(s) +AM_CONDITIONAL(HAVE_LIBZ, test x"$AMFIXLIB" = x1) + +AC_MSG_CHECKING([whether to build with libidn]) +AC_ARG_WITH(libidn, +AC_HELP_STRING([--with-libidn=PATH],[Enable libidn usage]) +AC_HELP_STRING([--without-libidn],[Disable libidn usage]), + [LIBIDN="$withval"]) + +case "$LIBIDN" in + no) + AC_MSG_RESULT(no) + ;; + *) AC_MSG_RESULT(yes) + + idn="" + dnl if there is a given path, check that FIRST + if test "x$LIBIDN" != "xyes"; then + oldLDFLAGS=$LDFLAGS + oldCPPFLAGS=$CPPFLAGS + LDFLAGS="$LDFLAGS -L$LIBIDN/lib" + CPPFLAGS="$CPPFLAGS -I$LIBIDN/include" + idn="yes" + AC_CHECK_LIB(idn, idna_to_ascii_4i, , + idn="" + LDFLAGS=$oldLDFLAGS + CPPFLAGS=$oldCPPFLAGS) + fi + + if test "x$idn" != "xyes"; then + dnl check with default paths + AC_CHECK_LIB(idn, idna_to_ascii_lz, , + idn="") + fi + + if test "x$idn" = "xyes"; then + curl_idn_msg="enabled" + dnl different versions of libidn have different setups of these: + AC_CHECK_FUNCS( idn_free idna_strerror tld_strerror) + AC_CHECK_HEADERS( idn-free.h ) + fi + + ;; +esac + +dnl Default is to try the thread-safe versions of a few functions +OPT_THREAD=on + +dnl detect AIX 4.3 or later +dnl see full docs on this reasoning in the lib/hostip.c source file +AC_MSG_CHECKING([AIX 4.3 or later]) +AC_PREPROC_IFELSE([ +#if defined(_AIX) && defined(_AIX43) +printf("just fine"); +#else +#error "this is not AIX 4.3 or later" +#endif +], + [ AC_MSG_RESULT([yes]) + RECENTAIX=yes + OPT_THREAD=off ], + [ AC_MSG_RESULT([no]) ] +) + +AC_ARG_ENABLE(thread,dnl +AC_HELP_STRING([--disable-thread],[don't look for thread-safe functions]) +AC_HELP_STRING([--enable-thread],[look for thread-safe functions]), +[ case "$enableval" in + no) + OPT_THREAD=off + AC_MSG_WARN(libcurl will not get built using thread-safe functions) + ;; + *) + ;; + esac +] +) + +if test X"$OPT_THREAD" = Xoff +then + AC_DEFINE(DISABLED_THREADSAFE, 1, \ +Set to explicitly specify we don't want to use thread-safe functions) +else + if test "$ipv6" != "yes"; then + dnl dig around for gethostbyname_r() + CURL_CHECK_GETHOSTBYNAME_R() + + dnl dig around for gethostbyaddr_r() + dnl CURL_CHECK_GETHOSTBYADDR_R() + fi + + dnl poke around for inet_ntoa_r() + CURL_CHECK_INET_NTOA_R() + + dnl is there a localtime_r() + CURL_CHECK_LOCALTIME_R() + + dnl is there a strerror_r() + CURL_CHECK_STRERROR_R() + + AC_CHECK_FUNCS( gmtime_r ) +fi + +dnl for recent AIX versions, we skip all the thread-safe checks above since +dnl they claim a thread-safe libc using the standard API. But there are +dnl some functions still not thread-safe. Check for these! + +dnl Let's hope this split URL remains working: +dnl http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/ \ +dnl genprogc/thread_quick_ref.htm + +if test "x$RECENTAIX" = "xyes"; then + + AC_DEFINE(_THREAD_SAFE, 1, [define this if you need it to compile thread-safe code]) + + dnl check if this is the IMB xlc compiler + dnl Details thanks to => http://predef.sourceforge.net/ + AC_MSG_CHECKING([if this is the xlc compiler]) + AC_EGREP_CPP([^__xlC__], [__xlC__], + dnl action if the text is found, this it has not been replaced by the + dnl cpp + XLC="no" + AC_MSG_RESULT([no]), + dnl the text was not found, it was replaced by the cpp + XLC="yes" + AC_MSG_RESULT([yes]) + CFLAGS="$CFLAGS -qthreaded" + ) + + + dnl is there a localtime_r() + CURL_CHECK_LOCALTIME_R() + + dnl is there a strerror_r() + CURL_CHECK_STRERROR_R() + + AC_CHECK_FUNCS( gmtime_r ) +fi + + +dnl ********************************************************************** +dnl Back to "normal" configuring +dnl ********************************************************************** + +dnl Checks for header files. +AC_HEADER_STDC + +dnl First check for the very most basic headers. Then we can use these +dnl ones as default-headers when checking for the rest! +AC_CHECK_HEADERS( + sys/types.h \ + sys/time.h \ + sys/select.h \ + sys/socket.h \ + sys/ioctl.h \ + assert.h \ + unistd.h \ + malloc.h \ + stdlib.h \ + limits.h \ + arpa/inet.h \ + net/if.h \ + netinet/in.h \ + netinet/tcp.h \ + netdb.h \ + sys/sockio.h \ + sys/stat.h \ + sys/param.h \ + termios.h \ + termio.h \ + sgtty.h \ + fcntl.h \ + dlfcn.h \ + alloca.h \ + winsock.h \ + time.h \ + io.h \ + pwd.h \ + utime.h \ + sys/utime.h \ + sys/poll.h \ + libgen.h \ + setjmp.h, +dnl to do if not found +[], +dnl to do if found +[], +dnl default includes +[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +] +) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME + +AC_CHECK_SIZEOF(curl_off_t, ,[ +#include +#include "$srcdir/include/curl/curl.h" +]) +AC_CHECK_SIZEOF(size_t) +AC_CHECK_SIZEOF(long) + +AC_CHECK_TYPE(long long, + [AC_DEFINE(HAVE_LONGLONG, 1, [if your compiler supports long long])] + longlong="yes" +) + +if test "xyes" = "x$longlong"; then + AC_MSG_CHECKING([if numberLL works]) + AC_COMPILE_IFELSE([long long val = 1000LL;], + [AC_DEFINE(HAVE_LL, 1, [if your compiler supports LL])] + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) +fi + + +# check for ssize_t +AC_CHECK_TYPE(ssize_t, , + AC_DEFINE(ssize_t, int, [the signed version of size_t])) + +TYPE_SOCKLEN_T +TYPE_IN_ADDR_T + +AC_FUNC_SELECT_ARGTYPES + +dnl Checks for library functions. +dnl AC_PROG_GCC_TRADITIONAL +AC_TYPE_SIGNAL +dnl AC_FUNC_VPRINTF +AC_CHECK_FUNCS( strtoll \ + socket \ + select \ + strdup \ + strstr \ + strtok_r \ + strftime \ + uname \ + strcasecmp \ + stricmp \ + strcmpi \ + gethostbyaddr \ + gettimeofday \ + inet_addr \ + inet_ntoa \ + inet_pton \ + tcsetattr \ + tcgetattr \ + perror \ + closesocket \ + siginterrupt \ + sigaction \ + signal \ + getpass_r \ + strlcat \ + getpwuid \ + geteuid \ + dlopen \ + utime \ + sigsetjmp \ + basename \ + poll, +dnl if found +[], +dnl if not found, $ac_func is the name we check for + func="$ac_func" + AC_MSG_CHECKING([deeper for $func]) + AC_TRY_LINK( [], + [ $func ();], + AC_MSG_RESULT(yes!) + eval "ac_cv_func_$func=yes" + def=`echo "HAVE_$func" | tr 'a-z' 'A-Z'` + AC_DEFINE_UNQUOTED($def, 1, [If you have $func]), + AC_MSG_RESULT(but still no) + ) + +) + +dnl sigsetjmp() might be a macro and no function so if it isn't found already +dnl we make an extra check here! +if test "$ac_cv_func_sigsetjmp" != "yes"; then + AC_MSG_CHECKING([for sigsetjmp defined as macro]) + AC_TRY_LINK( [#include ], + [sigjmp_buf jmpenv; + sigsetjmp(jmpenv, 1);], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SIGSETJMP, 1, [If you have sigsetjmp]), + AC_MSG_RESULT(no) + ) +fi + +AC_CHECK_DECL(basename, , + AC_DEFINE(NEED_BASENAME_PROTO, 1, [If you lack a fine basename() prototype]), +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif +) + +dnl poll() might be badly emulated, as in Mac OS X 10.3 (and other BSDs?) and +dnl to find out we make an extra check here! +if test "$ac_cv_func_poll" = "yes"; then + AC_MSG_CHECKING([if poll works with NULL inputs]) + AC_RUN_IFELSE([ +#ifdef HAVE_SYS_POLL_H +#include +#endif + + int main(void) + { + /* make this return 0 == timeout since there's nothing to read from */ + return poll((void *)0, 0, 10 /*ms*/); + } +], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_POLL_FINE, 1, [If you have a fine poll]), + AC_MSG_RESULT(no), + AC_MSG_RESULT(cross-compiling assumes yes) + AC_DEFINE(HAVE_POLL_FINE, 1, [If you have a fine poll]) + ) dnl end of AC_RUN_IFELSE +fi + + + +AC_PATH_PROG( PERL, perl, , + $PATH:/usr/local/bin/perl:/usr/bin/:/usr/local/bin ) +AC_SUBST(PERL) + +AC_PATH_PROGS( NROFF, gnroff nroff, , + $PATH:/usr/bin/:/usr/local/bin ) +AC_SUBST(NROFF) + +if test -n "$NROFF"; then + dnl only check for nroff options if an nroff command was found + + AC_MSG_CHECKING([how to use *nroff to get plain text from man pages]) + MANOPT="-man" + mancheck=`echo foo | $NROFF $MANOPT 2>/dev/null` + if test -z "$mancheck"; then + MANOPT="-mandoc" + mancheck=`echo foo | $NROFF $MANOPT 2>/dev/null` + if test -z "$mancheck"; then + MANOPT="" + AC_MSG_RESULT([failed]) + AC_MSG_WARN([found no *nroff option to get plaintext from man pages]) + else + AC_MSG_RESULT([$MANOPT]) + fi + else + AC_MSG_RESULT([$MANOPT]) + fi + AC_SUBST(MANOPT) +fi + +if test -z "$MANOPT" +then + dnl if no nroff tool was found, or no option that could convert man pages + dnl was found, then disable the built-in manual stuff + AC_MSG_WARN([disabling built-in manual]) + USE_MANUAL="no"; +fi + +dnl ************************************************************************* +dnl If the manual variable still is set, then we go with providing a built-in +dnl manual + +if test "$USE_MANUAL" = "1"; then + AC_DEFINE(USE_MANUAL, 1, [If you want to build curl with the built-in manual]) + curl_manual_msg="enabled" +fi + +dnl set variable for use in automakefile(s) +AM_CONDITIONAL(USE_MANUAL, test x"$USE_MANUAL" = x1) + +AC_MSG_CHECKING([whether to enable ares]) +AC_ARG_ENABLE(ares, +AC_HELP_STRING([--enable-ares=PATH],[Enable ares for name lookups]) +AC_HELP_STRING([--disable-ares],[Disable ares for name lookups]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + ;; + *) AC_MSG_RESULT(yes) + + if test "x$IPV6_ENABLED" = "x1"; then + AC_MSG_ERROR([ares doesn't work with ipv6, disable ipv6 to use ares]) + fi + + AC_DEFINE(USE_ARES, 1, [Define if you want to enable ares support]) + dnl substitute HAVE_ARES for curl-config and similar + HAVE_ARES="1" + AC_SUBST(HAVE_ARES) + curl_ares_msg="enabled" + + LIBS="$LIBS -lcares" + + dnl For backwards compatibility default to includes/lib in srcdir/ares + dnl If a value is specified it is assumed that the libs are in $val/lib + dnl and the includes are in $val/include. This is the default setup for + dnl ares so it should not be a problem. + if test "x$enableval" = "xyes" ; then + if test -d "$srcdir/ares"; then + aresembedded="yes" + AC_CONFIG_SUBDIRS(ares) + aresinc=`cd $srcdir/ares && pwd` + CPPFLAGS="$CPPFLAGS -I$aresinc" + + dnl the pwd= below cannot 'cd' into the ares dir to get the full + dnl path to it, since it may not exist yet if we build outside of + dnl the source tree + pwd=`pwd` + LDFLAGS="$LDFLAGS -L$pwd/ares" + fi + else + CPPFLAGS="$CPPFLAGS -I$enableval/include" + LDFLAGS="$LDFLAGS -L$enableval/lib" + fi + + if test -z "$aresembedded"; then + dnl verify that a sufficient c-ares is here if we have pointed one + dnl out and don't use the "embedded" ares dir (in which case we don't + dnl check it because it might not have been built yet) + AC_MSG_CHECKING([that c-ares is good and recent enough]) + AC_LINK_IFELSE( [ +#include +/* provide a set of dummy functions in case c-ares was built with debug */ +void curl_dofree() { } +void curl_sclose() { } +void curl_domalloc() { } + +int main(void) +{ + ares_channel channel; + ares_cancel(channel); + return 0; +} +], + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + AC_MSG_ERROR([c-ares library defective or too old]) + ) + fi + ;; + esac ], + AC_MSG_RESULT(no) +) + +dnl ************************************************************ +dnl lame option to switch on debug options +dnl +AC_MSG_CHECKING([whether to enable debug options]) +AC_ARG_ENABLE(debug, +AC_HELP_STRING([--enable-debug],[Enable pedantic debug options]) +AC_HELP_STRING([--disable-debug],[Disable debug options]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + ;; + *) AC_MSG_RESULT(yes) + + CPPFLAGS="$CPPFLAGS -DCURLDEBUG" + CFLAGS="$CFLAGS -g" + + dnl set compiler "debug" options to become more picky, and remove + dnl optimize options from CFLAGS + CURL_CC_DEBUG_OPTS + ;; + esac + ], + AC_MSG_RESULT(no) +) + +AM_CONDITIONAL(CROSSCOMPILING, test x$cross_compiling = xyes) + +AC_CONFIG_FILES([Makefile \ + docs/Makefile \ + docs/examples/Makefile \ + docs/libcurl/Makefile \ + include/Makefile \ + include/curl/Makefile \ + src/Makefile \ + lib/Makefile \ + tests/Makefile \ + tests/data/Makefile \ + tests/server/Makefile \ + tests/libtest/Makefile \ + packages/Makefile \ + packages/Win32/Makefile \ + packages/Win32/cygwin/Makefile \ + packages/Linux/Makefile \ + packages/Linux/RPM/Makefile \ + packages/Linux/RPM/curl.spec \ + packages/Linux/RPM/curl-ssl.spec \ + packages/Solaris/Makefile \ + packages/DOS/Makefile \ + packages/EPM/curl.list \ + packages/EPM/Makefile \ + packages/vms/Makefile \ + curl-config +]) +AC_OUTPUT + +AC_MSG_NOTICE([Configured to build curl/libcurl: + + curl version: ${VERSION} + Host setup: ${host} + Install prefix: ${prefix} + Compiler: ${CC} + SSL support: ${curl_ssl_msg} + zlib support: ${curl_zlib_msg} + krb4 support: ${curl_krb4_msg} + GSSAPI support: ${curl_gss_msg} + SNPEGO support: ${curl_spnego_msg} + c-ares support: ${curl_ares_msg} + ipv6 support: ${curl_ipv6_msg} + IDN support: ${curl_idn_msg} + Build libcurl: Shared=${enable_shared}, Static=${enable_static} + Built-in manual: ${curl_manual_msg} +]) diff --git a/src/curl-7.12.2/curl-config.in b/src/curl-7.12.2/curl-config.in new file mode 100644 index 0000000..740725a --- /dev/null +++ b/src/curl-7.12.2/curl-config.in @@ -0,0 +1,133 @@ +#! /bin/sh +# +# The idea to this kind of setup info script was stolen from numerous +# other packages, such as neon, libxml and gnome. +# +# $Id: curl-config.in,v 1.18 2003/12/08 10:00:21 bagder Exp $ +# +prefix=@prefix@ +exec_prefix=@exec_prefix@ +includedir=@includedir@ + +usage() +{ + cat <. + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit 0 + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit 0 + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +if test -z "$depfile"; then + base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'` + dir=`echo "$object" | sed 's,/.*$,/,'` + if test "$dir" = "$object"; then + dir= + fi + # FIXME: should be _deps on DOS. + depfile="$dir.deps/$base" +fi + +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + tmpdepfile1="$dir.libs/$base.lo.d" + tmpdepfile2="$dir.libs/$base.d" + "$@" -Wc,-MD + else + tmpdepfile1="$dir$base.o.d" + tmpdepfile2="$dir$base.d" + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/curl-7.12.2/docs/BINDINGS b/src/curl-7.12.2/docs/BINDINGS new file mode 100644 index 0000000..049aa91 --- /dev/null +++ b/src/curl-7.12.2/docs/BINDINGS @@ -0,0 +1,141 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + libcurl bindings + +Creative people have written bindings or interfaces for various environments +and programming languages. Using one of these allows you to take advantage of +curl powers from within your favourite language or system. + +This is a list of all known interfaces as of this writing. + +The bindings listed below are not part of the curl/libcurl distribution +archives, but must be downloaded and installed separately. + +Ada95 + + Writtten by Andreas Almroth + http://www.almroth.com/adacurl/index.html + +Basic + + ScriptBasic bindings to libcurl. Writtten by Peter Verhas + http://scriptbasic.com/ + +C++ + + Written by Jean-Philippe Barrette-LaPierre + http://www.sourceforge.net/projects/curlpp + +Ch + + Written by Stephen Nestinger and Jonathan Rogado + http://chcurl.sourceforge.net/ + +Cocoa + + Written by Dan Wood + http://curlhandle.sourceforge.net/ + +D + + Written by Charles Sanders and James Wavro + http://www.atari-soldiers.com/libcurl.html + +Dylan + + Written by Chris Double + http://dylanlibs.sourceforge.net/ + +Euphoria + + Written by Ray Smith + http://rays-web.com/eulibcurl.htm + +Ferite + Written by Paul Querna + http://www.ferite.org/ + +Gambas + http://gambas.sourceforge.net + +glib/GTK+ + Written by Richard Atterer + http://atterer.net/glibcurl/ + +Java + + Written by Daniel Stenberg + http://curl.haxx.se/libcurl/java/ + +Lua + + Written by Steve Dekorte + http://curl.haxx.se/libcurl/lua/ + +Object-Pascal + + Free Pascal, Delphi and Kylix binding written by Christophe Espern. + http://www.tekool.com/opcurl + +O'Caml + + Written by Lars Nilsson + http://sourceforge.net/projects/ocurl/ + +Pascal + + Free Pascal, Delphi and Kylix binding written by Jeffrey Pohlmeyer. + http://houston.quik.com/jkp/curlpas/ + +Perl + + Maintained by Cris Bailiff + http://curl.haxx.se/libcurl/perl/ + +PHP + + Written by Sterling Hughes + http://curl.haxx.se/libcurl/php/ + +PostgreSQL + + Written by Gian Paolo Ciceri + http://gborg.postgresql.org/project/pgcurl/projdisplay.php + +Python + + Written by Kjetil Jacobsen + http://pycurl.sourceforge.net/ + +Rexx + + Written Mark Hessling + http://rexxcurl.sourceforge.net/ + +Ruby + + Written by Hirotaka Matsuyuki + http://www.d1.dion.ne.jp/~matuyuki/ruby.html + +Scheme + + Bigloo binding written by Kirill Lisovsky + http://curl.haxx.se/libcurl/scheme/ + +Tcl + + Written by Andrés García + http://personal1.iddeo.es/andresgarci/tclcurl/english/docs.html + +Q + + http://q-lang.sourceforge.net/ + +wxWidgets + + Written by Casey O'Donnell + http://homepage.mac.com/codonnell/wxcurldav/ diff --git a/src/curl-7.12.2/docs/BUGS b/src/curl-7.12.2/docs/BUGS new file mode 100644 index 0000000..d1448a0 --- /dev/null +++ b/src/curl-7.12.2/docs/BUGS @@ -0,0 +1,81 @@ +$Id: BUGS,v 1.7 2003/08/18 15:24:46 bagder Exp $ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +BUGS + + Curl and libcurl have grown substantially since the beginning. At the time + of writing (August 2003), there are about 40000 lines of source code, and by + the time you read this it has probably grown even more. + + Of course there are lots of bugs left. And lots of misfeatures. + + To help us make curl the stable and solid product we want it to be, we need + bug reports and bug fixes. + +WHERE TO REPORT + + If you can't fix a bug yourself and submit a fix for it, try to report an as + detailed report as possible to a curl mailing list to allow one of us to + have a go at a solution. You should also post your bug/problem at curl's bug + tracking system over at + + http://sourceforge.net/bugs/?group_id=976 + + (but please read the sections below first before doing that) + + If you feel you need to ask around first, find a suitable mailing list and + post there. The lists are available on http://curl.haxx.se/mail/ + +WHAT TO REPORT + + When reporting a bug, you should include all information that will help us + understand what's wrong, what you expected to happen and how to repeat the + bad behavior. You therefore need to tell us: + + - your operating system's name and version number (uname -a under a unix + is fine) + - what version of curl you're using (curl -V is fine) + - what URL you were working with (if possible), at least which protocol + + and anything and everything else you think matters. Tell us what you + expected to happen, tell use what did happen, tell us how you could make it + work another way. Dig around, try out, test. Then include all the tiny bits + and pieces in your report. You will benefit from this yourself, as it will + enable us to help you quicker and more accurately. + + Since curl deals with networks, it often helps us if you include a protocol + debug dump with your bug report. The output you get by using the -v or + --trace options. + + If curl crashed, causing a core dump (in unix), there is hardly any use to + send that huge file to anyone of us. Unless we have an exact same system + setup as you, we can't do much with it. Instead we ask you to get a stack + trace and send that (much smaller) output to us instead! + + The address and how to subscribe to the mailing lists are detailed in the + MANUAL file. + +HOW TO GET A STACK TRACE + + First, you must make sure that you compile all sources with -g and that you + don't 'strip' the final executable. Try to avoid optimizing the code as + well, remove -O, -O2 etc from the compiler options. + + Run the program until it cores. + + Run your debugger on the core file, like ' curl core'. + should be replaced with the name of your debugger, in most cases that will + be 'gdb', but 'dbx' and others also occur. + + When the debugger has finished loading the core file and presents you a + prompt, enter 'where' (without the quotes) and press return. + + The list that is presented is the stack trace. If everything worked, it is + supposed to contain the chain of functions that were called when curl + crashed. Include the stack trace with your detailed bug report. It'll help a + lot. + diff --git a/src/curl-7.12.2/docs/CONTRIBUTE b/src/curl-7.12.2/docs/CONTRIBUTE new file mode 100644 index 0000000..4747bd5 --- /dev/null +++ b/src/curl-7.12.2/docs/CONTRIBUTE @@ -0,0 +1,159 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +To Think About When Contributing Source Code + + This document is intended to offer some simple guidelines that can be useful + to keep in mind when you decide to contribute to the project. This concerns + new features as well as corrections to existing flaws or bugs. + +Join the Community + + Skip over to http://curl.haxx.se/mail/ and join the appropriate mailing + list(s). Read up on details before you post questions. Read this file before + you start sending patches! We prefer patches and discussions being held on + the mailing list(s), not sent to individuals. + +The License Issue + + When contributing with code, you agree to put your changes and new code under + the same license curl and libcurl is already using unless stated otherwise. + + If you add a larger piece of code, you can opt to make that file or set of + files to use a different license as long as they don't enforce any changes to + the rest of the package and they make sense. Such "separate parts" can not be + GPL (as we don't want the GPL virus to attack users of libcurl) but they must + use "GPL compatible" licenses. + +What To Read + + Source code, the man pages, the INTERNALS document, the TODO, the most recent + CHANGES. Just lurking on the libcurl mailing list is gonna give you a lot of + insights on what's going on right now. Asking there is a good idea too. + +Naming + + Try using a non-confusing naming scheme for your new functions and variable + names. It doesn't necessarily have to mean that you should use the same as in + other places of the code, just that the names should be logical, + understandable and be named according to what they're used for. File-local + functions should be made static. We like lower case names. + + See the INTERNALS document on how we name non-exported library-global + symbols. + +Indenting + + Please try using the same indenting levels and bracing method as all the + other code already does. It makes the source code a lot easier to follow if + all of it is written using the same style. We don't ask you to like it, we + just ask you to follow the tradition! ;-) This mainly means: 2-level indents, + using spaces only (no tabs) and having the opening brace ({) on the same line + as the if() or while(). + +Commenting + + Comment your source code extensively using C comments (/* comment */), DO NOT + use C++ comments (// this style). Commented code is quality code and enables + future modifications much more. Uncommented code risk having to be completely + replaced when someone wants to extend things, since other persons' source + code can get quite hard to read. + +General Style + + Keep your functions small. If they're small you avoid a lot of mistakes and + you don't accidentally mix up variables etc. + +Non-clobbering All Over + + When you write new functionality or fix bugs, it is important that you don't + fiddle all over the source files and functions. Remember that it is likely + that other people have done changes in the same source files as you have and + possibly even in the same functions. If you bring completely new + functionality, try writing it in a new source file. If you fix bugs, try to + fix one bug at a time and send them as separate patches. + +Platform Dependent Code + + Use #ifdef HAVE_FEATURE to do conditional code. We avoid checking for + particular operating systems or hardware in the #ifdef lines. The + HAVE_FEATURE shall be generated by the configure script for unix-like systems + and they are hard-coded in the config-[system].h files for the others. + +Separate Patches + + It is annoying when you get a huge patch from someone that is said to fix 511 + odd problems, but discussions and opinions don't agree with 510 of them - or + 509 of them were already fixed in a different way. Then the patcher needs to + extract the single interesting patch from somewhere within the huge pile of + source, and that gives a lot of extra work. Preferably, all fixes that + correct different problems should be in their own patch with an attached + description exactly what they correct so that all patches can be selectively + applied by the maintainer or other interested parties. + +Patch Against Recent Sources + + Please try to get the latest available sources to make your patches + against. It makes the life of the developers so much easier. The very best is + if you get the most up-to-date sources from the CVS repository, but the + latest release archive is quite OK as well! + +Document + + Writing docs is dead boring and one of the big problems with many open source + projects. Someone's gotta do it. It makes it a lot easier if you submit a + small description of your fix or your new features with every contribution so + that it can be swiftly added to the package documentation. + + The documentation is always made in man pages (nroff formatted) or plain + ASCII files. All HTML files on the web site and in the release archives are + generated from the nroff/ASCII versions. + +Write Access to CVS Repository + + If you are a frequent contributor, or have another good reason, you can of + course get write access to the CVS repository and then you'll be able to + check-in all your changes straight into the CVS tree instead of sending all + changes by mail as patches. Just ask if this is what you'd want. You will be + required to have posted a few quality patches first, before you can be + granted write access. + +Test Cases + + Since the introduction of the test suite, we can quickly verify that the main + features are working as they're supposed to. To maintain this situation and + improve it, all new features and functions that are added need to be tested + in the test suite. Every feature that is added should get at least one valid + test case that verifies that it works as documented. If every submitter also + posts a few test cases, it won't end up as a heavy burden on a single person! + +How To Make a Patch + + Keep a copy of the unmodified curl sources. Make your changes in a separate + source tree. When you think you have something that you want to offer the + curl community, use GNU diff to generate patches. + + If you have modified a single file, try something like: + + diff -u unmodified-file.c my-changed-one.c > my-fixes.diff + + If you have modified several files, possibly in different directories, you + can use diff recursively: + + diff -ur curl-original-dir curl-modified-sources-dir > my-fixes.diff + + The GNU diff and GNU patch tools exist for virtually all platforms, including + all kinds of Unixes and Windows: + + For unix-like operating systems: + + http://www.fsf.org/software/patch/patch.html + http://www.gnu.org/directory/diffutils.html + + For Windows: + + http://gnuwin32.sourceforge.net/packages/patch.htm + http://gnuwin32.sourceforge.net/packages/diffutils.htm diff --git a/src/curl-7.12.2/docs/FAQ b/src/curl-7.12.2/docs/FAQ new file mode 100644 index 0000000..6d8deb2 --- /dev/null +++ b/src/curl-7.12.2/docs/FAQ @@ -0,0 +1,920 @@ +Updated: August 18, 2004 (http://curl.haxx.se/docs/faq.html) + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +FAQ + + 1. Philosophy + 1.1 What is cURL? + 1.2 What is libcurl? + 1.3 What is curl not? + 1.4 When will you make curl do XXXX ? + 1.5 Who makes curl? + 1.6 What do you get for making curl? + 1.7 What about CURL from curl.com? + 1.8 I have a problem who do I mail? + + 2. Install Related Problems + 2.1 configure doesn't find OpenSSL even when it is installed + 2.1.1 native linker doesn't find OpenSSL + 2.1.2 only the libssl lib is missing + 2.2 Does curl work/build with other SSL libraries? + 2.3 Where can I find a copy of LIBEAY32.DLL? + 2.4 Does curl support Socks (RFC 1928) ? + + 3. Usage Problems + 3.1 curl: (1) SSL is disabled, https: not supported + 3.2 How do I tell curl to resume a transfer? + 3.3 Why doesn't my posting using -F work? + 3.4 How do I tell curl to run custom FTP commands? + 3.5 How can I disable the Pragma: nocache header? + 3.6 Does curl support ASP, XML, XHTML or HTML version Y? + 3.7 Can I use curl to delete/rename a file through FTP? + 3.8 How do I tell curl to follow HTTP redirects? + 3.9 How do I use curl in my favorite programming language? + 3.10 What about SOAP, WebDAV, XML-RPC or similar protocols over HTTP? + 3.11 How do I POST with a different Content-Type? + 3.12 Why do FTP specific features over HTTP proxy fail? + 3.13 Why does my single/double quotes fail? + 3.14 Does curl support javascript or pac (automated proxy config)? + 3.15 Can I do recursive fetches with curl? + 3.16 What certificates do I need when I use SSL? + + 4. Running Problems + 4.1 Problems connecting to SSL servers. + 4.2 Why do I get problems when I use & or % in the URL? + 4.3 How can I use {, }, [ or ] to specify multiple URLs? + 4.4 Why do I get downloaded data even though the web page doesn't exist? + 4.5 Why do I get return code XXX from a HTTP server? + 4.5.1 "400 Bad Request" + 4.5.2 "401 Unauthorized" + 4.5.3 "403 Forbidden" + 4.5.4 "404 Not Found" + 4.5.5 "405 Method Not Allowed" + 4.5.6 "301 Moved Permanently" + 4.6 Can you tell me what error code 142 means? + 4.7 How do I keep user names and passwords secret in Curl command lines? + 4.8 I found a bug! + 4.9 Curl can't authenticate to the server that requires NTLM? + 4.10 My HTTP request using HEAD, PUT or DELETE doesn't work! + 4.11 Why does my HTTP range requests return the full document? + 4.12 Why do I get "certificate verify failed" ? + + 5. libcurl Issues + 5.1 Is libcurl thread-safe? + 5.2 How can I receive all data into a large memory chunk? + 5.3 How do I fetch multiple files with libcurl? + 5.4 Does libcurl do Winsock initing on win32 systems? + 5.5 Does CURLOPT_WRITEDATA and CURLOPT_READDATA work on win32 ? + 5.6 What about Keep-Alive or persistent connections? + 5.7 Link errors when building libcurl on Windows! + 5.8 libcurl.so.3: open failed: No such file or directory + 5.9 How does libcurl resolve host names? + + 6. License Issues + 6.1 I have a GPL program, can I use the libcurl library? + 6.2 I have a closed-source program, can I use the libcurl library? + 6.3 I have a BSD licensed program, can I use the libcurl library? + 6.4 I have a program that uses LGPL libraries, can I use libcurl? + 6.5 Can I modify curl/libcurl for my program and keep the changes secret? + 6.6 Can you please change the curl/libcurl license to XXXX? + + 7. PHP/CURL Issues + 7.1 What is PHP/CURL? + 7.2 Who write PHP/CURL? + 7.3 Can I perform multiple requests using the same handle? + +============================================================================== + +1. Philosophy + + 1.1 What is cURL? + + cURL is the name of the project. The name is a play on 'Client for URLs', + originally with URL spelled in uppercase to make it obvious it deals with + URLs. The fact it can also be pronounced 'see URL' also helped, it works as + an abbrivation for "Client URL Request Library" or why not the recursive + version: "Curl URL Request Library". + + The cURL project produces two products: + + libcurl + + A free and easy-to-use client-side URL transfer library, supporting FTP, + FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl supports + HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, kerberos, HTTP + form based upload, proxies, cookies, user+password authentication, file + transfer resume, http proxy tunneling and more! + + libcurl is highly portable, it builds and works identically on numerous + platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, + IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac + OS X, Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS and more... + + libcurl is free, thread-safe, IPv6 compatible, feature rich, well + supported and fast. + + curl + + A command line tool for getting or sending files using URL syntax. + + Since curl uses libcurl, it supports a range of common Internet protocols, + currently including HTTP, HTTPS, FTP, FTPS, GOPHER, LDAP, DICT, TELNET and + FILE. + + We pronounce curl and cURL with an initial k sound: [kurl]. + + NOTE: there are numerous sub-projects and related projects that also use the + word curl in the project names in various combinations, but you should take + notice that this FAQ is directed at the command-line tool named curl (and + libcurl the library), and may therefore not be valid for other curl-related + projects. + + 1.2 What is libcurl? + + libcurl is a reliable and portable library which provides you with an easy + interface to a range of common Internet protocols. + + You can use libcurl for free in your application, be it open source, + commercial or closed-source. + + 1.3 What is curl not? + + Curl is *not* a wget clone. That is a common misconception. Never, during + curl's development, have we intended curl to replace wget or compete on its + market. Curl is targeted at single-shot file transfers. + + Curl is not a web site mirroring program. If you want to use curl to mirror + something: fine, go ahead and write a script that wraps around curl to make + it reality (like curlmirror.pl does). + + Curl is not an FTP site mirroring program. Sure, get and send FTP with curl + but if you want systematic and sequential behavior you should write a + script (or write a new program that interfaces libcurl) and do it. + + Curl is not a PHP tool, even though it works perfectly well when used from + or with PHP (when using the PHP/CURL module). + + Curl is not a single-OS program. Curl exists, compiles, builds and runs + under a wide range of operating systems, including all modern Unixes (and a + bunch of older ones too), Windows, Amiga, BeOS, OS/2, OS X, QNX etc. + + 1.4 When will you make curl do XXXX ? + + We love suggestions of what to change in order to make curl and libcurl + better. We do however believe in a few rules when it comes to the future of + curl: + + * Curl -- the command line tool -- is to remain a non-graphical command line + tool. If you want GUIs or fancy scripting capabilities, you should look + for another tool that uses libcurl. + + * We do not add things to curl that other small and available tools already + do very fine at the side. Curl's output is fine to pipe into another + program or redirect to another file for the next program to interpret. + + * We focus on protocol related issues and improvements. If you wanna do more + magic with the supported protocols than curl currently does, chances are + big we will agree. If you wanna add more protocols, we may very well + agree. + + * If you want someone else to make all the work while you wait for us to + implement it for you, that is not a very friendly attitude. We spend a + considerable time already on maintaining and developing curl. In order to + get more out of us, you should consider trading in some of your time and + efforts in return. + + * If you write the code, chances are bigger that it will get into curl + faster. + + 1.5 Who makes curl? + + curl and libcurl are not made by any single individual. Sure, Daniel + Stenberg writes the major parts, but other persons' submissions are + important and crucial. Anyone can contribute and post their changes and + improvements and have them inserted in the main sources (of course on the + condition that developers agree on that the fixes are good). + + The list of contributors in the docs/THANKS file is only a small part of all + the people that every day provide us with bug reports, suggestions, ideas + and source code. + + curl is developed by a community, with Daniel at the wheel. + + 1.6 What do you get for making curl? + + Project cURL is entirely free and open. No person gets paid for developing + (lib)curl. We do this voluntarily on our spare time. + + We get some help from companies. Contactor Data hosts the curl web site, + Haxx owns the curl web site's domain and sourceforge.net hosts project + services we take advantage from, like the bug tracker. Also, some companies + have sponsored certain parts of the development in the past and I hope some + will continue to do so in the future. + + If you want to support our project with a donation or similar, one way of + doing that would be to buy "gift certificates" at useful online shopping + sites, such as amazon.com or thinkgeek.com. Another way would be to sponsor + us through a banner-program or even better: by helping us coding, + documenting, testing etc. You're welcome to send us a buck using paypal, as + described here: http://curl.haxx.se/donation.html + + 1.7 What about CURL from curl.com? + + During the summer 2001, curl.com was busy advertising their client-side + programming language for the web, named CURL. + + We are in no way associated with curl.com or their CURL programming + language. + + Our project name curl has been in effective use since 1998. We were not the + first computer related project to use the name "curl" and do not claim any + first-hand rights to the name. + + We recognize that we will be living in parallel with curl.com and wish them + every success. + + 1.8 I have a problem who do I mail? + + Please do not mail any single individual unless you really need to. Keep + curl-related questions on a suitable mailing list. All available mailing + lists are listed in the MANUAL document and online at + http://curl.haxx.se/mail/ + + Keeping curl-related questions and discussions on mailing lists allows + others to join in and help, to share their ideas, contribute their + suggestions and spread their wisdom. Keeping discussions on public mailing + lists also allows for others to learn from this (both current and future + users thanks to the web based archives of the mailing lists), thus saving us + from having to repeat ourselves even more. Thanks for respecting this. + + +2. Install Related Problems + + 2.1 configure doesn't find OpenSSL even when it is installed + + This may be because of several reasons. + + 2.1.1 native linker doesn't find openssl + + Affected platforms: + Solaris (native cc compiler) + HPUX (native cc compiler) + SGI IRIX (native cc compiler) + SCO UNIX (native cc compiler) + + When configuring curl, I specify --with-ssl. OpenSSL is installed in + /usr/local/ssl Configure reports SSL in /usr/local/ssl, but fails to find + CRYPTO_lock in -lcrypto + + Cause: The cc for this test places the -L/usr/local/ssl/lib AFTER + -lcrypto, so ld can't find the library. This is due to a bug in the GNU + autoconf tool. + + Workaround: Specifying "LDFLAGS=-L/usr/local/ssl/lib" in front of + ./configure places the -L/usr/local/ssl/lib early enough in the command + line to make things work + + Solution submitted by: Bob Allison + + 2.1.2 only the libssl lib is missing + + If all include files and the libcrypto lib is present, with only the + libssl being missing according to configure, this is mostly likely because + a few functions are left out from the libssl. + + If the function names missing include RSA or RSAREF you can be certain + that this is because libssl requires the RSA and RSAREF libs to build. + + See the INSTALL file section that explains how to add those libs to + configure. Make sure that you remove the config.cache file before you + rerun configure with the new flags. + + 2.2 Does curl work/build with other SSL libraries? + + Curl has been written to use OpenSSL, although there should not be much + problems using a different library. If anyone does "port" curl to use a + different SSL library, we are of course very interested in getting the + patch! + + 2.3 Where can I find a copy of LIBEAY32.DLL? + + That is an OpenSSL binary built for Windows. + + Curl uses OpenSSL to do the SSL stuff. The LIBEAY32.DLL is what curl needs + on a windows machine to do https://. Check out the curl web site to find + accurate and up-to-date pointers to recent OpenSSL DLLs and other binary + packages. + + 2.4 Does curl support Socks (RFC 1928) ? + + Yes, SOCKS5 is supported. + + +3. Usage problems + + 3.1 curl: (1) SSL is disabled, https: not supported + + If you get this output when trying to get anything from a https:// server, + it means that the configure script couldn't find all libs and include files + it requires for SSL to work. If the configure script fails to find them, + curl is simply built without SSL support. + + To get the https:// support into a curl that was previously built but that + reports that https:// is not supported, you should dig through the document + and logs and check out why the configure script doesn't find the SSL libs + and/or include files. + + Also, check out the other paragraph in this FAQ labeled "configure doesn't + find OpenSSL even when it is installed". + + 3.2 How do I tell curl to resume a transfer? + + Curl supports resumed transfers both ways on both FTP and HTTP. + + Try the -C option. + + 3.3 Why doesn't my posting using -F work? + + You can't simply use -F or -d at your choice. The web server that will + receive your post assumes one of the formats. If the form you're trying to + "fake" sets the type to 'multipart/form-data', then and only then you must + use the -F type. In all the most common cases, you should use -d which then + causes a posting with the type 'application/x-www-form-urlencoded'. + + This is described in some detail in the MANUAL and TheArtOfHttpScripting + documents, and if you don't understand it the first time, read it again + before you post questions about this to the mailing list. Also, try reading + through the mailing list archives for old postings and questions regarding + this. + + 3.4 How do I tell curl to run custom FTP commands? + + You can tell curl to perform optional commands both before and/or after a + file transfer. Study the -Q/--quote option. + + Since curl is used for file transfers, you don't use curl to just perform + FTP commands without transferring anything. Therefore you must always specify + a URL to transfer to/from even when doing custom FTP commands. + + 3.5 How can I disable the Pragma: nocache header? + + You can change all internally generated headers by adding a replacement with + the -H/--header option. By adding a header with empty contents you safely + disable that one. Use -H "Pragma:" to disable that specific header. + + 3.6 Does curl support ASP, XML, XHTML or HTML version Y? + + To curl, all contents are alike. It doesn't matter how the page was + generated. It may be ASP, PHP, Perl, shell-script, SSI or plain + HTML-files. There's no difference to curl and it doesn't even know what kind + of language that generated the page. + + See also item 3.14 regarding javascript. + + 3.7 Can I use curl to delete/rename a file through FTP? + + Yes. You specify custom FTP commands with -Q/--quote. + + One example would be to delete a file after you have downloaded it: + + curl -O ftp://download.com/coolfile -Q '-DELE coolfile' + + 3.8 How do I tell curl to follow HTTP redirects? + + Curl does not follow so-called redirects by default. The Location: header + that informs the client about this is only interpreted if you're using the + -L/--location option. As in: + + curl -L http://redirector.com + + 3.9 How do I use curl in my favorite programming language? + + There exist many language interfaces/bindings for curl that integrates it + better with various languages. If you are fluid in a script language, you + may very well opt to use such an interface instead of using the command line + tool. + + Find out more about which languages that support curl directly, and how to + install and use them, in the libcurl section of the curl web site: + http://curl.haxx.se/libcurl/ + + In February 2003, there are interfaces available for the following + languages: Basic, C, C++, Cocoa, Dylan, Euphoria, Java, Lua, Object-Pascal, + Pascal, Perl, PHP, PostgreSQL, Python, Rexx, Ruby, Scheme and Tcl. By the + time you read this, additional ones may have appeared! + + 3.10 What about SOAP, WebDAV, XML-RPC or similar protocols over HTTP? + + Curl adheres to the HTTP spec, which basically means you can play with *any* + protocol that is built on top of HTTP. Protocols such as SOAP, WEBDAV and + XML-RPC are all such ones. You can use -X to set custom requests and -H to + set custom headers (or replace internally generated ones). + + Using libcurl is of course just as fine and you'd just use the proper + library options to do the same. + + 3.11 How do I POST with a different Content-Type? + + You can always replace the internally generated headers with -H/--header. + To make a simple HTTP POST with text/xml as content-type, do something like: + + curl -d "datatopost" -H "Content-Type: text/xml" [URL] + + 3.12 Why do FTP specific features over HTTP proxy fail? + + Because when you use a HTTP proxy, the protocol spoken on the network will + be HTTP, even if you specify a FTP URL. This effectively means that you + normally can't use FTP specific features such as FTP upload and FTP quote + etc. + + There is one exception to this rule, and that is if you can "tunnel through" + the given HTTP proxy. Proxy tunneling is enabled with a special option (-p) + and is generally not available as proxy admins usually disable tunneling to + other ports than 443 (which is used for HTTPS access through proxies). + + 3.13 Why does my single/double quotes fail? + + To specify a command line option that includes spaces, you might need to + put the entire option within quotes. Like in: + + curl -d " with spaces " url.com + + or perhaps + + curl -d ' with spaces ' url.com + + Exactly what kind of quotes and how to do this is entirely up to the shell + or command line interpreter that you are using. For most unix shells, you + can more or less pick either single (') or double (") quotes. For + Windows/DOS prompts I believe you're forced to use double (") quotes. + + Please study the documentation for your particular environment. Examples in + the curl docs will use a mix of both these ones as shown above. You must + adjust them to work in your environment. + + Remember that curl works and runs on more operating systems than most single + individuals have ever tried. + + 3.14 Does curl support javascript or pac (automated proxy config)? + + Many web pages do magic stuff using embedded javascript. Curl and libcurl + have no built-in support for that, so it will be treated just like any other + contents. + + .pac files are a netscape invention and are sometimes used by organizations + to allow them to differentiate which proxies to use. The .pac contents is + just a javascript program that gets invoked by the browser and that returns + the name of the proxy to connect to. Since curl doesn't support javascript, + it can't support .pac proxy configuration either. + + Some work-arounds usually suggested to overcome this javascript dependency: + + - Depending on the javascript complexity, write up a script that + translates it to another language and execute that. + + - Read the javascript code and rewrite the same logic in another language. + + - Implement a javascript interpreter, people have successfully used the + Mozilla javascript engine in the past. + + - Ask your admins to stop this, for a static proxy setup or similar. + + 3.15 Can I do recursive fetches with curl? + + No. curl itself has no code that performs recursive operations, such as + those performed by wget and similar tools. + + There exist wrapper scripts with that functionality (for example the + curlmirror perl script), and you can write programs based on libcurl to do + it, but the command line tool curl itself cannot. + + 3.16 What certificates do I need when I use SSL? + + There are three different kinds of "certificates" to keep track of when we + talk about using SSL-based protocols (HTTPS or FTPS) using curl or libcurl. + + - Client certificate. The server you communicate may require that you can + provide this in order to prove that you actually are who you claim to be. + If the server doesn't require this, you don't need a client certificate. + + - Server certificate. The server you communicate with has a server + certificate. You can and should verify this certficate to make sure that + you are truly talking to the real server and not a server impersonating + it. The server certificate verifaction process is made by using a + Certificate Authority certificate ("CA cert") that was used to sign the + server certificate. Server certificate verification is enabled by default + in curl and libcurl and is often the reason for problems as explained in + FAQ entry 4.12 and the SSLCERTS document + (http://curl.haxx.se/docs/sslcerts.html). Server certificates that are + "self-signed" or otherwise signed by a CA that you do not have a CA cert + for, cannot be verified. If the verification during a connect fails, you + are refused access. You then need to explicitly disable the verification + to connect to the server. + + - Certificate Authority certificate ("CA cert"). You often have several CA + certs in a CA cert bundle that can be used to verify a server certificate + that was signed by one of the authorities in the bundle. curl comes with a + default CA cert bundle. You can override the default. + + +4. Running Problems + + 4.1 Problems connecting to SSL servers. + + It took a very long time before we could sort out why curl had problems to + connect to certain SSL servers when using SSLeay or OpenSSL v0.9+. The + error sometimes showed up similar to: + + 16570:error:1407D071:SSL routines:SSL2_READ:bad mac decode:s2_pkt.c:233: + + It turned out to be because many older SSL servers don't deal with SSLv3 + requests properly. To correct this problem, tell curl to select SSLv2 from + the command line (-2/--sslv2). + + There have also been examples where the remote server didn't like the SSLv2 + request and instead you had to force curl to use SSLv3 with -3/--sslv3. + + 4.2 Why do I get problems when I use & or % in the URL? + + In general unix shells, the & letter is treated special and when used, it + runs the specified command in the background. To safely send the & as a part + of a URL, you should quote the entire URL by using single (') or double (") + quotes around it. + + An example that would invoke a remote CGI that uses &-letters could be: + + curl 'http://www.altavista.com/cgi-bin/query?text=yes&q=curl' + + In Windows, the standard DOS shell treats the %-letter specially and you + need to use TWO %-letters for each single one you want to use in the URL. + + Also note that if you want the literal %-letter to be part of the data you + pass in a POST using -d/--data you must encode it as '%25' (which then also + needs the %-letter doubled on Windows machines). + + 4.3 How can I use {, }, [ or ] to specify multiple URLs? + + Because those letters have a special meaning to the shell, and to be used in + a URL specified to curl you must quote them. + + An example that downloads two URLs (sequentially) would do: + + curl '{curl,www}.haxx.se' + + To be able to use those letters as actual parts of the URL (without using + them for the curl URL "globbing" system), use the -g/--globoff option: + + curl -g 'www.site.com/weirdname[].html' + + 4.4 Why do I get downloaded data even though the web page doesn't exist? + + Curl asks remote servers for the page you specify. If the page doesn't exist + at the server, the HTTP protocol defines how the server should respond and + that means that headers and a "page" will be returned. That's simply how + HTTP works. + + By using the --fail option you can tell curl explicitly to not get any data + if the HTTP return code doesn't say success. + + 4.5 Why do I get return code XXX from a HTTP server? + + RFC2616 clearly explains the return codes. This is a short transcript. Go + read the RFC for exact details: + + 4.5.1 "400 Bad Request" + + The request could not be understood by the server due to malformed + syntax. The client SHOULD NOT repeat the request without modifications. + + 4.5.2 "401 Unauthorized" + + The request requires user authentication. + + 4.5.3 "403 Forbidden" + + The server understood the request, but is refusing to fulfill it. + Authorization will not help and the request SHOULD NOT be repeated. + + 4.5.4 "404 Not Found" + + The server has not found anything matching the Request-URI. No indication + is given of whether the condition is temporary or permanent. + + 4.5.5 "405 Method Not Allowed" + + The method specified in the Request-Line is not allowed for the resource + identified by the Request-URI. The response MUST include an Allow header + containing a list of valid methods for the requested resource. + + 4.5.6 "301 Moved Permanently" + + If you get this return code and an HTML output similar to this: + +

Moved Permanently

The document has moved
here. + + it might be because you request a directory URL but without the trailing + slash. Try the same operation again _with_ the trailing URL, or use the + -L/--location option to follow the redirection. + + 4.6 Can you tell me what error code 142 means? + + All error codes that are larger than the highest documented error code means + that curl has exited due to a crash. This is a serious error, and we + appreciate a detailed bug report from you that describes how we could go + ahead and repeat this! + + 4.7 How do I keep user names and passwords secret in Curl command lines? + + This problem has two sides: + + The first part is to avoid having clear-text passwords in the command line + so that they don't appear in 'ps' outputs and similar. That is easily + avoided by using the "-K" option to tell curl to read parameters from a file + or stdin to which you can pass the secret info. curl itself will also + attempt to "hide" the given password by blanking out the option - this + doesn't work on all platforms. + + To keep the passwords in your account secret from the rest of the world is + not a task that curl addresses. You could of course encrypt them somehow to + at least hide them from being read by human eyes, but that is not what + anyone would call security. + + Also note that regular HTTP (using Basic authentication) and FTP passwords + are sent in clear across the network. All it takes for anyone to fetch them + is to listen on the network. Eavesdropping is very easy. Use more secure + authentication methods (like Digest, Negotiate or even NTLM) or consider the + SSL-based alternatives HTTPS and FTPS. + + 4.8 I found a bug! + + It is not a bug if the behavior is documented. Read the docs first. + Especially check out the KNOWN_BUGS file, it may be a documented bug! + + If it is a problem with a binary you've downloaded or a package for your + particular platform, try contacting the person who built the package/archive + you have. + + If there is a bug, read the BUGS document first. Then report it as described + in there. + + 4.9 Curl can't authenticate to the server that requires NTLM? + + This is supported in curl 7.10.6 or later. No earlier curl version knows + of this magic. + + NTLM is a Microsoft proprietary protocol. Proprietary formats are evil. You + should not use such ones. + + 4.10 My HTTP request using HEAD, PUT or DELETE doesn't work! + + Many web servers allow or demand that the administrator configures the + server properly for these requests to work on the web server. + + Some servers seem to support HEAD only on certain kinds of URLs. + + To fully grasp this, try the documentation for the particular server + software you're trying to interact with. This is not anything curl can do + anything about. + + 4.11 Why does my HTTP range requests return the full document? + + Because the range may not be supported by the server, or the server may + choose to ignore it and return the full document anyway. + + 4.12 Why do I get "certificate verify failed" ? + + You invoke curl 7.10 or later to communicate on a https:// URL and get an + error back looking something similar to this: + + curl: (35) SSL: error:14090086:SSL routines: + SSL3_GET_SERVER_CERTIFICATE:certificate verify failed + + Then it means that curl couldn't verify that the server's certificate was + good. Curl verifies the certificate using the CA cert bundle that comes with + the curl installation. + + To disable the verification (which makes it act like curl did before 7.10), + use -k. This does however enable man-in-the-middle attacks. + + If you get this failure but are having a CA cert bundle installed and used, + the server's certificate is not signed by one of the CA's in the bundle. It + might for example be self-signed. You then correct this problem by obtaining + a valid CA cert for the server. Or again, decrease the security by disabling + this check. + + Details are also in the SSLCERTS file in the release archives, found online + here: http://curl.haxx.se/docs/sslcerts.html + + +5. libcurl Issues + + 5.1 Is libcurl thread-safe? + + Yes. + + We have written the libcurl code specificly adjusted for multi-threaded + programs. libcurl will use thread-safe functions instead of non-safe ones if + your system has such. + + We would appreciate some kind of report or README file from those who have + used libcurl in a threaded environment. + + 5.2 How can I receive all data into a large memory chunk? + + [ See also the examples/getinmemory.c source ] + + You are in full control of the callback function that gets called every time + there is data received from the remote server. You can make that callback do + whatever you want. You do not have to write the received data to a file. + + One solution to this problem could be to have a pointer to a struct that you + pass to the callback function. You set the pointer using the + curl_easy_setopt(CURLOPT_FILE) function. Then that pointer will be passed to + the callback instead of a FILE * to a file: + + /* imaginary struct */ + struct MemoryStruct { + char *memory; + size_t size; + }; + + /* imaginary callback function */ + size_t + WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) + { + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)data; + + mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; + } + + 5.3 How do I fetch multiple files with libcurl? + + libcurl has excellent support for transferring multiple files. You should + just repeatedly set new URLs with curl_easy_setopt() and then transfer it + with curl_easy_perform(). The handle you get from curl_easy_init() is not + only reusable, but you're even encouraged to reuse it if you can, as that + will enable libcurl to use persistent connections. + + 5.4 Does libcurl do Winsock initialization on win32 systems? + + Yes, if told to in the curl_global_init() call. + + 5.5 Does CURLOPT_WRITEDATA and CURLOPT_READDATA work on win32 ? + + Yes, but you cannot open a FILE * and pass the pointer to a DLL and have + that DLL use the FILE * (as the DLL and the client application cannot access + each others' variable memory areas). If you set CURLOPT_WRITEDATA you must + also use CURLOPT_WRITEFUNCTION as well to set a function that writes the + file, even if that simply writes the data to the specified FILE *. + Similarly, if you use CURLOPT_READDATA you must also specify + CURLOPT_READFUNCTION. + + (Provided by Joel DeYoung and Bob Schader) + + 5.6 What about Keep-Alive or persistent connections? + + curl and libcurl have excellent support for persistent connections when + transferring several files from the same server. Curl will attempt to reuse + connections for all URLs specified on the same command line/config file, and + libcurl will reuse connections for all transfers that are made using the + same libcurl handle. + + 5.7 Link errors when building libcurl on Windows! + + You need to make sure that your project, and all the libraries (both static + and dynamic) that it links against, are compiled/linked against the same run + time library. + + This is determined by the /MD, /ML, /MT (and their corresponding /M?d) + options to the command line compiler. /MD (linking against MSVCRT dll) seems + to be the most commonly used option. + + (Provided by Andrew Francis) + + 5.8 libcurl.so.3: open failed: No such file or directory + + This is an error message you might get when you try to run a program linked + with a shared version of libcurl and your run-time linker (ld.so) couldn't + find the shared library named libcurl.so.3. + + You need to make sure that ld.so finds libcurl.so.3. You can do that + multiple ways, and it differs somewhat between different operating systems, + but they are usually: + + * Add an option to the linker command line that specify the hard-coded path + the run-time linker should check for the lib (usually -R) + + * Set an environment variable (LD_LIBRARY_PATH for example) where ld.so + should check for libs + + * Adjust the system's config to check for libs in the directory where you've + put the dir (like Linux's /etc/ld.so.conf) + + 'man ld.so' and 'man ld' will tell you more details + + 5.9 How does libcurl resolve host names? + + libcurl includes a number of different name resolve functions: + + - The non-ipv6 resolver that can use one out of four host name resolve calls + (depending on what your system supports): + + A - gethostbyname() + B - gethostbyname_r() with 3 arguments + C - gethostbyname_r() with 5 arguments + D - gethostbyname_r() with 6 arguments + + - The ipv6-resolver that uses getaddrinfo() + + - The c-ares based name resolver that uses the c-ares library for resolves. + + - The Windows threaded resolver. It use: + + A - gethostbyname() on plain ipv4 windows hosts + B - getaddrinfo() on ipv6-enabled windows hosts + +6. License Issues + + Curl and libcurl are released under a MIT/X derivate license. The license is + very liberal and should not impose a problem for your project. This section + is just a brief summary for the cases we get the most questions. (Parts of + this section was much enhanced by Bjorn Reese.) + + 6.1 I have a GPL program, can I use the libcurl library? + + Yes! + + Since libcurl may be distributed under the MIT/X derivate license, it can be + used together with GPL in any software. + + 6.2 I have a closed-source program, can I use the libcurl library? + + Yes! + + libcurl does not put any restrictions on the program that uses the library. + + 6.3 I have a BSD licensed program, can I use the libcurl library? + + Yes! + + libcurl does not put any restrictions on the program that uses the library. + + 6.4 I have a program that uses LGPL libraries, can I use libcurl? + + Yes! + + The LGPL license doesn't clash with other licenses. + + 6.5 Can I modify curl/libcurl for my program and keep the changes secret? + + Yes! + + The MIT/X derivate license practically allows you to do almost anything with + the sources, on the condition that the copyright texts in the sources are + left intact. + + 6.6 Can you please change the curl/libcurl license to XXXX? + + No. + + We have carefully picked this license after years of development and + discussions and a large amount of people have contributed with source code + knowing that this is the license we use. This license puts the restrictions + we want on curl/libcurl and it does not spread to other programs or + libraries that use it. It should be possible for everyone to use libcurl or + curl in their projects, no matter what license they already have in use. + +7. PHP/CURL Issues + + 7.1 What is PHP/CURL? + + The module for PHP that makes it possible for PHP programs to access curl- + functions from within PHP. We often call it PHP/CURL to differentiate from + curl the command line tool and libcurl the library. + + 7.2 Who write PHP/CURL? + + PHP/CURL is a module that comes with the regular PHP package. It depends and + uses libcurl, so you need to have libcurl installed properly first before + PHP/CURL can be used. PHP/CURL is written by Sterling Hughes. + + 7.3 Can I perform multiple requests using the same handle? + + Yes - at least in PHP version 4.3.8 and later (this has been known to not + work in earlier versions, but the exact version when it started to work is + unknown to me). + + After a transfer, you just set new options in the handle and make another + transfer. This will make libcurl to re-use the same connection if it can. diff --git a/src/curl-7.12.2/docs/FEATURES b/src/curl-7.12.2/docs/FEATURES new file mode 100644 index 0000000..c9b1c96 --- /dev/null +++ b/src/curl-7.12.2/docs/FEATURES @@ -0,0 +1,126 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +FEATURES + +curl tool + - config file support + - multiple URLs in a single command line + - range "globbing" support: [0-13], {one,two,three} + - multiple file upload on a single command line + - custom maximum transfer rate + - redirectable stderr + +libcurl supports + - full URL syntax with no length limit + - custom maximum download time + - custom least download speed acceptable + - custom output result after completion + - guesses protocol from host name unless specified + - uses .netrc + - progress bar/time specs while downloading + - "standard" proxy environment variables support + - compiles on win32 (reported builds on 40+ operating systems) + - selectable network interface for outgoing traffic + - IPv6 support on unix and Windows + - persistant connections + - socks5 support + - supports user name + password in proxy environment variables + - operations through proxy "tunnel" (using CONNECT) + - supports large files (>2GB and >4GB) both upload/download + - replacable memory functions (malloc, free, realloc, etc) + - asynchronous name resolving (*6) + +HTTP + - HTTP/1.1 compliant (optionally uses 1.0) + - GET + - PUT + - HEAD + - POST + - multipart formpost (RFC1867-style) + - authentication: Basic, Digest, NTLM(*1), GSS-Negotiate/Negotiate(*3) and + SPNEGO (*4) to server and proxy + - resume (both GET and PUT) + - follow redirects + - maximum amount of redirects to follow + - custom HTTP request + - cookie get/send fully parsed + - reads/writes the netscape cookie file format + - custom headers (replace/remove internally generated headers) + - custom user-agent string + - custom referer string + - range + - proxy authentication + - time conditions + - via http-proxy + - retrieve file modification date + - Content-Encoding support for deflate and gzip + - "Transfer-Encoding: chunked" support for "uploads" + +HTTPS (*1) + - (all the HTTP features) + - using certificates + - verify server certificate + - via http-proxy + - select desired encryption + - force usage of a specific SSL version (SSLv2, SSLv3 or TLSv1) + +FTP + - download + - authentication + - kerberos4 (*5) + - active/passive using PORT, EPRT, PASV or EPSV + - single file size information (compare to HTTP HEAD) + - 'type=' URL support + - dir listing + - dir listing names-only + - upload + - upload append + - upload via http-proxy as HTTP PUT + - download resume + - upload resume + - custom ftp commands (before and/or after the transfer) + - simple "range" support + - via http-proxy + - all operations can be tunneled through a http-proxy + - customizable to retrieve file modification date + - third party transfers + - no dir depth limit + +FTPS (*1) + - explicit ftps:// support that use SSL on both connections + - implicit "AUTH TSL" and "AUTH SSL" usage to "upgrade" plain ftp:// + connection to use SSL for both or one of the connections + +TELNET + - connection negotiation + - custom telnet options + - stdin/stdout I/O + +LDAP (*2) + - full LDAP URL support + +DICT + - extended DICT URL support + +GOPHER + - GET + - via http-proxy + +FILE + - URL support + - "uploads" + - resume + +FOOTNOTES +========= + + *1 = requires OpenSSL + *2 = requires OpenLDAP + *3 = requires a GSSAPI-compliant library, such as Heimdal or similar. + *4 = requires FBopenssl + *5 = requires a krb4 library, such as the MIT one or similar. + *6 = requires c-ares diff --git a/src/curl-7.12.2/docs/HISTORY b/src/curl-7.12.2/docs/HISTORY new file mode 100644 index 0000000..884dcea --- /dev/null +++ b/src/curl-7.12.2/docs/HISTORY @@ -0,0 +1,141 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + How cURL Became Like This + + +In the second half of 1997, Daniel Stenberg came up with the idea to make +currency-exchange calculations available to Internet Relay Chat (IRC) +users. All the necessary data are published on the Web; he just needed to +automate their retrieval. + +Daniel simply adopted an existing command-line open-source tool, httpget, that +Brazilian Rafael Sagula had written. After a few minor adjustments, it did +just what he needed. + +Soon, he found currencies on a GOPHER site, so support for that had to go in, +and not before long FTP download support was added as well. The name of the +project was changed to urlget to better fit what it actually did now, since +the http-only days were already passed. + +The project slowly grew bigger. When upload capabilities were added and the +name once again was misleading, a second name change was made and on March 20, +1998 curl 4 was released. (The version numbering from the previous names was +kept.) + +(Unrelated to this project a company called Curl Corporation registered a US +trademark on the name "CURL" on May 18 1998. That company had then already +registered the curl.com domain back in November of the previous year. All this +was revealed to us much later.) + +SSL support was added, powered by the SSLeay library. + +August 1998, first announcement of curl on freshmeat.net. + +October 1998, with the curl 4.9 release and the introduction of cookie +support, curl was no longer released under the GPL license. Now we're at 4000 +lines of code, we switched over to the MPL license to restrict the effects of +"copyleft". + +November 1998, configure script and reported successful compiles on several +major operating systems. The never-quite-understood -F option was added and +curl could now simulate quite a lot of a browser. TELNET support was added. + +Curl 5 was released in December 1998 and introduced the first ever curl man +page. People started making Linux RPM packages out of it. + +January 1999, DICT support added. + +OpenSSL took over where SSLeay was abandoned. + +May 1999, first Debian package. + +August 1999, LDAP:// and FILE:// support added. The curl web site gets 1300 +visits weekly. + +Released curl 6.0 in September. 15000 lines of code. + +December 28 1999, added the project on Sourceforge and started using its +services for managing the project. + +Spring 2000, major internal overhaul to provide a suitable library interface. +The first non-beta release was named 7.1 and arrived in August. This offered +the easy interface and turned out to be the beginning of actually getting +other software and programs to get based on and powered by libcurl. Almost +20000 lines of code. + +August 2000, the curl web site gets 4000 visits weekly. + +The PHP guys adopted libcurl already the same month, when the first ever third +party libcurl binding showed up. CURL has been a supported module in PHP since +the release of PHP 4.0.2. This would soon get followers. More than 16 +different bindings exist at the time of this writing. + +September 2000, kerberos4 support was added. + +In November 2000 started the work on a test suite for curl. It was later +re-written from scratch again. + +January 2001, Daniel released curl 7.5.2 under a new license again: MIT (or +MPL). The MIT license is extremely liberal and can be used combined with GPL +in other projects. This would finally put an end to the "complaints" from +people involved in GPLed projects that previously were prohibited from using +libcurl while it was released under MPL only. (Due to the fact that MPL is +deemed "GPL incompatible".) + +curl supports HTTP 1.1 starting with the release of 7.7, March 22 2001. This +also introduced libcurl's ability to do persistent connections. 24000 lines of +code. + +The first experimental ftps:// support was added in March 2001. + +August 2001. curl is bundled in Mac OS X, 10.1. It was already becoming more +and more of a standard utility of Linux distributions and a regular in the BSD +ports collections. The curl web site gets 8000 visits weekly. Curl Corporation +contacted Daniel to discuss "the name issue". After Daniel's reply, they have +never since got in touch again. + +September 2001, libcurl 7.9 introduces cookie jar and curl_formadd(). During +the forthcoming 7.9.x releases, we introduced the multi interface slowly and +without much whistles. + +June 2002, the curl web site gets 13000 visits weekly. curl and libcurl is +35000 lines of code. Reported successful compiles on more than 40 combinations +of CPUs and operating systems. + +To estimate number of users of the curl tool or libcurl library is next to +impossible. Around 5000 downloaded packages each week from the main site gives +a hint, but the packages are mirrored extensively, bundled with numerous OS +distributions and otherwise retrieved as part of other software. + +September 2002, with the release of curl 7.10 it is released under the MIT +license only. + +February 2003, the curl site averages at 20000 visits weekly. At any given +moment, there's an average of 3 people browsing the curl.haxx.se site. + +Multiple new authentication schemes are supported: Digest (May), NTLM (June) +and Negotiate (June). + +November 2003: curl 7.10.8 is released. 45000 lines of code. ~55000 unique +visitors to the curl.haxx.se site. Five official web mirrors. + +December 2003, full-fledged SSL for FTP is supported. + +January 2004: curl 7.11.0 introduced large file support. + +June 2004: curl 7.12.0 introduced IDN support. 10 official web mirrors. + +August 2004: + Curl and libcurl 7.12.1 + + Public curl release number: 82 + Releases counted from the very beginning: 109 + Available command line options: 96 + Available curl_easy_setopt() options: 120 + Number of public functions in libcurl: 36 + Amount of public web site mirrors: 12 + Number of known libcurl bindings: 26 diff --git a/src/curl-7.12.2/docs/INSTALL b/src/curl-7.12.2/docs/INSTALL new file mode 100644 index 0000000..7b452ce --- /dev/null +++ b/src/curl-7.12.2/docs/INSTALL @@ -0,0 +1,567 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + How To Compile + +Installing Binary Packages +========================== + + Lots of people download binary distributions of curl and libcurl. This + document does not describe how to install curl or libcurl using such a + binary package. This document describes how to compile, build and install + curl and libcurl from source code. + +UNIX +==== + + A normal unix installation is made in three or four steps (after you've + unpacked the source archive): + + ./configure + make + make test (optional) + make install + + You probably need to be root when doing the last command. + + If you have checked out the sources from the CVS repository, read the + CVS-INFO on how to proceed. + + Get a full listing of all available configure options by invoking it like: + + ./configure --help + + If you want to install curl in a different file hierarchy than /usr/local, + you need to specify that already when running configure: + + ./configure --prefix=/path/to/curl/tree + + If you happen to have write permission in that directory, you can do 'make + install' without being root. An example of this would be to make a local + install in your own home directory: + + ./configure --prefix=$HOME + make + make install + + The configure script always tries to find a working SSL library unless + explicitly told not to. If you have OpenSSL installed in the default search + path for your compiler/linker, you don't need to do anything special. If + you have OpenSSL installed in /usr/local/ssl, you can run configure like: + + ./configure --with-ssl + + If you have OpenSSL installed somewhere else (for example, /opt/OpenSSL,) + you can run configure like this: + + ./configure --with-ssl=/opt/OpenSSL + + If you insist on forcing a build without SSL support, even though you may + have OpenSSL installed in your system, you can run configure like this: + + ./configure --without-ssl + + If you have OpenSSL installed, but with the libraries in one place and the + header files somewhere else, you have to set the LDFLAGS and CPPFLAGS + environment variables prior to running configure. Something like this + should work: + + (with the Bourne shell and its clones): + + CPPFLAGS="-I/path/to/ssl/include" LDFLAGS="-L/path/to/ssl/lib" \ + ./configure + + (with csh, tcsh and their clones): + + env CPPFLAGS="-I/path/to/ssl/include" LDFLAGS="-L/path/to/ssl/lib" \ + ./configure + + If your SSL library was compiled with rsaref (usually for use in the United + States), you may also need to set: + + LIBS=-lRSAglue -lrsaref + (as suggested by Doug Kaufman) + + MORE OPTIONS + + To force configure to use the standard cc compiler if both cc and gcc are + present, run configure like + + CC=cc ./configure + or + env Cc=cc ./configure + + To force a static library compile, disable the shared library creation + by running configure like: + + ./configure --disable-shared + + To tell the configure script to skip searching for thread-safe functions, + add an option like: + + ./configure --disable-thread + + To build curl with kerberos4 support enabled, curl requires the krb4 libs + and headers installed. You can then use a set of options to tell + configure where those are: + + --with-krb4-includes[=DIR] Specify location of kerberos4 headers + --with-krb4-libs[=DIR] Specify location of kerberos4 libs + --with-krb4[=DIR] where to look for Kerberos4 + + In most cases, /usr/athena is the install prefix and then it works with + + ./configure --with-krb4=/usr/athena + + If you're a curl developer and use gcc, you might want to enable more + debug options with the --enable-debug option. + +Win32 +===== + + Without SSL: + + MingW32 (GCC-2.95) style + ------------------------ + Run the 'mingw32.bat' file to get the proper environment variables + set, then run 'make mingw32' in the root dir. + + If you have any problems linking libraries or finding header files, be + sure to verify that the provided "Makefile.m32" files use the proper + paths, and adjust as necessary. + + Cygwin style + ------------ + Almost identical to the unix installation. Run the configure script in + the curl root with 'sh configure'. Make sure you have the sh + executable in /bin/ or you'll see the configure fail towards the end. + + Run 'make' + + Microsoft command line style + ---------------------------- + Run the 'vcvars32.bat' file to get the proper environment variables + set, then run 'nmake vc' in the root dir. + + The vcvars32.bat file is part of the Microsoft development + environment. + + IDE-style + ------------------------- + If you use VC++, Borland or similar compilers. Include all lib source + files in a static lib "project" (all .c and .h files that is). + (you should name it libcurl or similar) + + Make the sources in the src/ drawer be a "win32 console application" + project. Name it curl. + + For VC++ 6, there's an included Makefile.vc6 that should be possible + to use out-of-the-box. + + + With SSL: + + MingW32 (GCC-2.95) style + ------------------------ + Run the 'mingw32.bat' file to get the proper environment variables + set, then run 'make mingw32-ssl' in the root dir. + + If you have any problems linking libraries or finding header files, be + sure to look at the provided "Makefile.m32" files for the proper + paths, and adjust as necessary. + + Cygwin style + ------------ + Haven't done, nor got any reports on how to do. It should although be + identical to the unix setup for the same purpose. See above. + + Microsoft command line style + ---------------------------- + + Run the 'vcvars32.bat' file to get a proper environment. The + vcvars32.bat file is part of the Microsoft development environment and + you may find it in 'C:\Program Files\Microsoft Visual Studio\vc98\bin' + provided that you installed Visual C/C++ 6 in the default directory. + + Then run 'nmake vc' in curl's root directory. + + If you want to compile with zlib support, you will need to build + zlib (http://www.gzip.org/zlib/) as well. Please read the zlib + documentation on how to compile zlib. Define the ZLIB_PATH environment + variable to the location of zlib.h and zlib.lib, for example: + + set ZLIB_PATH=c:\zlib-1.2.1 + + Then run 'nmake vc-zlib' in curl's root directory. + + If you want to compile with SSL support you need the OpenSSL package. + Please read the OpenSSL documentation on how to compile and install + the OpenSSL libraries. The build process of OpenSSL generates the + libeay32.dll and ssleay32.dll files in the out32dll subdirectory in + the OpenSSL home directory. OpenSSL static libraries (libeay32.lib, + ssleay32.lib, RSAglue.lib) are created in the out32 subdirectory. + + Before running nmake define the OPENSSL_PATH environment variable with + the root/base directory of OpenSSL, for example: + + set OPENSSL_PATH=c:\openssl-0.9.7d + + Then run 'nmake vc-ssl' or 'nmake vc-ssl-dll' in curl's root + directory. 'nmake vc-ssl' will create a libcurl static and dynamic + libraries in the lib subdirectory, as well as a statically linked + version of curl.exe in the src subdirectory. This statically linked + version is a standalone executable not requiring any DLL at + runtime. This make method requires that you have the static OpenSSL + libraries available in OpenSSL's out32 subdirectory. + 'nmake vc-ssl-dll' creates the libcurl dynamic library and + links curl.exe against libcurl and OpenSSL dynamically. + This executable requires libcurl.dll and the OpenSSL DLLs + at runtime. + Run 'nmake vc-ssl-zlib' to build with both ssl and zlib support. + + Microsoft / Borland style + ------------------------- + If you have OpenSSL, and want curl to take advantage of it, edit your + project properties to use the SSL include path, link with the SSL libs + and define the USE_SSLEAY symbol. + + Using Borland C++ compiler version 5.5.1 (available as free download + from Borland's site) + --------------------------------------------------------------------- + + compile openssl + + Make sure you include the paths to curl/include and openssl/inc32 in + your bcc32.cnf file + + + eg : -I"c:\Bcc55\include;c:\path_curl\include;c:\path_openssl\inc32" + + Check to make sure that all of the sources listed in lib/Makefile.b32 + are present in the /path_to_curl/lib directory. (Check the src + directory for missing ones.) + + Make sure the environment variable "BCCDIR" is set to the install + location for the compiler eg : c:\Borland\BCC55 + + command line: + make -f /path_to_curl/lib/Makefile-ssl.b32 + + compile simplessl.c with appropriate links + + c:\curl\docs\examples\> bcc32 -L c:\path_to_curl\lib\libcurl.lib + -L c:\borland\bcc55\lib\psdk\ws2_32.lib + -L c:\openssl\out32\libeay32.lib + -L c:\openssl\out32\ssleay32.lib + simplessl.c + + Disabling Specific Protocols: + + The configure utility, unfortunately, is not available for the Windows + environment, therefore, you cannot use the various disable-protocol + options of the configure utility on this platform. + + However, you can use the following defines to disable specific + protocols: + + HTTP_ONLY disables all protocols except HTTP + CURL_DISABLE_FTP disables FTP + CURL_DISABLE_LDAP disables LDAP + CURL_DISABLE_TELNET disables TELNET + CURL_DISABLE_DICT disables DICT + CURL_DISABLE_FILE disables FILE + CURL_DISABLE_GOPHER disables GOPHER + + If you want to set any of these defines you have the following + possibilities: + + - Modify lib/setup.h + - Modify lib/Makefile.vc6 + - Add defines to Project/Settings/C/C++/General/Preprocessor Definitions + in the curllib.dsw/curllib.dsp Visual C++ 6 IDE project. + +IBM OS/2 +======== + + Building under OS/2 is not much different from building under unix. + You need: + + - emx 0.9d + - GNU make + - GNU patch + - ksh + - GNU bison + - GNU file utilities + - GNU sed + - autoconf 2.13 + + If you want to build with OpenSSL or OpenLDAP support, you'll need to + download those libraries, too. Dirk Ohme has done some work to port SSL + libraries under OS/2, but it looks like he doesn't care about emx. You'll + find his patches on: http://come.to/Dirk_Ohme + + If during the linking you get an error about _errno being an undefined + symbol referenced from the text segment, you need to add -D__ST_MT_ERRNO__ + in your definitions. + + If everything seems to work fine but there's no curl.exe, you need to add + -Zexe to your linker flags. + + If you're getting huge binaries, probably your makefiles have the -g in + CFLAGS. + +VMS +=== + (The VMS section is in whole contributed by the friendly Nico Baggus) + + Curl seems to work with FTP & HTTP other protocols are not tested. (the + perl http/ftp testing server supplied as testing too cannot work on VMS + because vms has no concept of fork(). [ I tried to give it a whack, but + thats of no use. + + SSL stuff has not been ported. + + Telnet has about the same issues as for Win32. When the changes for Win32 + are clear maybe they'll work for VMS too. The basic problem is that select + ONLY works for sockets. + + Marked instances of fopen/[f]stat that might become a problem, especially + for non stream files. In this regard, the files opened for writing will be + created stream/lf and will thus be safe. Just keep in mind that non-binary + read/wring from/to files will have a records size limit of 32767 bytes + imposed. + + Stat to get the size of the files is again only safe for stream files & + fixed record files without implied CC. + + -- My guess is that only allowing access to stream files is the quickest + way to get around the most issues. Therefore all files need to to be + checked to be sure they will be stream/lf before processing them. This is + the easiest way out, I know. The reason for this is that code that needs to + report the filesize will become a pain in the ass otherwise. + + Exit status.... Well we needed something done here, + + VMS has a structured exist status: + | 3 | 2 | 1 | 0| + |1098|765432109876|5432109876543|210| + +----+------------+-------------+---+ + |Ctrl| Facility | Error code |sev| + +----+------------+-------------+---+ + + With the Ctrl-bits an application can tell if part or the whole message has + allready been printed from the program, DCL doesn't need to print it again. + + Facility - basicaly the program ID. A code assigned to the program + the name can be fetched from external or internal message libraries + Errorcode - the errodes assigned by the application + Sev. - severity: Even = error, off = non error + 0 = Warning + 1 = Success + 2 = Error + 3 = Information + 4 = Fatal + <5-7> reserved. + + This all presents itself with: + %--, + + See also the src/curlmsg.msg file, it has the source for the messages In + src/main.c a section is devoted to message status values, the globalvalues + create symbols with certain values, referenced from a compiled message + file. Have all exit function use a exit status derived from a translation + table with the compiled message codes. + + This was all compiled with: + + Compaq C V6.2-003 on OpenVMS Alpha V7.1-1H2 + + So far for porting notes as of: + 13-jul-2001 + N. Baggus + +QNX +=== + (This section was graciously brought to us by David Bentham) + + As QNX is targetted for resource constrained environments, the QNX headers + set conservative limits. This includes the FD_SETSIZE macro, set by default + to 32. Socket descriptors returned within the CURL library may exceed this, + resulting in memory faults/SIGSEGV crashes when passed into select(..) + calls using fd_set macros. + + A good all-round solution to this is to override the default when building + libcurl, by overriding CFLAGS during configure, example + # configure CFLAGS='-DFD_SETSIZE=64 -g -O2' + +CROSS COMPILE +============= + + (This section was graciously brought to us by Jim Duey, 23-oct-2001) + + Download and unpack the cURL package. Version should be 7.9.1 or later. + + 'cd' to the new directory. (ie. curl-7.9.1-pre4) + + Set environment variables to point to the cross-compile toolchain and call + configure with any options you need. Be sure and specify the '--host' and + '--build' parameters at configuration time. The following script is an + example of cross-compiling for the IBM 405GP PowerPC processor using the + toolchain from MonteVista for Hardhat Linux. + + (begin script) + + #! /bin/sh + + export PATH=$PATH:/opt/hardhat/devkit/ppc/405/bin + export CPPFLAGS="-I/opt/hardhat/devkit/ppc/405/target/usr/include" + export AR=ppc_405-ar + export AS=ppc_405-as + export LD=ppc_405-ld + export RANLIB=ppc_405-ranlib + export CC=ppc_405-gcc + export NM=ppc_405-nm + + configure --target=powerpc-hardhat-linux \ + --host=powerpc-hardhat-linux \ + --build=i586-pc-linux-gnu \ + --prefix=/opt/hardhat/devkit/ppc/405/target/usr/local \ + --exec-prefix=/usr/local + + (end script) + + The '--prefix' parameter specifies where cURL will be installed. If + 'configure' completes successfully, do 'make' and 'make install' as usual. + +RISC OS +======= + The library can be cross-compiled using gccsdk as follows: + + CC=riscos-gcc AR=riscos-ar RANLIB='riscos-ar -s' ./configure \ + --host=arm-riscos-aof --without-random --disable-shared + make + + where riscos-gcc and riscos-ar are links to the gccsdk tools. + You can then link your program with curl/lib/.libs/libcurl.a + + +AmigaOS +======= + (This section was graciously brought to us by Diego Casorran) + + To build cURL/libcurl on AmigaOS just type 'make amiga' ... + + What you need is: (not tested with others versions) + + GeekGadgets / gcc 2.95.3 (http://www.geekgadgets.org/) + + AmiTCP SDK v4.3 (http://www.aminet.net/comm/tcp/AmiTCP-SDK-4.3.lha) + + Native Developer Kit (http://www.amiga.com/3.9/download/NDK3.9.lha) + + As no ixemul.library is required you will be able to build it for + WarpOS/PowerPC (not tested by me), as well a MorphOS version should be + possible with no problems. + + To enable SSL support, you need a OpenSSL native version (without ixemul), + you can find a precompiled package at http://amiga.sourceforge.net/OpenSSL/ + + +NetWare +======= + + To compile curl.nlm / libcurl.nlm you need: + - either any gcc / nlmconv, or CodeWarrior 7 PDK 4 or later. + - gnu make and awk running on the platform you compile on; + native Win32 versions can be downloaded from: + http://www.gknw.com/development/prgtools/ + - recent Novell LibC SDK availabable from: + http://developer.novell.com/ndk/libc.htm + - optional zlib sources (at the moment only dynamic linking with zlib.imp); + sources with NetWare Makefile can be optained from: + http://www.gknw.com/mirror/zlib/ + + Set a search path to your compiler, linker and tools; if you want to have + zlib support then set the environment var ZLIB_PATH pointing to your zlib + sources, on Linux make sure the var OSTYPE contains the string 'linux'; + and finally type 'make netware' from the top source directory... + I found on some Linux systems (RH9) that OS detection didnt work although + a 'set | grep OSTYPE' shows the var present and set; I simply overwrote it + with 'OSTYPE=linux-rh9-gnu' and the detection in the Makefile worked...; + other options are currently not supported, although partly prepared. + The Ares lib builds arlready fine, and both test tools work fine at least + when build with CodeWarrior...; dont know yet why they fail when build with + gcc though; if you want to compile with Ares support then set an env var + WITH_ARES=1; I've not tested yet including libcares into curl. + Any help in testing appreciated! + Buils automatically created 4 times a day from current CVS are here: + http://www.gknw.com/mirror/curl/autobuilds/ + the status of these buids can be viewed at the autobuild table: + http://curl.haxx.se/auto/ + + +PORTS +===== + This is a probably incomplete list of known hardware and operating systems + that curl has been compiled for. If you know a system curl compiles and + runs on, that isn't listed, please let us know! + + - Alpha DEC OSF 4 + - Alpha Digital UNIX v3.2 + - Alpha FreeBSD 4.1, 4.5 + - Alpha Linux 2.2, 2.4 + - Alpha NetBSD 1.5.2 + - Alpha OpenBSD 3.0 + - Alpha OpenVMS V7.1-1H2 + - Alpha Tru64 v5.0 5.1 + - HP-PA HP-UX 9.X 10.X 11.X + - HP-PA Linux + - HP3000 MPE/iX + - MIPS IRIX 6.2, 6.5 + - MIPS Linux + - Pocket PC/Win CE 3.0 + - Power AIX 3.2.5, 4.2, 4.3.1, 4.3.2, 5.1 + - PowerPC Darwin 1.0 + - PowerPC Linux + - PowerPC Mac OS 9 + - PowerPC Mac OS X + - SINIX-Z v5 + - Sparc Linux + - Sparc Solaris 2.4, 2.5, 2.5.1, 2.6, 7, 8 + - Sparc SunOS 4.1.X + - StrongARM (and other ARM) RISC OS 3.1, 4.02 + - StrongARM Linux 2.4 + - StrongARM NetBSD 1.4.1 + - Ultrix 4.3a + - i386 BeOS + - i386 DOS + - i386 FreeBSD + - i386 HURD + - i386 Linux 1.3, 2.0, 2.2, 2.3, 2.4 + - i386 NetBSD + - i386 Novell NetWare + - i386 OS/2 + - i386 OpenBSD + - i386 SCO unix + - i386 Solaris 2.7 + - i386 Windows 95, 98, ME, NT, 2000 + - i386 QNX 6 + - i486 ncr-sysv4.3.03 (NCR MP-RAS) + - ia64 Linux 2.3.99 + - m68k AmigaOS 3 + - m68k Linux + - m68k OpenBSD + - m88k dg-dgux5.4R3.00 + - s390 Linux + - XScale/PXA250 Linux 2.4 + +Useful URLs +=========== + +OpenSSL http://www.openssl.org +MingW http://www.mingw.org +OpenLDAP http://www.openldap.org +Zlib http://www.gzip.org/zlib/ diff --git a/src/curl-7.12.2/docs/INTERNALS b/src/curl-7.12.2/docs/INTERNALS new file mode 100644 index 0000000..42ac450 --- /dev/null +++ b/src/curl-7.12.2/docs/INTERNALS @@ -0,0 +1,381 @@ + Updated for curl 7.9.1 on November 2, 2001 + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +INTERNALS + + The project is split in two. The library and the client. The client part uses + the library, but the library is designed to allow other applications to use + it. + + The largest amount of code and complexity is in the library part. + +CVS +=== + All changes to the sources are committed to the CVS repository as soon as + they're somewhat verified to work. Changes shall be commited as independently + as possible so that individual changes can be easier spotted and tracked + afterwards. + + Tagging shall be used extensively, and by the time we release new archives we + should tag the sources with a name similar to the released version number. + +Windows vs Unix +=============== + + There are a few differences in how to program curl the unix way compared to + the Windows way. The four perhaps most notable details are: + + 1. Different function names for socket operations. + + In curl, this is solved with defines and macros, so that the source looks + the same at all places except for the header file that defines them. The + macros in use are sclose(), sread() and swrite(). + + 2. Windows requires a couple of init calls for the socket stuff. + + Those must be made by the application that uses libcurl, in curl that means + src/main.c has some code #ifdef'ed to do just that. + + 3. The file descriptors for network communication and file operations are + not easily interchangable as in unix. + + We avoid this by not trying any funny tricks on file descriptors. + + 4. When writing data to stdout, Windows makes end-of-lines the DOS way, thus + destroying binary data, although you do want that conversion if it is + text coming through... (sigh) + + We set stdout to binary under windows + + Inside the source code, We make an effort to avoid '#ifdef [Your OS]'. All + conditionals that deal with features *should* instead be in the format + '#ifdef HAVE_THAT_WEIRD_FUNCTION'. Since Windows can't run configure scripts, + we maintain two config-win32.h files (one in lib/ and one in src/) that are + supposed to look exactly as a config.h file would have looked like on a + Windows machine! + + Generally speaking: always remember that this will be compiled on dozens of + operating systems. Don't walk on the edge. + +Library +======= + + There are plenty of entry points to the library, namely each publicly defined + function that libcurl offers to applications. All of those functions are + rather small and easy-to-follow. All the ones prefixed with 'curl_easy' are + put in the lib/easy.c file. + + curl_global_init_() and curl_global_cleanup() should be called by the + application to initialize and clean up global stuff in the library. As of + today, it can handle the global SSL initing if SSL is enabled and it can init + the socket layer on windows machines. libcurl itself has no "global" scope. + + All printf()-style functions use the supplied clones in lib/mprintf.c. This + makes sure we stay absolutely platform independent. + + curl_easy_init() allocates an internal struct and makes some initializations. + The returned handle does not reveal internals. This is the 'SessionHandle' + struct which works as an "anchor" struct for all curl_easy functions. All + connections performed will get connect-specific data allocated that should be + used for things related to particular connections/requests. + + curl_easy_setopt() takes three arguments, where the option stuff must be + passed in pairs: the parameter-ID and the parameter-value. The list of + options is documented in the man page. This function mainly sets things in + the 'SessionHandle' struct. + + curl_easy_perform() does a whole lot of things: + + It starts off in the lib/easy.c file by calling Curl_perform() and the main + work then continues in lib/url.c. The flow continues with a call to + Curl_connect() to connect to the remote site. + + o Curl_connect() + + ... analyzes the URL, it separates the different components and connects to + the remote host. This may involve using a proxy and/or using SSL. The + Curl_gethost() function in lib/hostip.c is used for looking up host names. + + When Curl_connect is done, we are connected to the remote site. Then it is + time to tell the server to get a document/file. Curl_do() arranges this. + + This function makes sure there's an allocated and initiated 'connectdata' + struct that is used for this particular connection only (although there may + be several requests performed on the same connect). A bunch of things are + inited/inherited from the SessionHandle struct. + + o Curl_do() + + Curl_do() makes sure the proper protocol-specific function is called. The + functions are named after the protocols they handle. Curl_ftp(), + Curl_http(), Curl_dict(), etc. They all reside in their respective files + (ftp.c, http.c and dict.c). HTTPS is handled by Curl_http() and FTPS by + Curl_ftp(). + + The protocol-specific functions of course deal with protocol-specific + negotiations and setup. They have access to the Curl_sendf() (from + lib/sendf.c) function to send printf-style formatted data to the remote + host and when they're ready to make the actual file transfer they call the + Curl_Transfer() function (in lib/transfer.c) to setup the transfer and + returns. + + Starting in 7.9.1, if this DO function fails and the connection is being + re-used, libcurl will then close this connection, setup a new connection + and re-issue the DO request on that. This is because there is no way to be + perfectly sure that we have discovered a dead connection before the DO + function and thus we might wrongly be re-using a connection that was closed + by the remote peer. + + o Transfer() + + Curl_perform() then calls Transfer() in lib/transfer.c that performs + the entire file transfer. + + During transfer, the progress functions in lib/progress.c are called at a + frequent interval (or at the user's choice, a specified callback might get + called). The speedcheck functions in lib/speedcheck.c are also used to + verify that the transfer is as fast as required. + + o Curl_done() + + Called after a transfer is done. This function takes care of everything + that has to be done after a transfer. This function attempts to leave + matters in a state so that Curl_do() should be possible to call again on + the same connection (in a persistent connection case). It might also soon + be closed with Curl_disconnect(). + + o Curl_disconnect() + + When doing normal connections and transfers, no one ever tries to close any + connections so this is not normally called when curl_easy_perform() is + used. This function is only used when we are certain that no more transfers + is going to be made on the connection. It can be also closed by force, or + it can be called to make sure that libcurl doesn't keep too many + connections alive at the same time (there's a default amount of 5 but that + can be changed with the CURLOPT_MAXCONNECTS option). + + This function cleans up all resources that are associated with a single + connection. + + Curl_perform() is the function that does the main "connect - do - transfer - + done" loop. It loops if there's a Location: to follow. + + When completed, the curl_easy_cleanup() should be called to free up used + resources. It runs Curl_disconnect() on all open connectons. + + A quick roundup on internal function sequences (many of these call + protocol-specific function-pointers): + + curl_connect - connects to a remote site and does initial connect fluff + This also checks for an existing connection to the requested site and uses + that one if it is possible. + + curl_do - starts a transfer + curl_transfer() - transfers data + curl_done - ends a transfer + + curl_disconnect - disconnects from a remote site. This is called when the + disconnect is really requested, which doesn't necessarily have to be + exactly after curl_done in case we want to keep the connection open for + a while. + + HTTP(S) + + HTTP offers a lot and is the protocol in curl that uses the most lines of + code. There is a special file (lib/formdata.c) that offers all the multipart + post functions. + + base64-functions for user+password stuff (and more) is in (lib/base64.c) and + all functions for parsing and sending cookies are found in (lib/cookie.c). + + HTTPS uses in almost every means the same procedure as HTTP, with only two + exceptions: the connect procedure is different and the function used to read + or write from the socket is different, although the latter fact is hidden in + the source by the use of curl_read() for reading and curl_write() for writing + data to the remote server. + + http_chunks.c contains functions that understands HTTP 1.1 chunked transfer + encoding. + + An interesting detail with the HTTP(S) request, is the add_buffer() series of + functions we use. They append data to one single buffer, and when the + building is done the entire request is sent off in one single write. This is + done this way to overcome problems with flawed firewalls and lame servers. + + FTP + + The Curl_if2ip() function can be used for getting the IP number of a + specified network interface, and it resides in lib/if2ip.c. + + Curl_ftpsendf() is used for sending FTP commands to the remote server. It was + made a separate function to prevent us programmers from forgetting that they + must be CRLF terminated. They must also be sent in one single write() to make + firewalls and similar happy. + + Kerberos + + The kerberos support is mainly in lib/krb4.c and lib/security.c. + + TELNET + + Telnet is implemented in lib/telnet.c. + + FILE + + The file:// protocol is dealt with in lib/file.c. + + LDAP + + Everything LDAP is in lib/ldap.c. + + GENERAL + + URL encoding and decoding, called escaping and unescaping in the source code, + is found in lib/escape.c. + + While transfering data in Transfer() a few functions might get + used. curl_getdate() in lib/getdate.c is for HTTP date comparisons (and + more). + + lib/getenv.c offers curl_getenv() which is for reading environment variables + in a neat platform independent way. That's used in the client, but also in + lib/url.c when checking the proxy environment variables. Note that contrary + to the normal unix getenv(), this returns an allocated buffer that must be + free()ed after use. + + lib/netrc.c holds the .netrc parser + + lib/timeval.c features replacement functions for systems that don't have + gettimeofday() and a few support functions for timeval convertions. + + A function named curl_version() that returns the full curl version string is + found in lib/version.c. + + If authentication is requested but no password is given, a getpass_r() clone + exists in lib/getpass.c. libcurl offers a custom callback that can be used + instead of this, but it doesn't change much to us. + +Persistent Connections +====================== + + The persistent connection support in libcurl requires some considerations on + how to do things inside of the library. + + o The 'SessionHandle' struct returned in the curl_easy_init() call must never + hold connection-oriented data. It is meant to hold the root data as well as + all the options etc that the library-user may choose. + o The 'SessionHandle' struct holds the "connection cache" (an array of + pointers to 'connectdata' structs). There's one connectdata struct + allocated for each connection that libcurl knows about. + o This also enables the 'curl handle' to be reused on subsequent transfers, + something that was illegal before libcurl 7.7. + o When we are about to perform a transfer with curl_easy_perform(), we first + check for an already existing connection in the cache that we can use, + otherwise we create a new one and add to the cache. If the cache is full + already when we add a new connection, we close one of the present ones. We + select which one to close dependent on the close policy that may have been + previously set. + o When the transfer operation is complete, we try to leave the connection + open. Particular options may tell us not to, and protocols may signal + closure on connections and then we don't keep it open of course. + o When curl_easy_cleanup() is called, we close all still opened connections. + + You do realize that the curl handle must be re-used in order for the + persistent connections to work. + +Library Symbols +=============== + + All symbols used internally in libcurl must use a 'Curl_' prefix if they're + used in more than a single file. Single-file symbols must be made static. + Public ("exported") symbols must use a 'curl_' prefix. (There are exceptions, + but they are to be changed to follow this pattern in future versions.) + +Return Codes and Informationals +=============================== + + I've made things simple. Almost every function in libcurl returns a CURLcode, + that must be CURLE_OK if everything is OK or otherwise a suitable error code + as the curl/curl.h include file defines. The very spot that detects an error + must use the Curl_failf() function to set the human-readable error + description. + + In aiding the user to understand what's happening and to debug curl usage, we + must supply a fair amount of informational messages by using the Curl_infof() + function. Those messages are only displayed when the user explicitly asks for + them. They are best used when revealing information that isn't otherwise + obvious. + +Client +====== + + main() resides in src/main.c together with most of the client code. + + src/hugehelp.c is automatically generated by the mkhelp.pl perl script to + display the complete "manual" and the src/urlglob.c file holds the functions + used for the URL-"globbing" support. Globbing in the sense that the {} and [] + expansion stuff is there. + + The client mostly messes around to setup its 'config' struct properly, then + it calls the curl_easy_*() functions of the library and when it gets back + control after the curl_easy_perform() it cleans up the library, checks status + and exits. + + When the operation is done, the ourWriteOut() function in src/writeout.c may + be called to report about the operation. That function is using the + curl_easy_getinfo() function to extract useful information from the curl + session. + + Recent versions may loop and do all this several times if many URLs were + specified on the command line or config file. + +Memory Debugging +================ + + The file lib/memdebug.c contains debug-versions of a few functions. Functions + such as malloc, free, fopen, fclose, etc that somehow deal with resources + that might give us problems if we "leak" them. The functions in the memdebug + system do nothing fancy, they do their normal function and then log + information about what they just did. The logged data can then be analyzed + after a complete session, + + memanalyze.pl is the perl script present only present in CVS (not part of the + release archives) that analyzes a log file generated by the memdebug + system. It detects if resources are allocated but never freed and other kinds + of errors related to resource management. + + Use -DMALLOCDEBUG when compiling to enable memory debugging, this is also + switched on by running configure with --enable-debug. + +Test Suite +========== + + Since November 2000, a test suite has evolved. It is placed in its own + subdirectory directly off the root in the curl archive tree, and it contains + a bunch of scripts and a lot of test case data. + + The main test script is runtests.pl that will invoke the two servers + httpserver.pl and ftpserver.pl before all the test cases are performed. The + test suite currently only runs on unix-like platforms. + + You'll find a complete description of the test case data files in the + tests/README file. + + The test suite automatically detects if curl was built with the memory + debugging enabled, and if it was it will detect memory leaks too. + +Building Releases +================= + + There's no magic to this. When you consider everything stable enough to be + released, run the 'maketgz' script (using 'make distcheck' will give you a + pretty good view on the status of the current sources). maketgz prompts for + version number of the client and the library before it creates a release + archive. maketgz uses 'make dist' for the actual archive building, why you + need to fill in the Makefile.am files properly for which files that should + be included in the release archives. + diff --git a/src/curl-7.12.2/docs/KNOWN_BUGS b/src/curl-7.12.2/docs/KNOWN_BUGS new file mode 100644 index 0000000..cb06edc --- /dev/null +++ b/src/curl-7.12.2/docs/KNOWN_BUGS @@ -0,0 +1,63 @@ +These are problems known to exist at the time of this release. Feel free to +join in and help us correct one or more of these! Also be sure to check the +changelog of the current development status, as one or more of these problems +may have been fixed since this was written! + +* curl version 7.12.2 fails on AIX if compiled with --enable-ares. + The workaround is to combine --enable-ares with --disable-shared + +* When connecting to a SOCKS proxy, the (connect) timeout is not properly + acknowledged after the actual TCP connect (during the SOCKS "negotiate" + phase). Pointed out by Lucas. Fix: need to select() and timeout properly. + +* Using configure --disable-[protocol] may cause 'make test' to fail for + tests using the disabled protocol(s). + +* To get HTTP Negotiate authentication to work fine, you need to provide a + (fake) user name (this concerns both curl and the lib) because the code + wrongly only considers authentication if there's a user name provided. + Bug report #1004841. How? http://curl.haxx.se/mail/lib-2004-08/0182.html + +* If you use a very large amount of file descriptors (more than FD_SETSIZE) + and then use libcurl, it might crash on its use of select() which then + stores data out of bounds. Bug report #948950. + +* --limit-rate using -d or -F does not work. This is because the limit logic + is provided by the curl app in its read/write callbacks, and when doing + -d/-F the callbacks aren't used! Bug report #921395. + +* Doing resumed upload over HTTP does not work with '-C -', because curl + doesn't do a HEAD first to get the initial size. This needs to be done + manually for HTTP PUT resume to work, and then '-C [index]'. + +* CURLOPT_USERPWD and CURLOPT_PROXYUSERPWD have no way of providing user names + that contain a colon. This can't be fixed easily in a backwards compatible + way without adding new options (and then, they should most probably allow + setting user name and password separately). + +* libcurl ignores empty path parts in FTP URLs, whereas RFC1738 states that + such parts should be sent to the server as 'CWD ' (without an argument). + The only exception to this rule, is that we knowingly break this if the + empty part is first in the path, as then we use the double slashes to + indicate that the user wants to reach the root dir (this exception SHALL + remain even when this bug is fixed). + +* libcurl doesn't treat the content-length of compressed data properly, as + it seems HTTP servers send the *uncompressed* length in that header and + libcurl thinks of it as the *compressed* lenght. Some explanations are here: + http://curl.haxx.se/mail/lib-2003-06/0146.html + +* IPv6 support on AIX 4.3.3 doesn't work due to a missing sockaddr_storage + struct. It has been reported to work on AIX 5.1 though. + +* GOPHER transfers seem broken + +* If a HTTP server responds to a HEAD request and includes a body (thus + violating the RFC2616), curl won't wait to read the response but just stop + reading and return back. If a second request (let's assume a GET) is then + immediately made to the same server again, the connection will be re-used + fine of course, and the second request will be sent off but when the + response is to get read, the previous response-body is what curl will read + and havoc is what happens. + More details on this is found in this libcurl mailing list thread: + http://curl.haxx.se/mail/lib-2002-08/0000.html diff --git a/src/curl-7.12.2/docs/LICENSE-MIXING b/src/curl-7.12.2/docs/LICENSE-MIXING new file mode 100644 index 0000000..9321f2f --- /dev/null +++ b/src/curl-7.12.2/docs/LICENSE-MIXING @@ -0,0 +1,86 @@ + License Mixing with apps, libcurl and Third Party Libraries + =========================================================== + +libcurl can be built to use a fair amount of various third party libraries, +libraries that are written and provided by other parties that are distributed +using their own licenses. Even libcurl itself contains code that may cause +problems to some. This document attempts to describe what licenses libcurl and +the other libraries use and what possible dilemmas linking and mixing them all +can lead to for end users. + +I am not a lawyer and this is not legal advice! + +One common dilemma is that GPL[1]-licensed code is not allowed to be linked +with code licensed under the Original BSD license (with the announcement +clause, unless there's a specified exception in the GPL-licensed module). You +may still build your own copies that use them all, but distributing them as +binaries would be to violate the GPL license - unless you accompany your +license with an exception[2]. This particular problem was addressed when the +Modified BSD license was created, which does not have the annoncement clause +that collides with GPL. + +libcurl http://curl.haxx.se/docs/copyright.html + + Uses an MIT (or Modified BSD)-style license that is as liberal as + possible. Some of the source files that deal with KRB4 have Original + BSD-style announce-clause licenses. You may not distribute binaries + with krb4-enabled libcurl that also link with GPL-licensed code! + +OpenSSL http://www.openssl.org/source/license.html + + Uses an Original BSD-style license with an announement clause that + makes it "incompatible" with GPL. You are not allowed to ship binaries + that link with OpenSSL that includes GPL code (unless that specific + GPL code includes an exception for OpenSSL - a habit that is growing + more and more common). + +c-ares http://daniel.haxx.se/projects/c-ares/license.html + + Uses an MIT license that is very liberal and imposes no restrictions + on any other library or part you may link with. + +zlib http://www.gzip.org/zlib/zlib_license.html + + Uses an MIT-style license that shouldn't collide with any other + library. + +krb4 + + While nothing in particular says that a Kerberos4 library must use any + particular license, the one I've tried and used successfully so far + (kth-krb4) is Original BSD-licensed with the announcement clause. Some + of the code in libcurl that is written to deal with Kerberos4 likewise + have such a license. + +GSSAPI + + While nothing in particular says that a GSS/Kerberos5 library must use + any particular license, the one I've used (Heimdal) is Original BSD- + licensed with the announcement clause. + +fbopenssl + + Unclear license. Based on its name, I assume that it uses the OpenSSL + license and thus shares the same issues as described for OpenSSL + above. + +libidn http://www.gnu.org/licenses/lgpl.html + + Uses the GNU Lesser General Public License. LGPL is a variation of GPL + with slightly less aggressive "copyleft". This license requires more + requirements to be met when distributing binaries, see the license for + details. Also note that if you distribute a binary that includes this + library, you must also include the full LGPL license text. Please + properly point out what parts of the distributed package that the + license addresses. + +OpenLDAP http://www.openldap.org/software/release/license.html + + Uses a Modified BSD-style license. Since libcurl uses OpenLDAP as a + shared library only, I have not heard of anyone that ships OpenLDAP + linked with libcurl in an app. + + +[1] = GPL - GNU General Public License: http://www.gnu.org/licenses/gpl.html +[2] = http://www.fsf.org/licenses/gpl-faq.html#GPLIncompatibleLibs details on + how to write such an exception to the GPL diff --git a/src/curl-7.12.2/docs/MANUAL b/src/curl-7.12.2/docs/MANUAL new file mode 100644 index 0000000..7eac93a --- /dev/null +++ b/src/curl-7.12.2/docs/MANUAL @@ -0,0 +1,882 @@ +LATEST VERSION + + You always find news about what's going on as well as the latest versions + from the curl web pages, located at: + + http://curl.haxx.se + +SIMPLE USAGE + + Get the main page from netscape's web-server: + + curl http://www.netscape.com/ + + Get the README file the user's home directory at funet's ftp-server: + + curl ftp://ftp.funet.fi/README + + Get a web page from a server using port 8000: + + curl http://www.weirdserver.com:8000/ + + Get a list of a directory of an FTP site: + + curl ftp://cool.haxx.se/ + + Get a gopher document from funet's gopher server: + + curl gopher://gopher.funet.fi + + Get the definition of curl from a dictionary: + + curl dict://dict.org/m:curl + + Fetch two documents at once: + + curl ftp://cool.haxx.se/ http://www.weirdserver.com:8000/ + +DOWNLOAD TO A FILE + + Get a web page and store in a local file: + + curl -o thatpage.html http://www.netscape.com/ + + Get a web page and store in a local file, make the local file get the name + of the remote document (if no file name part is specified in the URL, this + will fail): + + curl -O http://www.netscape.com/index.html + + Fetch two files and store them with their remote names: + + curl -O www.haxx.se/index.html -O curl.haxx.se/download.html + +USING PASSWORDS + + FTP + + To ftp files using name+passwd, include them in the URL like: + + curl ftp://name:passwd@machine.domain:port/full/path/to/file + + or specify them with the -u flag like + + curl -u name:passwd ftp://machine.domain:port/full/path/to/file + + FTPS + + It is just like for FTP, but you may also want to specify and use + SSL-specific options for certificates etc. + + HTTP + + The HTTP URL doesn't support user and password in the URL string. Curl + does support that anyway to provide a ftp-style interface and thus you can + pick a file like: + + curl http://name:passwd@machine.domain/full/path/to/file + + or specify user and password separately like in + + curl -u name:passwd http://machine.domain/full/path/to/file + + HTTP offers many different methods of authentication and curl supports + several: Basic, Digest, NTLM and Negotiate. Without telling which method to + use, curl defaults to Basic. You can also ask curl to pick the most secure + ones out of the ones that the server accepts for the given URL, by using + --anyauth. + + NOTE! Since HTTP URLs don't support user and password, you can't use that + style when using Curl via a proxy. You _must_ use the -u style fetch + during such circumstances. + + HTTPS + + Probably most commonly used with private certificates, as explained below. + + GOPHER + + Curl features no password support for gopher. + +PROXY + + Get an ftp file using a proxy named my-proxy that uses port 888: + + curl -x my-proxy:888 ftp://ftp.leachsite.com/README + + Get a file from a HTTP server that requires user and password, using the + same proxy as above: + + curl -u user:passwd -x my-proxy:888 http://www.get.this/ + + Some proxies require special authentication. Specify by using -U as above: + + curl -U user:passwd -x my-proxy:888 http://www.get.this/ + + See also the environment variables Curl support that offer further proxy + control. + +RANGES + + With HTTP 1.1 byte-ranges were introduced. Using this, a client can request + to get only one or more subparts of a specified document. Curl supports + this with the -r flag. + + Get the first 100 bytes of a document: + + curl -r 0-99 http://www.get.this/ + + Get the last 500 bytes of a document: + + curl -r -500 http://www.get.this/ + + Curl also supports simple ranges for FTP files as well. Then you can only + specify start and stop position. + + Get the first 100 bytes of a document using FTP: + + curl -r 0-99 ftp://www.get.this/README + +UPLOADING + + FTP + + Upload all data on stdin to a specified ftp site: + + curl -T - ftp://ftp.upload.com/myfile + + Upload data from a specified file, login with user and password: + + curl -T uploadfile -u user:passwd ftp://ftp.upload.com/myfile + + Upload a local file to the remote site, and use the local file name remote + too: + + curl -T uploadfile -u user:passwd ftp://ftp.upload.com/ + + Upload a local file to get appended to the remote file using ftp: + + curl -T localfile -a ftp://ftp.upload.com/remotefile + + Curl also supports ftp upload through a proxy, but only if the proxy is + configured to allow that kind of tunneling. If it does, you can run curl in + a fashion similar to: + + curl --proxytunnel -x proxy:port -T localfile ftp.upload.com + + HTTP + + Upload all data on stdin to a specified http site: + + curl -T - http://www.upload.com/myfile + + Note that the http server must've been configured to accept PUT before this + can be done successfully. + + For other ways to do http data upload, see the POST section below. + +VERBOSE / DEBUG + + If curl fails where it isn't supposed to, if the servers don't let you in, + if you can't understand the responses: use the -v flag to get verbose + fetching. Curl will output lots of info and what it sends and receives in + order to let the user see all client-server interaction (but it won't show + you the actual data). + + curl -v ftp://ftp.upload.com/ + + To get even more details and information on what curl does, try using the + --trace or --trace-ascii options with a given file name to log to, like + this: + + curl --trace trace.txt www.haxx.se + + +DETAILED INFORMATION + + Different protocols provide different ways of getting detailed information + about specific files/documents. To get curl to show detailed information + about a single file, you should use -I/--head option. It displays all + available info on a single file for HTTP and FTP. The HTTP information is a + lot more extensive. + + For HTTP, you can get the header information (the same as -I would show) + shown before the data by using -i/--include. Curl understands the + -D/--dump-header option when getting files from both FTP and HTTP, and it + will then store the headers in the specified file. + + Store the HTTP headers in a separate file (headers.txt in the example): + + curl --dump-header headers.txt curl.haxx.se + + Note that headers stored in a separate file can be very useful at a later + time if you want curl to use cookies sent by the server. More about that in + the cookies section. + +POST (HTTP) + + It's easy to post data using curl. This is done using the -d + option. The post data must be urlencoded. + + Post a simple "name" and "phone" guestbook. + + curl -d "name=Rafael%20Sagula&phone=3320780" \ + http://www.where.com/guest.cgi + + How to post a form with curl, lesson #1: + + Dig out all the tags in the form that you want to fill in. (There's + a perl program called formfind.pl on the curl site that helps with this). + + If there's a "normal" post, you use -d to post. -d takes a full "post + string", which is in the format + + =&=&... + + The 'variable' names are the names set with "name=" in the tags, and + the data is the contents you want to fill in for the inputs. The data *must* + be properly URL encoded. That means you replace space with + and that you + write weird letters with %XX where XX is the hexadecimal representation of + the letter's ASCII code. + + Example: + + (page located at http://www.formpost.com/getthis/ + +
+ + + + +
+ + We want to enter user 'foobar' with password '12345'. + + To post to this, you enter a curl command line like: + + curl -d "user=foobar&pass=12345&id=blablabla&ding=submit" (continues) + http://www.formpost.com/getthis/post.cgi + + + While -d uses the application/x-www-form-urlencoded mime-type, generally + understood by CGI's and similar, curl also supports the more capable + multipart/form-data type. This latter type supports things like file upload. + + -F accepts parameters like -F "name=contents". If you want the contents to + be read from a file, use <@filename> as contents. When specifying a file, + you can also specify the file content type by appending ';type=' + to the file name. You can also post the contents of several files in one + field. For example, the field name 'coolfiles' is used to send three files, + with different content types using the following syntax: + + curl -F "coolfiles=@fil1.gif;type=image/gif,fil2.txt,fil3.html" \ + http://www.post.com/postit.cgi + + If the content-type is not specified, curl will try to guess from the file + extension (it only knows a few), or use the previously specified type (from + an earlier file if several files are specified in a list) or else it will + using the default type 'text/plain'. + + Emulate a fill-in form with -F. Let's say you fill in three fields in a + form. One field is a file name which to post, one field is your name and one + field is a file description. We want to post the file we have written named + "cooltext.txt". To let curl do the posting of this data instead of your + favourite browser, you have to read the HTML source of the form page and + find the names of the input fields. In our example, the input field names + are 'file', 'yourname' and 'filedescription'. + + curl -F "file=@cooltext.txt" -F "yourname=Daniel" \ + -F "filedescription=Cool text file with cool text inside" \ + http://www.post.com/postit.cgi + + To send two files in one post you can do it in two ways: + + 1. Send multiple files in a single "field" with a single field name: + + curl -F "pictures=@dog.gif,cat.gif" + + 2. Send two fields with two field names: + + curl -F "docpicture=@dog.gif" -F "catpicture=@cat.gif" + +REFERRER + + A HTTP request has the option to include information about which address + that referred to actual page. Curl allows you to specify the + referrer to be used on the command line. It is especially useful to + fool or trick stupid servers or CGI scripts that rely on that information + being available or contain certain data. + + curl -e www.coolsite.com http://www.showme.com/ + + NOTE: The referer field is defined in the HTTP spec to be a full URL. + +USER AGENT + + A HTTP request has the option to include information about the browser + that generated the request. Curl allows it to be specified on the command + line. It is especially useful to fool or trick stupid servers or CGI + scripts that only accept certain browsers. + + Example: + + curl -A 'Mozilla/3.0 (Win95; I)' http://www.nationsbank.com/ + + Other common strings: + 'Mozilla/3.0 (Win95; I)' Netscape Version 3 for Windows 95 + 'Mozilla/3.04 (Win95; U)' Netscape Version 3 for Windows 95 + 'Mozilla/2.02 (OS/2; U)' Netscape Version 2 for OS/2 + 'Mozilla/4.04 [en] (X11; U; AIX 4.2; Nav)' NS for AIX + 'Mozilla/4.05 [en] (X11; U; Linux 2.0.32 i586)' NS for Linux + + Note that Internet Explorer tries hard to be compatible in every way: + 'Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)' MSIE for W95 + + Mozilla is not the only possible User-Agent name: + 'Konqueror/1.0' KDE File Manager desktop client + 'Lynx/2.7.1 libwww-FM/2.14' Lynx command line browser + +COOKIES + + Cookies are generally used by web servers to keep state information at the + client's side. The server sets cookies by sending a response line in the + headers that looks like 'Set-Cookie: ' where the data part then + typically contains a set of NAME=VALUE pairs (separated by semicolons ';' + like "NAME1=VALUE1; NAME2=VALUE2;"). The server can also specify for what + path the "cookie" should be used for (by specifying "path=value"), when the + cookie should expire ("expire=DATE"), for what domain to use it + ("domain=NAME") and if it should be used on secure connections only + ("secure"). + + If you've received a page from a server that contains a header like: + Set-Cookie: sessionid=boo123; path="/foo"; + + it means the server wants that first pair passed on when we get anything in + a path beginning with "/foo". + + Example, get a page that wants my name passed in a cookie: + + curl -b "name=Daniel" www.sillypage.com + + Curl also has the ability to use previously received cookies in following + sessions. If you get cookies from a server and store them in a file in a + manner similar to: + + curl --dump-header headers www.example.com + + ... you can then in a second connect to that (or another) site, use the + cookies from the 'headers' file like: + + curl -b headers www.example.com + + While saving headers to a file is a working way to store cookies, it is + however error-prone and not the prefered way to do this. Instead, make curl + save the incoming cookies using the well-known netscape cookie format like + this: + + curl -c cookies.txt www.example.com + + Note that by specifying -b you enable the "cookie awareness" and with -L + you can make curl follow a location: (which often is used in combination + with cookies). So that if a site sends cookies and a location, you can + use a non-existing file to trigger the cookie awareness like: + + curl -L -b empty.txt www.example.com + + The file to read cookies from must be formatted using plain HTTP headers OR + as netscape's cookie file. Curl will determine what kind it is based on the + file contents. In the above command, curl will parse the header and store + the cookies received from www.example.com. curl will send to the server the + stored cookies which match the request as it follows the location. The + file "empty.txt" may be a non-existant file. + + Alas, to both read and write cookies from a netscape cookie file, you can + set both -b and -c to use the same file: + + curl -b cookies.txt -c cookies.txt www.example.com + +PROGRESS METER + + The progress meter exists to show a user that something actually is + happening. The different fields in the output have the following meaning: + + % Total % Received % Xferd Average Speed Time Curr. + Dload Upload Total Current Left Speed + 0 151M 0 38608 0 0 9406 0 4:41:43 0:00:04 4:41:39 9287 + + From left-to-right: + % - percentage completed of the whole transfer + Total - total size of the whole expected transfer + % - percentage completed of the download + Received - currently downloaded amount of bytes + % - percentage completed of the upload + Xferd - currently uploaded amount of bytes + Average Speed + Dload - the average transfer speed of the download + Average Speed + Upload - the average transfer speed of the upload + Time Total - expected time to complete the operation + Time Current - time passed since the invoke + Time Left - expected time left to completetion + Curr.Speed - the average transfer speed the last 5 seconds (the first + 5 seconds of a transfer is based on less time of course.) + + The -# option will display a totally different progress bar that doesn't + need much explanation! + +SPEED LIMIT + + Curl allows the user to set the transfer speed conditions that must be met + to let the transfer keep going. By using the switch -y and -Y you + can make curl abort transfers if the transfer speed is below the specified + lowest limit for a specified time. + + To have curl abort the download if the speed is slower than 3000 bytes per + second for 1 minute, run: + + curl -Y 3000 -y 60 www.far-away-site.com + + This can very well be used in combination with the overall time limit, so + that the above operatioin must be completed in whole within 30 minutes: + + curl -m 1800 -Y 3000 -y 60 www.far-away-site.com + + Forcing curl not to transfer data faster than a given rate is also possible, + which might be useful if you're using a limited bandwidth connection and you + don't want your transfer to use all of it (sometimes referred to as + "bandwith throttle"). + + Make curl transfer data no faster than 10 kilobytes per second: + + curl --limit-rate 10K www.far-away-site.com + + or + + curl --limit-rate 10240 www.far-away-site.com + + Or prevent curl from uploading data faster than 1 megabyte per second: + + curl -T upload --limit-rate 1M ftp://uploadshereplease.com + + When using the --limit-rate option, the transfer rate is regulated on a + per-second basis, which will cause the total transfer speed to become lower + than the given number. Sometimes of course substantially lower, if your + transfer stalls during periods. + +CONFIG FILE + + Curl automatically tries to read the .curlrc file (or _curlrc file on win32 + systems) from the user's home dir on startup. + + The config file could be made up with normal command line switches, but you + can also specify the long options without the dashes to make it more + readable. You can separate the options and the parameter with spaces, or + with = or :. Comments can be used within the file. If the first letter on a + line is a '#'-letter the rest of the line is treated as a comment. + + If you want the parameter to contain spaces, you must inclose the entire + parameter within double quotes ("). Within those quotes, you specify a + quote as \". + + NOTE: You must specify options and their arguments on the same line. + + Example, set default time out and proxy in a config file: + + # We want a 30 minute timeout: + -m 1800 + # ... and we use a proxy for all accesses: + proxy = proxy.our.domain.com:8080 + + White spaces ARE significant at the end of lines, but all white spaces + leading up to the first characters of each line are ignored. + + Prevent curl from reading the default file by using -q as the first command + line parameter, like: + + curl -q www.thatsite.com + + Force curl to get and display a local help page in case it is invoked + without URL by making a config file similar to: + + # default url to get + url = "http://help.with.curl.com/curlhelp.html" + + You can specify another config file to be read by using the -K/--config + flag. If you set config file name to "-" it'll read the config from stdin, + which can be handy if you want to hide options from being visible in process + tables etc: + + echo "user = user:passwd" | curl -K - http://that.secret.site.com + +EXTRA HEADERS + + When using curl in your own very special programs, you may end up needing + to pass on your own custom headers when getting a web page. You can do + this by using the -H flag. + + Example, send the header "X-you-and-me: yes" to the server when getting a + page: + + curl -H "X-you-and-me: yes" www.love.com + + This can also be useful in case you want curl to send a different text in a + header than it normally does. The -H header you specify then replaces the + header curl would normally send. If you replace an internal header with an + empty one, you prevent that header from being sent. To prevent the Host: + header from being used: + + curl -H "Host:" www.server.com + +FTP and PATH NAMES + + Do note that when getting files with the ftp:// URL, the given path is + relative the directory you enter. To get the file 'README' from your home + directory at your ftp site, do: + + curl ftp://user:passwd@my.site.com/README + + But if you want the README file from the root directory of that very same + site, you need to specify the absolute file name: + + curl ftp://user:passwd@my.site.com//README + + (I.e with an extra slash in front of the file name.) + +FTP and firewalls + + The FTP protocol requires one of the involved parties to open a second + connction as soon as data is about to get transfered. There are two ways to + do this. + + The default way for curl is to issue the PASV command which causes the + server to open another port and await another connection performed by the + client. This is good if the client is behind a firewall that don't allow + incoming connections. + + curl ftp.download.com + + If the server for example, is behind a firewall that don't allow connections + on other ports than 21 (or if it just doesn't support the PASV command), the + other way to do it is to use the PORT command and instruct the server to + connect to the client on the given (as parameters to the PORT command) IP + number and port. + + The -P flag to curl supports a few different options. Your machine may have + several IP-addresses and/or network interfaces and curl allows you to select + which of them to use. Default address can also be used: + + curl -P - ftp.download.com + + Download with PORT but use the IP address of our 'le0' interface (this does + not work on windows): + + curl -P le0 ftp.download.com + + Download with PORT but use 192.168.0.10 as our IP address to use: + + curl -P 192.168.0.10 ftp.download.com + +NETWORK INTERFACE + + Get a web page from a server using a specified port for the interface: + + curl --interface eth0:1 http://www.netscape.com/ + + or + + curl --interface 192.168.1.10 http://www.netscape.com/ + +HTTPS + + Secure HTTP requires SSL libraries to be installed and used when curl is + built. If that is done, curl is capable of retrieving and posting documents + using the HTTPS procotol. + + Example: + + curl https://www.secure-site.com + + Curl is also capable of using your personal certificates to get/post files + from sites that require valid certificates. The only drawback is that the + certificate needs to be in PEM-format. PEM is a standard and open format to + store certificates with, but it is not used by the most commonly used + browsers (Netscape and MSIE both use the so called PKCS#12 format). If you + want curl to use the certificates you use with your (favourite) browser, you + may need to download/compile a converter that can convert your browser's + formatted certificates to PEM formatted ones. This kind of converter is + included in recent versions of OpenSSL, and for older versions Dr Stephen + N. Henson has written a patch for SSLeay that adds this functionality. You + can get his patch (that requires an SSLeay installation) from his site at: + http://www.drh-consultancy.demon.co.uk/ + + Example on how to automatically retrieve a document using a certificate with + a personal password: + + curl -E /path/to/cert.pem:password https://secure.site.com/ + + If you neglect to specify the password on the command line, you will be + prompted for the correct password before any data can be received. + + Many older SSL-servers have problems with SSLv3 or TLS, that newer versions + of OpenSSL etc is using, therefore it is sometimes useful to specify what + SSL-version curl should use. Use -3, -2 or -1 to specify that exact SSL + version to use (for SSLv3, SSLv2 or TLSv1 respectively): + + curl -2 https://secure.site.com/ + + Otherwise, curl will first attempt to use v3 and then v2. + + To use OpenSSL to convert your favourite browser's certificate into a PEM + formatted one that curl can use, do something like this (assuming netscape, + but IE is likely to work similarly): + + You start with hitting the 'security' menu button in netscape. + + Select 'certificates->yours' and then pick a certificate in the list + + Press the 'export' button + + enter your PIN code for the certs + + select a proper place to save it + + Run the 'openssl' application to convert the certificate. If you cd to the + openssl installation, you can do it like: + + # ./apps/openssl pkcs12 -in [file you saved] -clcerts -out [PEMfile] + + +RESUMING FILE TRANSFERS + + To continue a file transfer where it was previously aborted, curl supports + resume on http(s) downloads as well as ftp uploads and downloads. + + Continue downloading a document: + + curl -C - -o file ftp://ftp.server.com/path/file + + Continue uploading a document(*1): + + curl -C - -T file ftp://ftp.server.com/path/file + + Continue downloading a document from a web server(*2): + + curl -C - -o file http://www.server.com/ + + (*1) = This requires that the ftp server supports the non-standard command + SIZE. If it doesn't, curl will say so. + + (*2) = This requires that the web server supports at least HTTP/1.1. If it + doesn't, curl will say so. + +TIME CONDITIONS + + HTTP allows a client to specify a time condition for the document it + requests. It is If-Modified-Since or If-Unmodified-Since. Curl allow you to + specify them with the -z/--time-cond flag. + + For example, you can easily make a download that only gets performed if the + remote file is newer than a local copy. It would be made like: + + curl -z local.html http://remote.server.com/remote.html + + Or you can download a file only if the local file is newer than the remote + one. Do this by prepending the date string with a '-', as in: + + curl -z -local.html http://remote.server.com/remote.html + + You can specify a "free text" date as condition. Tell curl to only download + the file if it was updated since yesterday: + + curl -z yesterday http://remote.server.com/remote.html + + Curl will then accept a wide range of date formats. You always make the date + check the other way around by prepending it with a dash '-'. + +DICT + + For fun try + + curl dict://dict.org/m:curl + curl dict://dict.org/d:heisenbug:jargon + curl dict://dict.org/d:daniel:web1913 + + Aliases for 'm' are 'match' and 'find', and aliases for 'd' are 'define' + and 'lookup'. For example, + + curl dict://dict.org/find:curl + + Commands that break the URL description of the RFC (but not the DICT + protocol) are + + curl dict://dict.org/show:db + curl dict://dict.org/show:strat + + Authentication is still missing (but this is not required by the RFC) + +LDAP + + If you have installed the OpenLDAP library, curl can take advantage of it + and offer ldap:// support. + + LDAP is a complex thing and writing an LDAP query is not an easy task. I do + advice you to dig up the syntax description for that elsewhere. Two places + that might suit you are: + + Netscape's "Netscape Directory SDK 3.0 for C Programmer's Guide Chapter 10: + Working with LDAP URLs": + http://developer.netscape.com/docs/manuals/dirsdk/csdk30/url.htm + + RFC 2255, "The LDAP URL Format" http://www.rfc-editor.org/rfc/rfc2255.txt + + To show you an example, this is now I can get all people from my local LDAP + server that has a certain sub-domain in their email address: + + curl -B "ldap://ldap.frontec.se/o=frontec??sub?mail=*sth.frontec.se" + + If I want the same info in HTML format, I can get it by not using the -B + (enforce ASCII) flag. + +ENVIRONMENT VARIABLES + + Curl reads and understands the following environment variables: + + http_proxy, HTTPS_PROXY, FTP_PROXY, GOPHER_PROXY + + They should be set for protocol-specific proxies. General proxy should be + set with + + ALL_PROXY + + A comma-separated list of host names that shouldn't go through any proxy is + set in (only an asterisk, '*' matches all hosts) + + NO_PROXY + + If a tail substring of the domain-path for a host matches one of these + strings, transactions with that node will not be proxied. + + + The usage of the -x/--proxy flag overrides the environment variables. + +NETRC + + Unix introduced the .netrc concept a long time ago. It is a way for a user + to specify name and password for commonly visited ftp sites in a file so + that you don't have to type them in each time you visit those sites. You + realize this is a big security risk if someone else gets hold of your + passwords, so therefor most unix programs won't read this file unless it is + only readable by yourself (curl doesn't care though). + + Curl supports .netrc files if told so (using the -n/--netrc and + --netrc-optional options). This is not restricted to only ftp, + but curl can use it for all protocols where authentication is used. + + A very simple .netrc file could look something like: + + machine curl.haxx.se login iamdaniel password mysecret + +CUSTOM OUTPUT + + To better allow script programmers to get to know about the progress of + curl, the -w/--write-out option was introduced. Using this, you can specify + what information from the previous transfer you want to extract. + + To display the amount of bytes downloaded together with some text and an + ending newline: + + curl -w 'We downloaded %{size_download} bytes\n' www.download.com + +KERBEROS4 FTP TRANSFER + + Curl supports kerberos4 for FTP transfers. You need the kerberos package + installed and used at curl build time for it to be used. + + First, get the krb-ticket the normal way, like with the kauth tool. Then use + curl in way similar to: + + curl --krb4 private ftp://krb4site.com -u username:fakepwd + + There's no use for a password on the -u switch, but a blank one will make + curl ask for one and you already entered the real password to kauth. + +TELNET + + The curl telnet support is basic and very easy to use. Curl passes all data + passed to it on stdin to the remote server. Connect to a remote telnet + server using a command line similar to: + + curl telnet://remote.server.com + + And enter the data to pass to the server on stdin. The result will be sent + to stdout or to the file you specify with -o. + + You might want the -N/--no-buffer option to switch off the buffered output + for slow connections or similar. + + Pass options to the telnet protocol negotiation, by using the -t option. To + tell the server we use a vt100 terminal, try something like: + + curl -tTTYPE=vt100 telnet://remote.server.com + + Other interesting options for it -t include: + + - XDISPLOC= Sets the X display location. + + - NEW_ENV= Sets an environment variable. + + NOTE: the telnet protocol does not specify any way to login with a specified + user and password so curl can't do that automatically. To do that, you need + to track when the login prompt is received and send the username and + password accordingly. + +PERSISTANT CONNECTIONS + + Specifying multiple files on a single command line will make curl transfer + all of them, one after the other in the specified order. + + libcurl will attempt to use persistant connections for the transfers so that + the second transfer to the same host can use the same connection that was + already initiated and was left open in the previous transfer. This greatly + decreases connection time for all but the first transfer and it makes a far + better use of the network. + + Note that curl cannot use persistant connections for transfers that are used + in subsequence curl invokes. Try to stuff as many URLs as possible on the + same command line if they are using the same host, as that'll make the + transfers faster. If you use a http proxy for file transfers, practicly + all transfers will be persistant. + +MAILING LISTS + + For your convenience, we have several open mailing lists to discuss curl, + its development and things relevant to this. Get all info at + http://curl.haxx.se/mail/. Some of the lists available are: + + curl-users + + Users of the command line tool. How to use it, what doesn't work, new + features, related tools, questions, news, installations, compilations, + running, porting etc. + + curl-library + + Developers using or developing libcurl. Bugs, extensions, improvements. + + curl-announce + + Low-traffic. Only receives announcements of new public versions. At worst, + that makes something like one or two mails per month, but usually only one + mail every second month. + + curl-and-php + + Using the curl functions in PHP. Everything curl with a PHP angle. Or PHP + with a curl angle. + + curl-and-python + + Python hackers using curl with or without the python binding pycurl. + + Please direct curl questions, feature requests and trouble reports to one of + these mailing lists instead of mailing any individual. diff --git a/src/curl-7.12.2/docs/Makefile.am b/src/curl-7.12.2/docs/Makefile.am new file mode 100644 index 0000000..f3b19b8 --- /dev/null +++ b/src/curl-7.12.2/docs/Makefile.am @@ -0,0 +1,41 @@ +# +# $Id: Makefile.am,v 1.34 2004/07/05 11:43:32 bagder Exp $ +# + +AUTOMAKE_OPTIONS = foreign no-dependencies + +man_MANS = curl.1 curl-config.1 +GENHTMLPAGES = curl.html curl-config.html +PDFPAGES = curl.pdf curl-config.pdf + +HTMLPAGES = $(GENHTMLPAGES) index.html + +SUBDIRS = examples libcurl + +CLEANFILES = $(GENHTMLPAGES) $(PDFPAGES) + +EXTRA_DIST = MANUAL BUGS CONTRIBUTE FAQ FEATURES INTERNALS SSLCERTS \ + README.win32 RESOURCES TODO TheArtOfHttpScripting THANKS VERSIONS \ + KNOWN_BUGS BINDINGS $(man_MANS) $(HTMLPAGES) HISTORY INSTALL \ + $(PDFPAGES) LICENSE-MIXING README.netware + +MAN2HTML= roffit < $< >$@ + +SUFFIXES = .1 .html .pdf + +html: $(HTMLPAGES) + cd libcurl; make html + +pdf: $(PDFPAGES) + cd libcurl; make pdf + +.1.html: + $(MAN2HTML) + +.1.pdf: + @(foo=`echo $@ | sed -e 's/\.[0-9]$$//g'`; \ + groff -Tps -man $< >$$foo.ps; \ + ps2pdf $$foo.ps $@; \ + rm $$foo.ps; \ + echo "converted $< to $@") + diff --git a/src/curl-7.12.2/docs/Makefile.in b/src/curl-7.12.2/docs/Makefile.in new file mode 100644 index 0000000..b2321ab --- /dev/null +++ b/src/curl-7.12.2/docs/Makefile.in @@ -0,0 +1,580 @@ +# Makefile.in generated by automake 1.8.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# $Id: Makefile.am,v 1.34 2004/07/05 11:43:32 bagder Exp $ +# +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_triplet = @host@ +subdir = docs +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in INSTALL \ + THANKS TODO +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/lib/config.h \ + $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +depcomp = +am__depfiles_maybe = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +man1dir = $(mandir)/man1 +am__installdirs = "$(DESTDIR)$(man1dir)" +MANS = $(man_MANS) +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CABUNDLE_FALSE = @CABUNDLE_FALSE@ +CABUNDLE_TRUE = @CABUNDLE_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CROSSCOMPILING_FALSE = @CROSSCOMPILING_FALSE@ +CROSSCOMPILING_TRUE = @CROSSCOMPILING_TRUE@ +CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ +CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ +CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ +CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ +CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ +CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ +CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ +CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +HAVE_ARES = @HAVE_ARES@ +HAVE_LIBZ = @HAVE_LIBZ@ +HAVE_LIBZ_FALSE = @HAVE_LIBZ_FALSE@ +HAVE_LIBZ_TRUE = @HAVE_LIBZ_TRUE@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPV6_ENABLED = @IPV6_ENABLED@ +KRB4_ENABLED = @KRB4_ENABLED@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MANOPT = @MANOPT@ +MIMPURE_FALSE = @MIMPURE_FALSE@ +MIMPURE_TRUE = @MIMPURE_TRUE@ +NO_UNDEFINED_FALSE = @NO_UNDEFINED_FALSE@ +NO_UNDEFINED_TRUE = @NO_UNDEFINED_TRUE@ +NROFF = @NROFF@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_ENABLED = @OPENSSL_ENABLED@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKGADD_NAME = @PKGADD_NAME@ +PKGADD_PKG = @PKGADD_PKG@ +PKGADD_VENDOR = @PKGADD_VENDOR@ +PKGCONFIG = @PKGCONFIG@ +RANDOM_FILE = @RANDOM_FILE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_MANUAL_FALSE = @USE_MANUAL_FALSE@ +USE_MANUAL_TRUE = @USE_MANUAL_TRUE@ +VERSION = @VERSION@ +VERSIONNUM = @VERSIONNUM@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_AS = @ac_ct_AS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DLLTOOL = @ac_ct_DLLTOOL@ +ac_ct_F77 = @ac_ct_F77@ +ac_ct_OBJDUMP = @ac_ct_OBJDUMP@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +AUTOMAKE_OPTIONS = foreign no-dependencies +man_MANS = curl.1 curl-config.1 +GENHTMLPAGES = curl.html curl-config.html +PDFPAGES = curl.pdf curl-config.pdf +HTMLPAGES = $(GENHTMLPAGES) index.html +SUBDIRS = examples libcurl +CLEANFILES = $(GENHTMLPAGES) $(PDFPAGES) +EXTRA_DIST = MANUAL BUGS CONTRIBUTE FAQ FEATURES INTERNALS SSLCERTS \ + README.win32 RESOURCES TODO TheArtOfHttpScripting THANKS VERSIONS \ + KNOWN_BUGS BINDINGS $(man_MANS) $(HTMLPAGES) HISTORY INSTALL \ + $(PDFPAGES) LICENSE-MIXING README.netware + +MAN2HTML = roffit < $< >$@ +SUFFIXES = .1 .html .pdf +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .1 .html .pdf +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign docs/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign docs/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-man1: $(man1_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(man1dir)" + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \ + done +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \ + rm -f "$(DESTDIR)$(man1dir)/$$inst"; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if (etags --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + else \ + include_option=--include; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || mkdir "$(distdir)/$$subdir" \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="../$(top_distdir)" \ + distdir="../$(distdir)/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(MANS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(man1dir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: install-man + +install-exec-am: + +install-info: install-info-recursive + +install-man: install-man1 + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am uninstall-man + +uninstall-info: uninstall-info-recursive + +uninstall-man: uninstall-man1 + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-libtool clean-recursive ctags \ + ctags-recursive distclean distclean-generic distclean-libtool \ + distclean-recursive distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-man1 install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-libtool mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am \ + uninstall-man uninstall-man1 + + +html: $(HTMLPAGES) + cd libcurl; make html + +pdf: $(PDFPAGES) + cd libcurl; make pdf + +.1.html: + $(MAN2HTML) + +.1.pdf: + @(foo=`echo $@ | sed -e 's/\.[0-9]$$//g'`; \ + groff -Tps -man $< >$$foo.ps; \ + ps2pdf $$foo.ps $@; \ + rm $$foo.ps; \ + echo "converted $< to $@") +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/curl-7.12.2/docs/README.netware b/src/curl-7.12.2/docs/README.netware new file mode 100644 index 0000000..00a241e --- /dev/null +++ b/src/curl-7.12.2/docs/README.netware @@ -0,0 +1,27 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +README.netware + + Read the README file first. + + Curl has been successfully compiled with gcc / nlmconv on different flavours + of Linux as well as with the official Metrowerks CodeWarrior compiler. + While not being the main development target, a continously growing share of + curl users are NetWare-based, specially also consuming the lib from PHP. + + The unix-style man pages are tricky to read on windows, so therefore are all + those pages converted to HTML as well as pdf, and included in the release + archives. + + The main curl.1 man page is also "built-in" in the command line tool. Use a + command line similar to this in order to extract a separate text file: + + curl -M >manual.txt + + Read the INSTALL file for instructions how to compile curl self. + + diff --git a/src/curl-7.12.2/docs/README.win32 b/src/curl-7.12.2/docs/README.win32 new file mode 100644 index 0000000..110af3c --- /dev/null +++ b/src/curl-7.12.2/docs/README.win32 @@ -0,0 +1,22 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +README.win32 + + Read the README file first. + + Curl has been compiled, built and run on all sorts of Windows and win32 + systems. While not being the main develop target, a fair share of curl users + are win32-based. + + The unix-style man pages are tricky to read on windows, so therefore are all + those pages converted to HTML as well as pdf, and included in the release + archives. + + The main curl.1 man page is also "built-in" in the command line tool. Use a + command line similar to this in order to extract a separate text file: + + curl -M >manual.txt diff --git a/src/curl-7.12.2/docs/RESOURCES b/src/curl-7.12.2/docs/RESOURCES new file mode 100644 index 0000000..408efe1 --- /dev/null +++ b/src/curl-7.12.2/docs/RESOURCES @@ -0,0 +1,72 @@ + _ _ ____ _ + Project ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + +This document lists documents and standards used by curl. + + RFC 959 - The FTP protocol + + RFC 1635 - How to Use Anonymous FTP + + RFC 1738 - Uniform Resource Locators + + RFC 1777 - defines the LDAP protocol + + RFC 1808 - Relative Uniform Resource Locators + + RFC 1867 - Form-based File Upload in HTML + + RFC 1950 - ZLIB Compressed Data Format Specification + + RFC 1951 - DEFLATE Compressed Data Format Specification + + RFC 1952 - gzip compression format + + RFC 1959 - LDAP URL syntax + + RFC 2045-2049 - Everything you need to know about MIME! (needed for form + based upload) + + RFC 2068 - HTTP 1.1 (obsoleted by RFC 2616) + + RFC 2109 - HTTP State Management Mechanism (cookie stuff) + - Also, read Netscape's specification at + http://curl.haxx.se/rfc/cookie_spec.html + + RFC 2183 - The Content-Disposition Header Field + + RFC 2229 - A Dictionary Server Protocol + + RFC 2255 - Newer LDAP URL syntax document. + + RFC 2231 - MIME Parameter Value and Encoded Word Extensions: + Character Sets, Languages, and Continuations + + RFC 2388 - "Returning Values from Forms: multipart/form-data" + Use this as an addition to the RFC1867 + + RFC 2396 - "Uniform Resource Identifiers: Generic Syntax and Semantics" This + one obsoletes RFC 1738, but since RFC 1738 is often mentioned + I've left it in this list. + + RFC 2428 - FTP Extensions for IPv6 and NATs + + RFC 2577 - FTP Security Considerations + + RFC 2616 - HTTP 1.1, the latest + + RFC 2617 - HTTP Authentication + + RFC 2718 - Guidelines for new URL Schemes + + RFC 2732 - Format for Literal IPv6 Addresses in URL's + + RFC 2818 - HTTP Over TLS (TLS is the successor to SSL) + + RFC 2964 - Use of HTTP State Management + + RFC 2965 - HTTP State Management Mechanism. Cookies. Obsoletes RFC2109 + diff --git a/src/curl-7.12.2/docs/SSLCERTS b/src/curl-7.12.2/docs/SSLCERTS new file mode 100644 index 0000000..3109cdd --- /dev/null +++ b/src/curl-7.12.2/docs/SSLCERTS @@ -0,0 +1,92 @@ + Peer SSL Certificate Verification + ================================= + +libcurl performs peer SSL certificate verification by default. This is done by +installing a default CA cert bundle on 'make install' (or similar), that CA +bundle package is used by default on operations against SSL servers. + +If you communicate with HTTPS or FTPS servers using certificates that are +signed by CAs present in the bundle, you can be sure that the remote server +really is the one it claims to be. + +If the remote server uses a self-signed certificate, if you don't install +curl's CA cert bundle, if the server uses a certificate signed by a CA that +isn't included in the bundle or if the remote host is an impostor +impersonating your favorite site, and you want to transfer files from this +server, do one of the following: + + 1. Tell libcurl to *not* verify the peer. With libcurl you disable with with + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); + + With the curl command line tool, you disable this with -k/--insecure. + + 2. Get a CA certificate that can verify the remote server and use the proper + option to point out this CA cert for verification when connecting. For + libcurl hackers: curl_easy_setopt(curl, CURLOPT_CAPATH, capath); + + With the curl command line tool: --cacert [file] + + 3. Add the CA cert for your server to the existing default CA cert bundle. + The default path of the CA bundle installed with the curl package is: + /usr/local/share/curl/curl-ca-bundle.crt, which can be changed by running + configure with the --with-ca-bundle option pointing out the path of your + choice. + + To do this, you need to get the CA cert for your server in PEM format and + then append that to your CA cert bundle. + + If you use Internet Explorer, this is one way to get extract the CA cert + for a particular server: + + o View the certificate by double-clicking the padlock + o Find out where the CA certificate is kept (Certificate> + Authority Information Access>URL) + o Get a copy of the crt file using curl + o Convert it from crt to PEM using the openssl tool: + openssl x509 -inform DES -in yourdownloaded.crt \ + -out outcert.pem -text + o Append the 'outcert.pem' to the CA cert bundle or use it stand-alone + as described below. + + (Thanks to Frankie V for this description) + + If you use the 'openssl' tool, this is one way to get extract the CA cert + for a particular server: + + o openssl s_client -connect xxxxx.com:443 |tee logfile + o type "QUIT", followed by the "ENTER" key + o The certificate will have "BEGIN CERTIFICATE" and "END CERTIFICATE" + markers. + o If you want to see the data in the certificate, you can do: "openssl + x509 -inform PEM -in certfile -text -out certdata" where certfile is + the cert you extracted from logfile. Look in certdata. + o If you want to trust the certificate, you can append it to your + cert_bundle or use it stand-alone as described. Just remember that the + security is no better than the way you obtained the certificate. + + (Thanks to Doug Kaufman for this description) + + 4. If you're using the curl command line tool, you can specify your own CA + cert path by setting the environment variable CURL_CA_BUNDLE to the path + of your choice. + + If you're using the curl command line tool on Windows, curl will search + for a CA cert file named "curl-ca-bundle.crt" in these directories and in + this order: + 1. application's directory + 2. current working directory + 3. Windows System directory (e.g. C:\windows\system32) + 4. Windows Directory (e.g. C:\windows) + 5. all directories along %PATH% + + 5. Get a better/different/newer CA cert bundle! One option is to extract the + one a recent Mozilla browser uses, by following the instruction found + here: + + http://curl.haxx.se/docs/caextract.html + +Neglecting to use one of the above methods when dealing with a server using a +certificate that isn't signed by one of the certificates in the installed CA +cert bundle, will cause SSL to report an error ("certificate verify failed") +during the handshake and SSL will then refuse further communication with that +server. diff --git a/src/curl-7.12.2/docs/THANKS b/src/curl-7.12.2/docs/THANKS new file mode 100644 index 0000000..5fe5cf2 --- /dev/null +++ b/src/curl-7.12.2/docs/THANKS @@ -0,0 +1,100 @@ + This project has been alive for several years. Countless people have provided + feedback that have improved curl. Here follows a (incomplete) list of people + that have contributed with non-trivial parts: + +Daniel Stenberg +Rafael Sagula +Sampo Kellomaki +Linas Vepstas +Bjorn Reese +Johan Anderson +Kjell Ericson +Troy Engel +Ryan Nelson +Björn Stenberg +Angus Mackay +Eric Young +Simon Dick +Oren Tirosh +Steven G. Johnson +Gilbert Ramirez Jr. +Andrés García +Douglas E. Wegscheid +Mark Butler +Eric Thelin +Marc Boucher +Greg Onufer +Doug Kaufman +David Eriksson +Ralph Beckmann +T. Yamada +Lars J. Aas +Jörn Hartroth +Matthew Clarke +Linus Nielsen Feltzing +Felix von Leitner +Dan Zitter +Jongki Suwandi +Chris Maltby +Ron Zapp +Paul Marquis +Ellis Pritchard +Damien Adant +Chris +Marco G. Salvagno +Paul Marquis +David LeBlanc +Rich Gray at Plus Technologies +Luong Dinh Dung +Torsten Foertsch +Kristian Köhntopp +Fred Noz +Caolan McNamara +Albert Chin-A-Young +Stephen Kick +Martin Hedenfalk +Richard Prescott +Jason S. Priebe +T. Bharath +Alexander Kourakos +James Griffiths +Loic Dachary +Robert Weaver +Ingo Ralf Blum +Jun-ichiro itojun Hagino +Frederic Lepied +Georg Horn +Cris Bailiff +Sterling Hughes +S. Moonesamy +Ingo Wilken +Pawel A. Gajda +Patrick Bihan-Faou +Nico Baggus +Sergio Ballestrero +Andrew Francis +Tomasz Lacki +Georg Huettenegger +John Lask +Eric Lavigne +Marcus Webster +Götz Babin-Ebell +Andreas Damm +Jacky Lam +James Gallagher +Kjetil Jacobsen +Markus F.X.J. Oberhumer +Miklos Nemeth +Kevin Roth +Ralph Mitchell +Dan Fandrich +Jean-Philippe Barrette-LaPierre +Richard Bramante +Daniel Kouril +Dirk Manske +David Meyer +Dominick Meglio +Gisle Vanem +Giuseppe Attardi +Tor Arntsen +David Byron diff --git a/src/curl-7.12.2/docs/TODO b/src/curl-7.12.2/docs/TODO new file mode 100644 index 0000000..081d23c --- /dev/null +++ b/src/curl-7.12.2/docs/TODO @@ -0,0 +1,252 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +TODO + + Things to do in project cURL. Please tell us what you think, contribute and + send us patches that improve things! Also check the http://curl.haxx.se/dev + web section for various technical development notes. + + All bugs documented in the KNOWN_BUGS document are subject for fixing! + + LIBCURL + + * Introduce an interface to libcurl that allows applications to easier get to + know what cookies that are received. Pushing interface that calls a + callback on each received cookie? Querying interface that asks about + existing cookies? We probably need both. Enable applications to modify + existing cookies as well. http://curl.haxx.se/dev/COOKIES + + * Introduce another callback interface for upload/download that makes one + less copy of data and thus a faster operation. + [http://curl.haxx.se/dev/no_copy_callbacks.txt] + + * More data sharing. curl_share_* functions already exist and work, and they + can be extended to share more. For example, enable sharing of the ares + channel. + + * Introduce a new error code indicating authentication problems (for proxy + CONNECT error 407 for example). This cannot be an error code, we must not + return informational stuff as errors, consider a new info returned by + curl_easy_getinfo() #845941 + + * Use 'struct lifreq' and SIOCGLIFADDR instead of 'struct ifreq' and + SIOCGIFADDR on newer Solaris versions as they claim the latter is obsolete. + + LIBCURL - multi interface + + * Add curl_multi_timeout() to make libcurl's ares-functionality better. + + * Make sure we don't ever loop because of non-blocking sockets return + EWOULDBLOCK or similar. This FTP command sending, the SSL connection etc. + + * Make transfers treated more carefully. We need a way to tell libcurl we + have data to write, as the current system expects us to upload data each + time the socket is writable and there is no way to say that we want to + upload data soon just not right now, without that aborting the upload. The + opposite situation should be possible as well, that we tell libcurl we're + ready to accept read data. Today libcurl feeds the data as soon as it is + available for reading, no matter what. + + DOCUMENTATION + + * More and better + + FTP + + * "PASV IP override" - When an FTPS host is behind a NAT firewall, passive + mode fails. The PASV response from the host ["227 PASV Entering passive + mode (_ip_address_, _port_)."] contains the private network IP address of + the host, which since it is encrypted, cannot be modified by the firewall + to the public IP address. What is needed is a cURL option to override the + IP address passed by the host "227 PASV" response. Requested by Ed + Hingsbergen + + * Support GSS/Kerberos 5 for ftp file transfer. This will allow user + authentication and file encryption. Possible libraries and example clients + are available from MIT or Heimdal. Requsted by Markus Moeller. + + * Optimize the way libcurl uses CWD on each new request over a persistent + connection (on FTP) even if it doesn't have to. + + * REST fix for servers not behaving well on >2GB requests. This should fail + if the server doesn't set the pointer to the requested index. The tricky + part is to figure out if the server did the right thing or not. + + * Support the most common FTP proxies, Philip Newton provided a list + allegedly from ncftp: + http://curl.haxx.se/mail/archive-2003-04/0126.html + + * Make CURLOPT_FTPPORT support an additional port number on the IP/if/name, + like "blabla:[port]" or possibly even "blabla:[portfirst]-[portsecond]". + + * FTP ASCII transfers do not follow RFC959. They don't convert the data + accordingly. + + * Since USERPWD always override the user and password specified in URLs, we + might need another way to specify user+password for anonymous ftp logins. + + * The FTP code should get a way of returning errors that is known to still + have the control connection alive and sound. Currently, a returned error + from within ftp-functions does not tell if the control connection is still + OK to use or not. This causes libcurl to fail to re-use connections + slightly too often. + + HTTP + + * Pipelining. Sending multiple requests before the previous one(s) are done. + This could possibly be implemented using the multi interface to queue + requests and the response data. + + TELNET + + * Reading input (to send to the remote server) on stdin is a crappy solution + for library purposes. We need to invent a good way for the application to + be able to provide the data to send. + + * Move the telnet support's network select() loop go away and merge the code + into the main transfer loop. Until this is done, the multi interface won't + work for telnet. + + SSL + + * Anton Fedorov's "dumpcert" patch: + http://curl.haxx.se/mail/lib-2004-03/0088.html + + * Evaluate/apply Gertjan van Wingerde's SSL patches: + http://curl.haxx.se/mail/lib-2004-03/0087.html + + * "Look at SSL cafile - quick traces look to me like these are done on every + request as well, when they should only be necessary once per ssl context + (or once per handle)". The major improvement we can rather easily do is to + make sure we don't create and kill a new SSL "context" for every request, + but instead make one for every connection and re-use that SSL context in + the same style connections are re-used. It will make us use slightly more + memory but it will libcurl do less creations and deletions of SSL contexts. + + * Add an interface to libcurl that enables "session IDs" to get + exported/imported. Cris Bailiff said: "OpenSSL has functions which can + serialise the current SSL state to a buffer of your choice, and + recover/reset the state from such a buffer at a later date - this is used + by mod_ssl for apache to implement and SSL session ID cache". + + * OpenSSL supports a callback for customised verification of the peer + certificate, but this doesn't seem to be exposed in the libcurl APIs. Could + it be? There's so much that could be done if it were! (brought by Chris + Clark) + + * Make curl's SSL layer option capable of using other free SSL libraries. + Such as the Mozilla Security Services + (http://www.mozilla.org/projects/security/pki/nss/) and GnuTLS + (http://www.gnu.org/software/gnutls/) This subject has been brought up + again recently since GPL-licensed applications that link with libcurl MAY + NOT distribute binaries that use OpenSSL without adding an exception clause + to the GPL license. See the LICENSE-MIXING document and this: + http://www.gnome.org/~markmc/openssl-and-the-gpl.html + + LDAP + + * Look over the implementation. The looping will have to "go away" from the + lib/ldap.c source file and get moved to the main network code so that the + multi interface and friends will work for LDAP as well. + + CLIENT + + * "curl --sync http://example.com/feed[1-100].rss" or + "curl --sync http://example.net/{index,calendar,history}.html" + + Downloads a range or set of URLs using the remote name, but only if the + remote file is newer than the local file. A Last-Modified HTTP date header + should also be used to set the mod date on the downloaded file. + (idea from "Brianiac") + + * Globbing support for -d and -F, as in 'curl -d "name=foo[0-9]" URL'. + Requested by Dane Jensen and others. This is easily scripted though. + + * Add an option that prevents cURL from overwiting existing local files. When + used, and there already is an existing file with the target file name + (either -O or -o), a number should be appended (and increased if already + existing). So that index.html becomes first index.html.1 and then + index.html.2 etc. Jeff Pohlmeyer suggested. + + * "curl ftp://site.com/*.txt" + + * The client could be told to use maximum N simultaneous transfers and then + just make sure that happens. It should of course not make more than one + connection to the same remote host. This would require the client to use + the multi interface. + + * Extending the capabilities of the multipart formposting. How about leaving + the ';type=foo' syntax as it is and adding an extra tag (headers) which + works like this: curl -F "coolfiles=@fil1.txt;headers=@fil1.hdr" where + fil1.hdr contains extra headers like + + Content-Type: text/plain; charset=KOI8-R" + Content-Transfer-Encoding: base64 + X-User-Comment: Please don't use browser specific HTML code + + which should overwrite the program reasonable defaults (plain/text, + 8bit...) (Idea brough to us by kromJx) + + * ability to specify the classic computing suffixes on the range + specifications. For example, to download the first 500 Kilobytes of a file, + be able to specify the following for the -r option: "-r 0-500K" or for the + first 2 Megabytes of a file: "-r 0-2M". (Mark Smith suggested) + + * --data-encode that URL encodes the data before posting + http://curl.haxx.se/mail/archive-2003-11/0091.html (Kevin Roth suggested) + + * Provide a way to make options bound to a specific URL among several on the + command line. Possibly by letting ':' separate options between URLs, + similar to this: + + curl --data foo --url url.com : \ + --url url2.com : \ + --url url3.com --data foo3 + + (More details: http://curl.haxx.se/mail/archive-2004-07/0133.html) + + The example would do a POST-GET-POST combination on a single command line. + + BUILD + + * Consider extending 'roffit' to produce decent ASCII output, and use that + instead of (g)nroff when building src/hugehelp.c + + TEST SUITE + + * Make the test servers able to serve multiple running test suites. Like if + two users run 'make test' at once. + + * Make runtests.pl capable of changing port numbers for the servers. This was + the intention from the start, but in practise it is now hard. + + * If perl wasn't found by the configure script, don't attempt to run the + tests but explain something nice why it doesn't. + + * Extend the test suite to include more protocols. The telnet could just do + ftp or http operations (for which we have test servers). + + * Make the test suite work on more platforms. OpenBSD and Mac OS. Remove + fork()s and it should become even more portable. + + NEXT MAJOR RELEASE + + * curl_easy_cleanup() returns void, but curl_multi_cleanup() returns a + CURLMcode. These should be changed to be the same. + + * remove obsolete defines from curl/curl.h + + * make several functions use size_t instead of int in their APIs + + * remove the following functions from the public API: + curl_getenv + curl_mprintf (and variations) + curl_strequal + curl_strnequal + + They will instead become curlx_ - alternatives. That makes the curl app + still capable of building with them from source. diff --git a/src/curl-7.12.2/docs/TheArtOfHttpScripting b/src/curl-7.12.2/docs/TheArtOfHttpScripting new file mode 100644 index 0000000..01ece3c --- /dev/null +++ b/src/curl-7.12.2/docs/TheArtOfHttpScripting @@ -0,0 +1,399 @@ +Online: http://curl.haxx.se/docs/httpscripting.shtml +Author: Daniel Stenberg +Date: November 6, 2001 +Version: 0.6 + + The Art Of Scripting HTTP Requests Using Curl + ============================================= + + This document will assume that you're familiar with HTML and general + networking. + + The possibility to write scripts is essential to make a good computer + system. Unix' capability to be extended by shell scripts and various tools to + run various automated commands and scripts is one reason why it has succeeded + so well. + + The increasing amount of applications moving to the web has made "HTTP + Scripting" more frequently requested and wanted. To be able to automatically + extract information from the web, to fake users, to post or upload data to + web servers are all important tasks today. + + Curl is a command line tool for doing all sorts of URL manipulations and + transfers, but this particular document will focus on how to use it when + doing HTTP requests for fun and profit. I'll assume that you know how to + invoke 'curl --help' or 'curl --manual' to get basic information about it. + + Curl is not written to do everything for you. It makes the requests, it gets + the data, it sends data and it retrieves the information. You probably need + to glue everything together using some kind of script language or repeated + manual invokes. + +1. The HTTP Protocol + + HTTP is the protocol used to fetch data from web servers. It is a very simple + protocol that is built upon TCP/IP. The protocol also allows information to + get sent to the server from the client using a few different methods, as will + be shown here. + + HTTP is plain ASCII text lines being sent by the client to a server to + request a particular action, and then the server replies a few text lines + before the actual requested content is sent to the client. + + Using curl's option -v will display what kind of commands curl sends to the + server, as well as a few other informational texts. -v is the single most + useful option when it comes to debug or even understand the curl<->server + interaction. + +2. URL + + The Uniform Resource Locator format is how you specify the address of a + particular resource on the Internet. You know these, you've seen URLs like + http://curl.haxx.se or https://yourbank.com a million times. + +3. GET a page + + The simplest and most common request/operation made using HTTP is to get a + URL. The URL could itself refer to a web page, an image or a file. The client + issues a GET request to the server and receives the document it asked for. + If you issue the command line + + curl http://curl.haxx.se + + you get a web page returned in your terminal window. The entire HTML document + that that URL holds. + + All HTTP replies contain a set of headers that are normally hidden, use + curl's -i option to display them as well as the rest of the document. You can + also ask the remote server for ONLY the headers by using the -I option (which + will make curl issue a HEAD request). + +4. Forms + + Forms are the general way a web site can present a HTML page with fields for + the user to enter data in, and then press some kind of 'OK' or 'submit' + button to get that data sent to the server. The server then typically uses + the posted data to decide how to act. Like using the entered words to search + in a database, or to add the info in a bug track system, display the entered + address on a map or using the info as a login-prompt verifying that the user + is allowed to see what it is about to see. + + Of course there has to be some kind of program in the server end to receive + the data you send. You cannot just invent something out of the air. + + 4.1 GET + + A GET-form uses the method GET, as specified in HTML like: + +
+ + +
+ + In your favorite browser, this form will appear with a text box to fill in + and a press-button labeled "OK". If you fill in '1905' and press the OK + button, your browser will then create a new URL to get for you. The URL will + get "junk.cgi?birthyear=1905&press=OK" appended to the path part of the + previous URL. + + If the original form was seen on the page "www.hotmail.com/when/birth.html", + the second page you'll get will become + "www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK". + + Most search engines work this way. + + To make curl do the GET form post for you, just enter the expected created + URL: + + curl "www.hotmail.com/when/junk.cgi?birthyear=1905&press=OK" + + 4.2 POST + + The GET method makes all input field names get displayed in the URL field of + your browser. That's generally a good thing when you want to be able to + bookmark that page with your given data, but it is an obvious disadvantage + if you entered secret information in one of the fields or if there are a + large amount of fields creating a very long and unreadable URL. + + The HTTP protocol then offers the POST method. This way the client sends the + data separated from the URL and thus you won't see any of it in the URL + address field. + + The form would look very similar to the previous one: + +
+ + +
+ + And to use curl to post this form with the same data filled in as before, we + could do it like: + + curl -d "birthyear=1905&press=%20OK%20" www.hotmail.com/when/junk.cgi + + This kind of POST will use the Content-Type + application/x-www-form-urlencoded and is the most widely used POST kind. + + The data you send to the server MUST already be properly encoded, curl will + not do that for you. For example, if you want the data to contain a space, + you need to replace that space with %20 etc. Failing to comply with this + will most likely cause your data to be received wrongly and messed up. + + 4.3 FILE UPLOAD POST + + Back in late 1995 they defined a new way to post data over HTTP. It was + documented in the RFC 1867, why this method sometimes is referred to as + a RFC1867-posting. + + This method is mainly designed to better support file uploads. A form that + allows a user to upload a file could be written like this in HTML: + +
+ + +
+ + This clearly shows that the Content-Type about to be sent is + multipart/form-data. + + To post to a form like this with curl, you enter a command line like: + + curl -F upload=@localfilename -F press=OK [URL] + + 4.4 HIDDEN FIELDS + + A very common way for HTML based application to pass state information + between pages is to add hidden fields to the forms. Hidden fields are + already filled in, they aren't displayed to the user and they get passed + along just as all the other fields. + + A similar example form with one visible field, one hidden field and one + submit button could look like: + +
+ + + +
+ + To post this with curl, you won't have to think about if the fields are + hidden or not. To curl they're all the same: + + curl -d "birthyear=1905&press=OK&person=daniel" [URL] + + 4.5 FIGURE OUT WHAT A POST LOOKS LIKE + + When you're about fill in a form and send to a server by using curl instead + of a browser, you're of course very interested in sending a POST exactly the + way your browser does. + + An easy way to get to see this, is to save the HTML page with the form on + your local disk, modify the 'method' to a GET, and press the submit button + (you could also change the action URL if you want to). + + You will then clearly see the data get appended to the URL, separated with a + '?'-letter as GET forms are supposed to. + +5. PUT + + The perhaps best way to upload data to a HTTP server is to use PUT. Then + again, this of course requires that someone put a program or script on the + server end that knows how to receive a HTTP PUT stream. + + Put a file to a HTTP server with curl: + + curl -T uploadfile www.uploadhttp.com/receive.cgi + +6. AUTHENTICATION + + Authentication is the ability to tell the server your username and password + so that it can verify that you're allowed to do the request you're doing. The + Basic authentication used in HTTP (which is the type curl uses by default) is + *plain* *text* based, which means it sends username and password only + slightly obfuscated, but still fully readable by anyone that sniffs on the + network between you and the remote server. + + To tell curl to use a user and password for authentication: + + curl -u name:password www.secrets.com + + The site might require a different authentication method (check the headers + returned by the server), and then --ntlm, --digest, --negotiate or even + --anyauth might be options that suit you. + + Sometimes your HTTP access is only available through the use of a HTTP + proxy. This seems to be especially common at various companies. A HTTP proxy + may require its own user and password to allow the client to get through to + the Internet. To specify those with curl, run something like: + + curl -U proxyuser:proxypassword curl.haxx.se + + If your proxy requires the authentication to be done using the NTLM method, + use --proxy-ntlm. + + If you use any one these user+password options but leave out the password + part, curl will prompt for the password interactively. + + Do note that when a program is run, its parameters are possible to see when + listing the running processes of the system. Thus, other users may be able to + watch your passwords if you pass them as plain command line options. There + are ways to circumvent this. + +7. REFERER + + A HTTP request may include a 'referer' field, which can be used to tell from + which URL the client got to this particular resource. Some programs/scripts + check the referer field of requests to verify that this wasn't arriving from + an external site or an unknown page. While this is a stupid way to check + something so easily forged, many scripts still do it. Using curl, you can put + anything you want in the referer-field and thus more easily be able to fool + the server into serving your request. + + Use curl to set the referer field with: + + curl -e http://curl.haxx.se daniel.haxx.se + +8. USER AGENT + + Very similar to the referer field, all HTTP requests may set the User-Agent + field. It names what user agent (client) that is being used. Many + applications use this information to decide how to display pages. Silly web + programmers try to make different pages for users of different browsers to + make them look the best possible for their particular browsers. They usually + also do different kinds of javascript, vbscript etc. + + At times, you will see that getting a page with curl will not return the same + page that you see when getting the page with your browser. Then you know it + is time to set the User Agent field to fool the server into thinking you're + one of those browsers. + + To make curl look like Internet Explorer on a Windows 2000 box: + + curl -A "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" [URL] + + Or why not look like you're using Netscape 4.73 on a Linux (PIII) box: + + curl -A "Mozilla/4.73 [en] (X11; U; Linux 2.2.15 i686)" [URL] + +9. REDIRECTS + + When a resource is requested from a server, the reply from the server may + include a hint about where the browser should go next to find this page, or a + new page keeping newly generated output. The header that tells the browser + to redirect is Location:. + + Curl does not follow Location: headers by default, but will simply display + such pages in the same manner it display all HTTP replies. It does however + feature an option that will make it attempt to follow the Location: pointers. + + To tell curl to follow a Location: + + curl -L www.sitethatredirects.com + + If you use curl to POST to a site that immediately redirects you to another + page, you can safely use -L and -d/-F together. Curl will only use POST in + the first request, and then revert to GET in the following operations. + +10. COOKIES + + The way the web browsers do "client side state control" is by using + cookies. Cookies are just names with associated contents. The cookies are + sent to the client by the server. The server tells the client for what path + and host name it wants the cookie sent back, and it also sends an expiration + date and a few more properties. + + When a client communicates with a server with a name and path as previously + specified in a received cookie, the client sends back the cookies and their + contents to the server, unless of course they are expired. + + Many applications and servers use this method to connect a series of requests + into a single logical session. To be able to use curl in such occasions, we + must be able to record and send back cookies the way the web application + expects them. The same way browsers deal with them. + + The simplest way to send a few cookies to the server when getting a page with + curl is to add them on the command line like: + + curl -b "name=Daniel" www.cookiesite.com + + Cookies are sent as common HTTP headers. This is practical as it allows curl + to record cookies simply by recording headers. Record cookies with curl by + using the -D option like: + + curl -D headers_and_cookies www.cookiesite.com + + (Take note that the -c option described below is a better way to store + cookies.) + + Curl has a full blown cookie parsing engine built-in that comes to use if you + want to reconnect to a server and use cookies that were stored from a + previous connection (or handicrafted manually to fool the server into + believing you had a previous connection). To use previously stored cookies, + you run curl like: + + curl -b stored_cookies_in_file www.cookiesite.com + + Curl's "cookie engine" gets enabled when you use the -b option. If you only + want curl to understand received cookies, use -b with a file that doesn't + exist. Example, if you want to let curl understand cookies from a page and + follow a location (and thus possibly send back cookies it received), you can + invoke it like: + + curl -b nada -L www.cookiesite.com + + Curl has the ability to read and write cookie files that use the same file + format that Netscape and Mozilla do. It is a convenient way to share cookies + between browsers and automatic scripts. The -b switch automatically detects + if a given file is such a cookie file and parses it, and by using the + -c/--cookie-jar option you'll make curl write a new cookie file at the end of + an operation: + + curl -b cookies.txt -c newcookies.txt www.cookiesite.com + +11. HTTPS + + There are a few ways to do secure HTTP transfers. The by far most common + protocol for doing this is what is generally known as HTTPS, HTTP over + SSL. SSL encrypts all the data that is sent and received over the network and + thus makes it harder for attackers to spy on sensitive information. + + SSL (or TLS as the latest version of the standard is called) offers a + truckload of advanced features to allow all those encryptions and key + infrastructure mechanisms encrypted HTTP requires. + + Curl supports encrypted fetches thanks to the freely available OpenSSL + libraries. To get a page from a HTTPS server, simply run curl like: + + curl https://that.secure.server.com + + 11.1 CERTIFICATES + + In the HTTPS world, you use certificates to validate that you are the one + you you claim to be, as an addition to normal passwords. Curl supports + client-side certificates. All certificates are locked with a pass phrase, + which you need to enter before the certificate can be used by curl. The pass + phrase can be specified on the command line or if not, entered interactively + when curl queries for it. Use a certificate with curl on a HTTPS server + like: + + curl -E mycert.pem https://that.secure.server.com + + curl also tries to verify that the server is who it claims to be, by + verifying the server's certificate against a CA cert bundle. Failing the + verification will cause curl to deny the connection. You must then use -k in + case you want to tell curl to ignore that the server can't be verified. + +12. REFERENCES + + RFC 2616 is a must to read if you want in-depth understanding of the HTTP + protocol. + + RFC 2396 explains the URL syntax. + + RFC 2109 defines how cookies are supposed to work. + + RFC 1867 defines the HTTP post upload format. + + http://www.openssl.org is the home of the OpenSSL project + + http://curl.haxx.se is the home of the cURL project diff --git a/src/curl-7.12.2/docs/VERSIONS b/src/curl-7.12.2/docs/VERSIONS new file mode 100644 index 0000000..0145571 --- /dev/null +++ b/src/curl-7.12.2/docs/VERSIONS @@ -0,0 +1,64 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +Version Numbers and Releases + + Curl is not only curl. Curl is also libcurl. They're actually individually + versioned, but they mostly follow each other rather closely. + + The version numbering is always built up using the same system: + + X.Y[.Z][-preN] + + Where + X is main version number + Y is release number + Z is patch number + N is pre-release number + + One of these numbers will get bumped in each new release. The numbers to the + right of a bumped number will be reset to zero. If Z is zero, it is not + included in the version number. The pre release number is only included in + pre releases (they're never used in public, official, releases). + + The main version number will get bumped when *really* big, world colliding + changes are made. The release number is bumped when big changes are + performed. The patch number is bumped when the changes are mere bugfixes and + only minor feature changes. The pre-release is a counter, to identify which + pre-release a certain release is. + + When reaching the end of a pre-release period, the version without the + pre-release part will be released as a public release. + + It means that after release 1.2.3, we can release 2.0 if something really big + has been made, 1.3 if not that big changes were made or 1.2.4 if mostly bugs + were fixed. Before 1.2.4 is released, we might release a 1.2.4-pre1 release + for the brave people to try before the actual release. + + Bumping, as in increasing the number with 1, is unconditionally only + affecting one of the numbers (except the ones to the right of it, that may be + set to zero). 1 becomes 2, 3 becomes 4, 9 becomes 10, 88 becomes 89 and 99 + becomes 100. So, after 1.2.9 comes 1.2.10. After 3.99.3, 3.100 might come. + + All original curl source release archives are named according to the libcurl + version (not according to the curl client version that, as said before, might + differ). + + As a service to any application that might want to support new libcurl + features while still being able to build with older versions, all releases + have the libcurl version stored in the curl/curl.h file using a static + numbering scheme that can be used for comparison. The version number is + defined as: + + #define LIBCURL_VERSION_NUM 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal. All three numbers are always represented using two digits. 1.2 + would appear as "0x010200" while version 9.11.7 appears as "0x090b07". + + This 6-digit hexadecimal number does not show pre-release number, and it is + always a greater number in a more recent release. It makes comparisons with + greater than and less than work. diff --git a/src/curl-7.12.2/docs/curl-config.1 b/src/curl-7.12.2/docs/curl-config.1 new file mode 100644 index 0000000..bc1935f --- /dev/null +++ b/src/curl-7.12.2/docs/curl-config.1 @@ -0,0 +1,64 @@ +.\" You can view this file with: +.\" nroff -man curl-config.1 +.\" Written by Daniel Stenberg +.\" +.TH curl-config 1 "8 Oct 2002" "Curl 7.10" "curl-config manual" +.SH NAME +curl-config \- Get information about a libcurl installation +.SH SYNOPSIS +.B curl-config [options] +.SH DESCRIPTION +.B curl-config +displays information about a previous curl and libcurl installation. +.SH OPTIONS +.IP "--ca" +Displays the built-in path to the CA cert bundle this libcurl uses. +.IP "--cc" +Displays the compiler used to build libcurl. +.IP "--cflags" +Set of compiler options (CFLAGS) to use when compiling files that use +libcurl. Currently that is only thw include path to the curl include files. +.IP "--feature" +Lists what particular main features the installed libcurl was built with. At +the time of writing, this list may include SSL, KRB4 or IPv6. Do not assume +any particular order. The keywords will be separated by newlines. There may be +none, one or several keywords in the list. +.IP "--help" +Displays the available options. +.IP "--libs" +Shows the complete set of libs and other linker options you will need in order +to link your application with libcurl. +.IP "--prefix" +This is the prefix used when libcurl was installed. Libcurl is then installed +in $prefix/lib and its header files are installed in $prefix/include and so +on. The prefix is set with "configure --prefix". +.IP "--version" +Outputs version information about the installed libcurl. +.IP "--vernum" +Outputs version information about the installed libcurl, in numerical mode. +This outputs the version number, in hexadecimal, with 8 bits for each part; +major, minor, patch. So that libcurl 7.7.4 would appear as 070704 and libcurl +12.13.14 would appear as 0c0d0e... +.SH "EXAMPLES" +What linker options do I need when I link with libcurl? + + $ curl-config --libs + +What compiler options do I need when I compile using libcurl functions? + + $ curl-config --cflags + +How do I know if libcurl was built with SSL support? + + $ curl-config --feature | grep SSL + +What's the installed libcurl version? + + $ curl-config --version + +How do I build a single file with a one-line command? + + $ `curl-config --cc --cflags --libs` -o example example.c + +.SH "SEE ALSO" +.BR curl (1) diff --git a/src/curl-7.12.2/docs/curl-config.html b/src/curl-7.12.2/docs/curl-config.html new file mode 100644 index 0000000..796df86 --- /dev/null +++ b/src/curl-7.12.2/docs/curl-config.html @@ -0,0 +1,82 @@ + +curl-config man page + + + + +

NAME

+

curl-config - Get information about a libcurl installation

SYNOPSIS

+

curl-config [options]

DESCRIPTION

+

curl-config displays information about a previous curl and libcurl installation.

OPTIONS

+

+

--ca +

Displays the built-in path to the CA cert bundle this libcurl uses. +

--cc +

Displays the compiler used to build libcurl. +

--cflags +

Set of compiler options (CFLAGS) to use when compiling files that use libcurl. Currently that is only thw include path to the curl include files. +

--feature +

Lists what particular main features the installed libcurl was built with. At the time of writing, this list may include SSL, KRB4 or IPv6. Do not assume any particular order. The keywords will be separated by newlines. There may be none, one or several keywords in the list. +

--help +

Displays the available options. +

--libs +

Shows the complete set of libs and other linker options you will need in order to link your application with libcurl. +

--prefix +

This is the prefix used when libcurl was installed. Libcurl is then installed in $prefix/lib and its header files are installed in $prefix/include and so on. The prefix is set with "configure --prefix". +

--version +

Outputs version information about the installed libcurl. +

--vernum +

Outputs version information about the installed libcurl, in numerical mode. This outputs the version number, in hexadecimal, with 8 bits for each part; major, minor, patch. So that libcurl 7.7.4 would appear as 070704 and libcurl 12.13.14 would appear as 0c0d0e...

EXAMPLES

+

What linker options do I need when I link with libcurl? +

  $ curl-config --libs +

What compiler options do I need when I compile using libcurl functions? +

  $ curl-config --cflags +

How do I know if libcurl was built with SSL support? +

  $ curl-config --feature | grep SSL +

What's the installed libcurl version? +

  $ curl-config --version +

How do I build a single file with a one-line command? +

  $ `curl-config --cc --cflags --libs` -o example example.c +

SEE ALSO

+

curl (1)

+ This HTML page was made with roffit. + diff --git a/src/curl-7.12.2/docs/curl-config.pdf b/src/curl-7.12.2/docs/curl-config.pdf new file mode 100644 index 0000000000000000000000000000000000000000..eeccc59c29c6285afeda964e1f74a8e5e5dba063 GIT binary patch literal 8389 zcmeGiXIKCu}z3=wE`{8`w`|5SQSM@4&RrLdX7aKcM z+>DOsmp)6(L|7;dmH34qlP6g*MB!F*Wfym@i zK2L;1?n?Ej5!cxDi@N2Z74w68H`y8QdspI0!tc6e4V;v;{75hoV4GuVFz7&w-#IHw zowlu?{DXp$b&V&4`Sx(=*ZjO9C4~{R+N`0j$@16xa>o0~i^H4Gh|J$>?b=WjeYMYt z`E|U^XwSP&6`I# z*0RVlKScQHQB8UFd!fa#{n(Y!uPo(l9>;I6o}L+IFL6{=HlF4YhL;q~iyi7fs*4$m{k^H^YtRnu4cS7Zipk+Fn3o9J^bN9ryI~ z>oxj1?&rf@HS$cHqRd};=I3EP!8ehlRSe#Wd|Hi(WY?kPIl`M&Thp=BRO^EW&->U3 zeX45B<{U4XeZ3e$3xznv!dN<&9NV%@uS%`_Q~0fMv<20A0L*;)gM*NwE0}N z+dl76b^03ZSta5eBvf~TxIQ#D#A$=@alRT7ByvBHdErH+*PM}$b**35A1l5XBW~Vk zAh?%%JrET4M&<(y&8iQ4U?Mc?$e&6 zr#GO-kWhEyi;b?li%3NGcgfBHB0WvQY(w8E_l-3tI&Ti>(Z_z~P~oGe4u{$fzla}v ze1ClAQ*o@G#&*lcO{-!{7PiiPSYyA~#LXv=YqgKf(ibfmvo&&VeYED5OEfDd zp5*LLC05DL%WrPXbn%oq4KHwaTzMoTal*zLQQF?C_+$UK89!HDzuL@Ax{|Rn>k- zrpdJ9mXDZ_I%S=U_6Wb6JIUQJk zdsfcL!wLQ5^I{H$6gR1lJ$&ju&8#M{CbQ+L8pg{VX3i;e8#S1xri;A!XdgDHxv`s8 za%>u7A|w+?o_0TYG}77?Oov6&tTfo z#u3^}<9+MHY}BvK?=fIBmgD1J`tGEU=I&dD!<+k>$j2QmcPx0mZ$)I*^#vQ&rY#J; zIZV%Uhe+@I483?>{JI`agN;&h1?itw&HY$#Xq`>dxoL0mt<^&Pbyn3@{e-wx2b~T} zh)Ap$#Bk}A7_jBRp48;P!;g1pmyUm(vspT1%j)49SDV1N6e-t%kkt|)T8{FxIaiwOTzIb+d>;`l2@`|PJ? zjH~i7@X)VVXR}$Wa@|Dj!7qZ&m@Ked^hWpC4)wrcEt8uKPFOlgKi}rb?~BtuBoV&p z=GFRRmlv>I3|ot;AHLpa>bk_LD#kE9)BCOZp#|m75Ac@;$&S*`j6HfMS9*Bn!jL8R zs%FgEk-H{FO*4h>VmO+;_?9?scLYsi@|8TJkF9uBp*-KBX>Z@Ag~g`X=iSydc&r_3 zp~GF67aLu7#C4wN-N>H5WejwR9u-nsy|+HBdFe5|El1AHk3QX}=Reo}VEAfacXaoP z(%%ZE@ARr`O8wWgR-ZddUe7xDWTaz^L9p|J;rm9p=B?l0GoFZN8}&YtIAH6Z-gwmG z>mdOXN6%t4M}`(Rsd4nTr(R$Cwwu!_hm%$-+R}I>Bik;yj+4DJeYm9VtPUfd!=RUW zk2f~ZODNTd$NCQN(_x|WJ+@zt54$YNjpi))rT9Yeh0kWl{b_u0fTEy*87zXZ^g)QT z83dxh;q=Z5>B>ZyusVWDNUbuIR&gA`ak!4TauKe|Mv8Z5p~X4m-an|c-Wv)wl+d(R zFS3j+rJp@3zVpV86ESsSx8Ytc&Si@4t)Ecmn}JCyCN*}?&w*f z<~!2eu8ZkOV?f)}aTaAL;gVMk*DPJr{p$}aFnlFP?PXV?PmfWc7GVlotS6a>i7c?fA<1aDWv8wH#qfTnaM4obdhXE2o~=Lsc2 z6sHcODBcwK;wXEl!OsUS=)k&+%D z8Mp~Ve3_}WL>Qn%4E_Ee6RS%6?UP+4BA&SOX!vgb`IA+M$v{_7&yMhu!-$4P#xZN2 zj1T3&I@KjtNINJjNvDDCT`H4L4dzKfkUQ}dQ#wIl9)bXQunhQ@gQEmEh9Fodi^WD6 z94^XaGEp|2j^Z>L4W+X=D4hWC_XqM7{MYuY;z@c2g8>8}Py(C>2YE0DAmIWv$VGO$ zAP`3$%;q2z(I3GMK7a>74H^yMGH9f>P^+1{z3Jw`66a!2VI46Y%E`X+oOdwRoQ#5c5&Y^MO9MY4Jq5*=EbnR=T zG9+&xJjs*GMCriY3;^LiV8qVz!5#+iGTe^?B}w5aViHNl1Ka_*gQ4&}Ld6K=__aYX zg4TfhI~W49bOr%9n%W2<6cs|ql+6UushCs#BXJA{!i03dr{p@d2MWpTjF)y=1uvik zTQLBCgmRH8?C>8M5HKhd0|tcB=wtviqk@KlP%v_?2rlJAVDJD-el>{UUg$T~L0TM~ zrGV}93ZE+*ketI{hXzu3tpidx8qHfdpDQKwzn*h&{Cmz(m|GEtFm|bUCE@p+^F8N$ z&p9CHV76j#ohT3ofjkeoB&Dw@@>#iSqIP07g1|sNXx2e@J`cf}G%Qfy-PvrBUju)p zUn2XgE)5e-{BiPPvOp`qB(pZzA}BXZTn?z5V0@z|`mgm%Oj>&lg_#|81F*s<*IqK? zvp}^6We(y=nf^k%L{%69@Sp~$LI@XpR=}!6RYG#!Ud1}vCsGbjA*nTvVh*0kZiS+U zm56Mg06&Q9XsaN9z>}0)NiU!i*{#582=!Bja4)Gdz<0>5m_yqr);cOJB_rHR(Loug zIazU`FX1@=lCu2Ix+U^-pe2+DN(i5!6@gX0-Yfx0q4ZEvHVayTMb0VjK^$0XZ`2m>}@T>Hs^?_UzKUGNf zEg%9Y+o(bqk*YZOOhtnNc0>T>=`SFRc7+|k0>8*xgXQ!x)Ah=K1}ODQ}4ecyQ0+Xvs=?^&xoGyXqjEtR`#y6Oejx3U%% zD1_JIm`EUwkSVoZO1Ii9GgvmjQJ&wPO!iwr3#J(3b!hb&g#OCMuUyx zm$WzsUcP;aqjT{t=d$1(gX%suw~H)HyemJP)0GzIfX{VRejEba+kPnv!RTriIupEj zD*JFQD90)|I?nB)2Oa#Wqsu<7pnC!{&f4NMGM}rXBKcMh`3V literal 0 HcmV?d00001 diff --git a/src/curl-7.12.2/docs/curl.1 b/src/curl-7.12.2/docs/curl.1 new file mode 100644 index 0000000..991f373 --- /dev/null +++ b/src/curl-7.12.2/docs/curl.1 @@ -0,0 +1,1190 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at http://curl.haxx.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" * $Id: curl.1,v 1.130 2004/09/30 14:38:29 bagder Exp $ +.\" ************************************************************************** +.\" +.TH curl 1 "3 May 2004" "Curl 7.12" "Curl Manual" +.SH NAME +curl \- transfer a URL +.SH SYNOPSIS +.B curl [options] +.I [URL...] +.SH DESCRIPTION +.B curl +is a tool to transfer data from or to a server, using one of the supported +protocols (HTTP, HTTPS, FTP, FTPS, GOPHER, DICT, TELNET, LDAP or FILE). The +command is designed to work without user interaction. + +curl offers a busload of useful tricks like proxy support, user +authentication, ftp upload, HTTP post, SSL (https:) connections, cookies, file +transfer resume and more. As you will see below, the amount of features will +make your head spin! + +curl is powered by libcurl for all transfer-related features. See +.BR libcurl (3) +for details. +.SH URL +The URL syntax is protocol dependent. You'll find a detailed description in +RFC 2396. + +You can specify multiple URLs or parts of URLs by writing part sets within +braces as in: + + http://site.{one,two,three}.com + +or you can get sequences of alphanumeric series by using [] as in: + + ftp://ftp.numericals.com/file[1-100].txt + ftp://ftp.numericals.com/file[001-100].txt (with leading zeros) + ftp://ftp.letters.com/file[a-z].txt + +No nesting of the sequences is supported at the moment, but you can use +several ones next to each other: + + http://any.org/archive[1996-1999]/vol[1-4]/part{a,b,c}.html + +You can specify any amount of URLs on the command line. They will be fetched +in a sequential manner in the specified order. + +Curl will attempt to re-use connections for multiple file transfers, so that +getting many files from the same server will not do multiple connects / +handshakes. This improves speed. Of course this is only done on files +specified on a single command line and cannot be used between separate curl +invokes. +.SH OPTIONS +.IP "-a/--append" +(FTP) When used in an FTP upload, this will tell curl to append to the target +file instead of overwriting it. If the file doesn't exist, it will be created. + +If this option is used twice, the second one will disable append mode again. +.IP "-A/--user-agent " +(HTTP) Specify the User-Agent string to send to the HTTP server. Some badly +done CGIs fail if its not set to "Mozilla/4.0". To encode blanks in the +string, surround the string with single quote marks. This can also be set +with the \fI-H/--header\fP option of course. + +If this option is set more than once, the last one will be the one that's +used. +.IP "--anyauth" +(HTTP) Tells curl to figure out authentication method by itself, and use the +most secure one the remote site claims it supports. This is done by first +doing a request and checking the response-headers, thus inducing an extra +network round-trip. This is used instead of setting a specific authentication +method, which you can do with \fI--basic\fP, \fI--digest\fP, \fI--ntlm\fP, and +\fI--negotiate\fP. (Added in 7.10.6) + +If this option is used several times, the following occurrences make no +difference. +.IP "-b/--cookie " +(HTTP) +Pass the data to the HTTP server as a cookie. It is supposedly the +data previously received from the server in a "Set-Cookie:" line. +The data should be in the format "NAME1=VALUE1; NAME2=VALUE2". + +If no '=' letter is used in the line, it is treated as a filename to use to +read previously stored cookie lines from, which should be used in this session +if they match. Using this method also activates the "cookie parser" which will +make curl record incoming cookies too, which may be handy if you're using this +in combination with the \fI-L/--location\fP option. The file format of the +file to read cookies from should be plain HTTP headers or the Netscape/Mozilla +cookie file format. + +\fBNOTE\fP that the file specified with \fI-b/--cookie\fP is only used as +input. No cookies will be stored in the file. To store cookies, use the +\fI-c/--cookie-jar\fP option or you could even save the HTTP headers to a file +using \fI-D/--dump-header\fP! + +If this option is set more than once, the last one will be the one that's +used. +.IP "-B/--use-ascii" +Use ASCII transfer when getting an FTP file or LDAP info. For FTP, this can +also be enforced by using an URL that ends with ";type=A". This option causes +data sent to stdout to be in text mode for win32 systems. + +If this option is used twice, the second one will disable ASCII usage. +.IP "--basic" +(HTTP) Tells curl to use HTTP Basic authentication. This is the default and +this option is usually pointless, unless you use it to override a previously +set option that sets a different authentication method (such as \fI--ntlm\fP, +\fI--digest\fP and \fI--negotiate\fP). (Added in 7.10.6) + +If this option is used several times, the following occurrences make no +difference. +.IP "--ciphers " +(SSL) Specifies which ciphers to use in the connection. The list of ciphers +must be using valid ciphers. Read up on SSL cipher list details on this URL: +\fIhttp://www.openssl.org/docs/apps/ciphers.html\fP + +If this option is used several times, the last one will override the others. +.IP "--compressed" +(HTTP) Request a compressed response using one of the algorithms libcurl +supports, and return the uncompressed document. If this option is used and +the server sends an unsupported encoding, Curl will report an error. + +If this option is used several times, each occurrence will toggle it on/off. +.IP "--connect-timeout " +Maximum time in seconds that you allow the connection to the server to take. +This only limits the connection phase, once curl has connected this option is +of no more use. See also the \fI-m/--max-time\fP option. + +If this option is used several times, the last one will be used. +.IP "-c/--cookie-jar " +Specify to which file you want curl to write all cookies after a completed +operation. Curl writes all cookies previously read from a specified file as +well as all cookies received from remote server(s). If no cookies are known, +no file will be written. The file will be written using the Netscape cookie +file format. If you set the file name to a single dash, "-", the cookies will +be written to stdout. + +.B NOTE +If the cookie jar can't be created or written to, the whole curl operation +won't fail or even report an error clearly. Using -v will get a warning +displayed, but that is the only visible feedback you get about this possibly +lethal situation. + +If this option is used several times, the last specfied file name will be +used. +.IP "-C/--continue-at " +Continue/Resume a previous file transfer at the given offset. The given offset +is the exact number of bytes that will be skipped counted from the beginning +of the source file before it is transfered to the destination. If used with +uploads, the ftp server command SIZE will not be used by curl. + +Use "-C -" to tell curl to automatically find out where/how to resume the +transfer. It then uses the given output/input files to figure that out. + +If this option is used several times, the last one will be used. +.IP "--create-dirs" +When used in conjunction with the -o option, curl will create the necessary +local directory hierarchy as needed. This option creates the dirs mentioned +with the -o option, nothing else. If the -o file name uses no dir or if the +dirs it mentions already exist, no dir will be created. + +To create remote directories when using FTP, try \fI--ftp-create-dirs\fP. +.IP "--crlf" +(FTP) Convert LF to CRLF in upload. Useful for MVS (OS/390). + +If this option is used twice, the second will again disable crlf converting. +.IP "-d/--data " +(HTTP) Sends the specified data in a POST request to the HTTP server, in a way +that can emulate as if a user has filled in a HTML form and pressed the submit +button. Note that the data is sent exactly as specified with no extra +processing (with all newlines cut off). The data is expected to be +\&"url-encoded". This will cause curl to pass the data to the server using the +content-type application/x-www-form-urlencoded. Compare to \fI-F/--form\fP. If +this option is used more than once on the same command line, the data pieces +specified will be merged together with a separating &-letter. Thus, using '-d +name=daniel -d skill=lousy' would generate a post chunk that looks like +\&'name=daniel&skill=lousy'. + +If you start the data with the letter @, the rest should be a file name to +read the data from, or - if you want curl to read the data from stdin. The +contents of the file must already be url-encoded. Multiple files can also be +specified. Posting data from a file named 'foobar' would thus be done with +\fI--data\fP @foobar". + +To post data purely binary, you should instead use the \fI--data-binary\fP +option. + +\fI-d/--data\fP is the same as \fI--data-ascii\fP. + +If this option is used several times, the ones following the first will +append data. +.IP "--data-ascii " +(HTTP) This is an alias for the \fI-d/--data\fP option. + +If this option is used several times, the ones following the first will +append data. +.IP "--data-binary " +(HTTP) This posts data in a similar manner as \fI--data-ascii\fP does, +although when using this option the entire context of the posted data is kept +as-is. If you want to post a binary file without the strip-newlines feature of +the \fI--data-ascii\fP option, this is for you. + +If this option is used several times, the ones following the first will +append data. +.IP "--digest" +(HTTP) Enables HTTP Digest authentication. This is a authentication that +prevents the password from being sent over the wire in clear text. Use this in +combination with the normal \fI-u/--user\fP option to set user name and +password. See also \fI--ntlm\fP, \fI--negotiate\fP and \fI--anyauth\fP for +related options. (Added in curl 7.10.6) + +If this option is used several times, the following occurrences make no +difference. +.IP "--disable-eprt" +(FTP) Tell curl to disable the use of the EPRT and LPRT commands when doing +active FTP transfers. Curl will normally always first attempt to use EPRT, +then LPRT before using PORT, but with this option, it will use PORT right +away. EPRT and LPRT are extensions to the original FTP protocol, may not work +on all servers but enable more functionality in a better way than the +traditional PORT command. (Aded in 7.10.5) + +If this option is used several times, each occurrence will toggle this on/off. +.IP "--disable-epsv" +(FTP) Tell curl to disable the use of the EPSV command when doing passive FTP +transfers. Curl will normally always first attempt to use EPSV before PASV, +but with this option, it will not try using EPSV. + +If this option is used several times, each occurrence will toggle this on/off. +.IP "-D/--dump-header " +Write the protocol headers to the specified file. + +This option is handy to use when you want to store the headers that a HTTP +site sends to you. Cookies from the headers could then be read in a second +curl invoke by using the \fI-b/--cookie\fP option! The \fI-c/--cookie-jar\fP +option is however a better way to store cookies. + +When used on FTP, the ftp server response lines are considered being "headers" +and thus are saved there. + +If this option is used several times, the last one will be used. +.IP "-e/--referer " +(HTTP) Sends the "Referer Page" information to the HTTP server. This can also +be set with the \fI-H/--header\fP flag of course. When used with +\fI-L/--location\fP you can append ";auto" to the referer URL to make curl +automatically set the previous URL when it follows a Location: header. The +";auto" string can be used alone, even if you don't set an initial referer. + +If this option is used several times, the last one will be used. +.IP "--environment" +(RISC OS ONLY) Sets a range of environment variables, using the names the -w +option supports, to easier allow extraction of useful information after having +run curl. + +If this option is used several times, each occurrence will toggle this on/off. +.IP "--egd-file " +(HTTPS) Specify the path name to the Entropy Gathering Daemon socket. The +socket is used to seed the random engine for SSL connections. See also the +\fI--random-file\fP option. +.IP "-E/--cert " +(HTTPS) +Tells curl to use the specified certificate file when getting a file +with HTTPS. The certificate must be in PEM format. +If the optional password isn't specified, it will be queried for on +the terminal. Note that this certificate is the private key and the private +certificate concatenated! + +If this option is used several times, the last one will be used. +.IP "--cert-type " +(SSL) Tells curl what certificate type the provided certificate is in. PEM, +DER and ENG are recognized types. + +If this option is used several times, the last one will be used. +.IP "--cacert " +(HTTPS) Tells curl to use the specified certificate file to verify the +peer. The file may contain multiple CA certificates. The certificate(s) must +be in PEM format. + +curl recognizes the environment variable named 'CURL_CA_BUNDLE' if that is +set, and uses the given path as a path to a CA cert bundle. This option +overrides that variable. + +The windows version of curl will automatically look for a CA certs file named +\'curl-ca-bundle.crt\', either in the same directory as curl.exe, or in the +Current Working Directory, or in any folder along your PATH. + +If this option is used several times, the last one will be used. +.IP "--capath " +(HTTPS) Tells curl to use the specified certificate directory to verify the +peer. The certificates must be in PEM format, and the directory must have been +processed using the c_rehash utility supplied with openssl. Using +\fI--capath\fP can allow curl to make https connections much more efficiently +than using \fI--cacert\fP if the \fI--cacert\fP file contains many CA +certificates. + +If this option is used several times, the last one will be used. +.IP "-f/--fail" +(HTTP) Fail silently (no output at all) on server errors. This is mostly done +like this to better enable scripts etc to better deal with failed attempts. In +normal cases when a HTTP server fails to deliver a document, it returns a HTML +document stating so (which often also describes why and more). This flag will +prevent curl from outputting that and fail silently instead. + +If this option is used twice, the second will again disable silent failure. +.IP "--ftp-create-dirs" +(FTP) When an FTP URL/operation uses a path that doesn't currently exist on +the server, the standard behavior of curl is to fail. Using this option, curl +will instead attempt to create missing directories. (Added in 7.10.7) + +If this option is used twice, the second will again disable silent failure. +.IP "--ftp-pasv" +(FTP) Use PASV when transfering. PASV is the internal default behavior, but +using this option can be used to override a previos --ftp-port option. (Added +in 7.11.0) + +If this option is used twice, the second will again disable silent failure. +.IP "--ftp-ssl" +(FTP) Make the FTP connection switch to use SSL/TLS. (Added in 7.11.0) + +If this option is used twice, the second will again disable this. +.IP "-F/--form " +(HTTP) This lets curl emulate a filled in form in which a user has pressed the +submit button. This causes curl to POST data using the Content-Type +multipart/form-data according to RFC1867. This enables uploading of binary +files etc. To force the 'content' part to be be a file, prefix the file name +with an @ sign. To just get the content part from a file, prefix the file name +with the letter <. The difference between @ and < is then that @ makes a file +get attached in the post as a file upload, while the < makes a text field and +just get the contents for that text field from a file. + +Example, to send your password file to the server, where +\&'password' is the name of the form-field to which /etc/passwd will be the +input: + +\fBcurl\fP -F password=@/etc/passwd www.mypasswords.com + +To read the file's content from stdin insted of a file, use - where the file +name should've been. This goes for both @ and < constructs. + +You can also tell curl what Content-Type to use by using 'type=', in a manner +similar to: + +\fBcurl\fP -F "web=@index.html;type=text/html" url.com + +or + +\fBcurl\fP -F "name=daniel;type=text/foo" url.com + +See further examples and details in the MANUAL. + +This option can be used multiple times. +.IP "-g/--globoff" +This option switches off the "URL globbing parser". When you set this option, +you can specify URLs that contain the letters {}[] without having them being +interpreted by curl itself. Note that these letters are not normal legal URL +contents but they should be encoded according to the URI standard. +.IP "-G/--get" +When used, this option will make all data specified with \fI-d/--data\fP or +\fI--data-binary\fP to be used in a HTTP GET request instead of the POST +request that otherwise would be used. The data will be appended to the URL +with a '?' separator. + +If used in combination with -I, the POST data will instead be appended to the +URL with a HEAD request. + +If used multiple times, nothing special happens. +.IP "-h/--help" +Usage help. +.IP "-H/--header

" +(HTTP) Extra header to use when getting a web page. You may specify any number +of extra headers. Note that if you should add a custom header that has the +same name as one of the internal ones curl would use, your externally set +header will be used instead of the internal one. This allows you to make even +trickier stuff than curl would normally do. You should not replace internally +set headers without knowing perfectly well what you're doing. Replacing an +internal header with one without content on the right side of the colon will +prevent that header from appearing. + +See also the \fI-A/--user-agent\fP and \fI-e/--referer\fP options. + +This option can be used multiple times to add/replace/remove multiple headers. +.IP "-i/--include" +(HTTP) +Include the HTTP-header in the output. The HTTP-header includes things +like server-name, date of the document, HTTP-version and more... + +If this option is used twice, the second will again disable header include. +.IP "--interface " +Perform an operation using a specified interface. You can enter interface +name, IP address or host name. An example could look like: + + curl --interface eth0:1 http://www.netscape.com/ + +If this option is used several times, the last one will be used. +.IP "-I/--head" +(HTTP/FTP/FILE) +Fetch the HTTP-header only! HTTP-servers feature the command HEAD +which this uses to get nothing but the header of a document. When used +on a FTP or FILE file, curl displays the file size and last modification +time only. + +If this option is used twice, the second will again disable header only. +.IP "-j/--junk-session-cookies" +(HTTP) When curl is told to read cookies from a given file, this option will +make it discard all "session cookies". This will basicly have the same effect +as if a new session is started. Typical browsers always discard session +cookies when they're closed down. (Added in 7.9.7) + +If this option is used several times, each occurrence will toggle this on/off. +.IP "-k/--insecure" +(SSL) This option explicitly allows curl to perform "insecure" SSL connections +and transfers. Starting with curl 7.10, all SSL connections will be attempted +to be made secure by using the CA certificate bundle installed by +default. This makes all connections considered "insecure" to fail unless +\fI-k/--insecure\fP is used. + +If this option is used twice, the second time will again disable it. +.IP "--key " +(SSL) Private key file name. Allows you to provide your private key in this +separate file. + +If this option is used several times, the last one will be used. +.IP "--key-type " +(SSL) Private key file type. Specify which type your \fI--key\fP provided +private key is. DER, PEM and ENG are supported. + +If this option is used several times, the last one will be used. +.IP "--krb4 " +(FTP) Enable kerberos4 authentication and use. The level must be entered and +should be one of 'clear', 'safe', 'confidential' or 'private'. Should you use +a level that is not one of these, 'private' will instead be used. + +This option requiures that the library was built with kerberos4 support. This +is not very common. Use \fI-V/--version\fP to see if your curl supports it. + +If this option is used several times, the last one will be used. +.IP "-K/--config " +Specify which config file to read curl arguments from. The config file is a +text file in which command line arguments can be written which then will be +used as if they were written on the actual command line. Options and their +parameters must be specified on the same config file line. If the parameter is +to contain white spaces, the parameter must be inclosed within quotes. If the +first column of a config line is a '#' character, the rest of the line will be +treated as a comment. + +Specify the filename as '-' to make curl read the file from stdin. + +Note that to be able to specify a URL in the config file, you need to specify +it using the \fI--url\fP option, and not by simply writing the URL on its own +line. So, it could look similar to this: + +url = "http://curl.haxx.se/docs/" + +This option can be used multiple times. +.IP "--limit-rate " +Specify the maximum transfer rate you want curl to use. This feature is useful +if you have a limited pipe and you'd like your transfer not use your entire +bandwidth. + +The given speed is measured in bytes/second, unless a suffix is appended. +Appending 'k' or 'K' will count the number as kilobytes, 'm' or M' makes it +megabytes while 'g' or 'G' makes it gigabytes. Examples: 200K, 3m and 1G. + +If you are also using the \fI-Y/--speed-limit\fP option, that option will take +precedence and might cripple the rate-limiting slightly, to help keeping the +speed-limit logic working. + +This option was introduced in curl 7.10. + +If this option is used several times, the last one will be used. +.IP "-l/--list-only" +(FTP) +When listing an FTP directory, this switch forces a name-only view. +Especially useful if you want to machine-parse the contents of an FTP +directory since the normal directory view doesn't use a standard look +or format. + +This option causes an FTP NLST command to be sent. Some FTP servers +list only files in their response to NLST; they do not include +subdirectories and symbolic links. + +If this option is used twice, the second will again disable list only. +.IP "-L/--location" +(HTTP/HTTPS) If the server reports that the requested page has a different +location (indicated with the header line Location:) this flag will let curl +attempt to reattempt the get on the new place. If used together with +\fI-i/--include\fP or \fI-I/--head\fP, headers from all requested pages will +be shown. If authentication is used, curl will only send its credentials to +the initial host, so if a redirect takes curl to a different host, it won't +intercept the user+password. See also \fI--location-trusted\fP on how to +change this. + +If this option is used twice, the second will again disable location following. +.IP "--location-trusted" +(HTTP/HTTPS) Like \fI-L/--location\fP, but will allow sending the name + +password to all hosts that the site may redirect to. This may or may not +introduce a security breach if the site redirects you do a site to which +you'll send your authentication info (which is plaintext in the case of HTTP +Basic authentication). + +If this option is used twice, the second will again disable location following. +.IP "--max-filesize " +Specify the maximum size (in bytes) of a file to download. If the file +requested is larger than this value, the transfer will not start and curl will +return with exit code 63. + +NOTE: The file size is not always known prior to download, and for such files +this option has no effect even if the file transfer ends up being larger than +this given limit. This concerns both FTP and HTTP transfers. +.IP "-m/--max-time " +Maximum time in seconds that you allow the whole operation to take. This is +useful for preventing your batch jobs from hanging for hours due to slow +networks or links going down. This doesn't work fully in win32 systems. See +also the \fI--connect-timeout\fP option. + +If this option is used several times, the last one will be used. +.IP "-M/--manual" +Manual. Display the huge help text. +.IP "-n/--netrc" +Makes curl scan the \fI.netrc\fP file in the user's home directory for login +name and password. This is typically used for ftp on unix. If used with http, +curl will enable user authentication. See +.BR netrc(4) +or +.BR ftp(1) +for details on the file format. Curl will not complain if that file +hasn't the right permissions (it should not be world nor group +readable). The environment variable "HOME" is used to find the home +directory. + +A quick and very simple example of how to setup a \fI.netrc\fP to allow curl +to ftp to the machine host.domain.com with user name \&'myself' and password +'secret' should look similar to: + +.B "machine host.domain.com login myself password secret" + +If this option is used twice, the second will again disable netrc usage. +.IP "--netrc-optional" +Very similar to \fI--netrc\fP, but this option makes the .netrc usage +\fBoptional\fP and not mandatory as the \fI--netrc\fP does. +.IP "--negotiate" +(HTTP) Enables GSS-Negotiate authentication. The GSS-Negotiate method was +designed by Microsoft and is used in their web aplications. It is primarily +meant as a support for Kerberos5 authentication but may be also used along +with another authentication methods. For more information see IETF draft +draft-brezak-spnego-http-04.txt. (Added in 7.10.6) + +This option requiures that the library was built with GSSAPI support. This is +not very common. Use \fI-V/--version\fP to see if your version supports +GSS-Negotiate. + +If this option is used several times, the following occurrences make no +difference. +.IP "-N/--no-buffer" +Disables the buffering of the output stream. In normal work situations, curl +will use a standard buffered output stream that will have the effect that it +will output the data in chunks, not necessarily exactly when the data arrives. +Using this option will disable that buffering. + +If this option is used twice, the second will again switch on buffering. +.IP "--ntlm" +(HTTP) Enables NTLM authentication. The NTLM authentication method was +designed by Microsoft and is used by IIS web servers. It is a proprietary +protocol, reversed engineered by clever people and implemented in curl based +on their efforts. This kind of behavior should not be endorsed, you should +encourage everyone who uses NTLM to switch to a public and documented +authentication method instead. Such as Digest. (Added in 7.10.6) + +If you want to enable NTLM for your proxy authentication, then use +\fI--proxy-ntlm\fP. + +This option requiures that the library was built with SSL support. Use +\fI-V/--version\fP to see if your curl supports NTLM. + +If this option is used several times, the following occurrences make no +difference. +.IP "-o/--output " +Write output to instead of stdout. If you are using {} or [] to fetch +multiple documents, you can use '#' followed by a number in the +specifier. That variable will be replaced with the current string for the URL +being fetched. Like in: + + curl http://{one,two}.site.com -o "file_#1.txt" + +or use several variables like: + + curl http://{site,host}.host[1-5].com -o "#1_#2" + +You may use this option as many times as you have number of URLs. + +See also the \fI--create-dirs\fP option to create the local directories +dynamically. +.IP "-O/--remote-name" +Write output to a local file named like the remote file we get. (Only the file +part of the remote file is used, the path is cut off.) + +You may use this option as many times as you have number of URLs. +.IP "--pass " +(SSL) Pass phrase for the private key + +If this option is used several times, the last one will be used. +.IP "--proxy-basic" +Tells curl to use HTTP Basic authentication when communicating with the given +proxy. Use \fI--basic\fP for enabling HTTP Basic with a remote host. Basic is +the default authentication method curl uses with proxies. + +If this option is used twice, the second will again disable proxy HTTP Basic +authentication. +.IP "--proxy-digest" +Tells curl to use HTTP Digest authentication when communicating with the given +proxy. Use \fI--digest\fP for enabling HTTP Digest with a remote host. + +If this option is used twice, the second will again disable proxy HTTP Digest. +.IP "--proxy-ntlm" +Tells curl to use HTTP NTLM authentication when communicating with the given +proxy. Use \fI--ntlm\fP for enabling NTLM with a remote host. + +If this option is used twice, the second will again disable proxy HTTP NTLM. +.IP "-p/--proxytunnel" +When an HTTP proxy is used (\fI-x/--proxy\fP), this option will cause non-HTTP +protocols to attempt to tunnel through the proxy instead of merely using it to +do HTTP-like operations. The tunnel approach is made with the HTTP proxy +CONNECT request and requires that the proxy allows direct connect to the +remote port number curl wants to tunnel through to. + +If this option is used twice, the second will again disable proxy tunnel. +.IP "-P/--ftp-port
" +(FTP) Reverses the initiator/listener roles when connecting with ftp. This +switch makes Curl use the PORT command instead of PASV. In practice, PORT +tells the server to connect to the client's specified address and port, while +PASV asks the server for an ip address and port to connect to.
+should be one of: +.RS +.IP interface +i.e "eth0" to specify which interface's IP address you want to use (Unix only) +.IP "IP address" +i.e "192.168.10.1" to specify exact IP number +.IP "host name" +i.e "my.host.domain" to specify machine +.IP "-" +(any single-letter string) to make it pick the machine's default +.RE + +If this option is used several times, the last one will be used. Disable the +use of PORT with \fI--ftp-pasv\fP. Disable the attempt to use the EPRT command +instead of PORT by using \fI--disable-eprt\fP. EPRT is really PORT++. +.IP "-q" +If used as the first parameter on the command line, the \fI$HOME/.curlrc\fP +file will not be read and used as a config file. +.IP "-Q/--quote " +(FTP) Send an arbitrary command to the remote FTP server. Quote commands are +sent BEFORE the transfer is taking place. To make commands take place after a +successful transfer, prefix them with a dash '-'. You may specify any amount +of commands to be run before and after the transfer. If the server returns +failure for one of the commands, the entire operation will be aborted. You +must send syntactically correct FTP commands as RFC959 defines. + +This option can be used multiple times. +.IP "--random-file " +(HTTPS) Specify the path name to file containing what will be considered as +random data. The data is used to seed the random engine for SSL connections. +See also the \fI--egd-file\fP option. +.IP "-r/--range " +(HTTP/FTP) +Retrieve a byte range (i.e a partial document) from a HTTP/1.1 or FTP +server. Ranges can be specified in a number of ways. +.RS +.TP 10 +.B 0-499 +specifies the first 500 bytes +.TP +.B 500-999 +specifies the second 500 bytes +.TP +.B -500 +specifies the last 500 bytes +.TP +.B 9500 +specifies the bytes from offset 9500 and forward +.TP +.B 0-0,-1 +specifies the first and last byte only(*)(H) +.TP +.B 500-700,600-799 +specifies 300 bytes from offset 500(H) +.TP +.B 100-199,500-599 +specifies two separate 100 bytes ranges(*)(H) +.RE + +(*) = NOTE that this will cause the server to reply with a multipart +response! + +You should also be aware that many HTTP/1.1 servers do not have this feature +enabled, so that when you attempt to get a range, you'll instead get the whole +document. + +FTP range downloads only support the simple syntax 'start-stop' (optionally +with one of the numbers omitted). It depends on the non-RFC command SIZE. + +If this option is used several times, the last one will be used. +.IP "-R/--remote-time" +When used, this will make libcurl attempt to figure out the timestamp of the +remote file, and if that is available make the local file get that same +timestamp. + +If this option is used twice, the second time disables this again. +.IP "-s/--silent" +Silent mode. Don't show progress meter or error messages. Makes +Curl mute. + +If this option is used twice, the second will again disable mute. +.IP "-S/--show-error" +When used with -s it makes curl show error message if it fails. + +If this option is used twice, the second will again disable show error. +.IP "--socks " +Use the specified SOCKS5 proxy. If the port number is not specified, it is +assumed at port 1080. (Option added in 7.11.1) + +This option overrides any previous use of \fI-x/--proxy\fP, as they are +mutually exclusive. + +If this option is used several times, the last one will be used. +.IP "--stderr " +Redirect all writes to stderr to the specified file instead. If the file name +is a plain '-', it is instead written to stdout. This option has no point when +you're using a shell with decent redirecting capabilities. + +If this option is used several times, the last one will be used. +.IP "--tcp-nodelay" +Turn on the TCP_NODELAY option. See the \fIcurl_easy_setopt(3)\fP man page for +details about this option. (Added in 7.11.2) + +If this option is used several times, each occurance toggles this on/off. +.IP "-t/--telnet-option " +Pass options to the telnet protocol. Supported options are: + +TTYPE= Sets the terminal type. + +XDISPLOC= Sets the X display location. + +NEW_ENV= Sets an environment variable. +.IP "-T/--upload-file " +This transfers the specified local file to the remote URL. If there is no file +part in the specified URL, Curl will append the local file name. NOTE that you +must use a trailing / on the last directory to really prove to Curl that there +is no file name or curl will think that your last directory name is the remote +file name to use. That will most likely cause the upload operation to fail. If +this is used on a http(s) server, the PUT command will be used. + +Use the file name "-" (a single dash) to use stdin instead of a given file. + +Before 7.10.8, when this option was used several times, the last one was used. + +In curl 7.10.8 and later, you can specify one -T for each URL on the command +line. Each -T + URL pair specifies what to upload and to where. curl also +supports "globbing" of the -T argument, meaning that you can upload multiple +files to a single URL by using the same URL globbing style supported in the +URL, like this: + +curl -T "{file1,file2}" http://www.uploadtothissite.com + +or even + +curl -T "img[1-1000].png" ftp://ftp.picturemania.com/upload/ +.IP "--trace " +Enables a full trace dump of all incoming and outgoing data, including +descriptive information, to the given output file. Use "-" as filename to have +the output sent to stdout. + +If this option is used several times, the last one will be used. (Added in +7.9.7) +.IP "--trace-ascii " +Enables a full trace dump of all incoming and outgoing data, including +descriptive information, to the given output file. Use "-" as filename to have +the output sent to stdout. + +This is very similar to \fI--trace\fP, but leaves out the hex part and only +shows the ASCII part of the dump. It makes smaller output that might be easier +to read for untrained humans. + +If this option is used several times, the last one will be used. (Added in +7.9.7) +.IP "-u/--user " +Specify user and password to use for server authentication. + +If this option is used several times, the last one will be used. +.IP "-U/--proxy-user " +Specify user and password to use for proxy authentication. + +If this option is used several times, the last one will be used. +.IP "--url " +Specify a URL to fetch. This option is mostly handy when you want to specify +URL(s) in a config file. + +This option may be used any number of times. To control where this URL is +written, use the \fI-o/--output\fP or the \fI-O/--remote-name\fP options. +.IP "-v/--verbose" +Makes the fetching more verbose/talkative. Mostly usable for debugging. Lines +starting with '>' means data sent by curl, '<' means data received by curl +that is hidden in normal cases and lines starting with '*' means additional +info provided by curl. + +Note that if you want to see HTTP headers in the output, \fI-i/--include\fP +might be option you're looking for. + +If you think this option still doesn't give you enough details, consider using +\fI--trace\fP or \fI--trace-ascii\fP instead. + +If this option is used twice, the second will again disable verbose. +.IP "-V/--version" +Displays information about curl and the libcurl version it uses. + +The first line includes the full version of curl, libcurl and other 3rd party +libraries linked with the executable. + +The second line (starts with "Protocols:") shows all protocols that libcurl +reports to support. + +The third line (starts with "Features:") shows specific features libcurl +reports to offer. Available features include: +.RS +.IP "IPv6" +You can use IPv6 with this. +.IP "krb4" +Krb4 for ftp is supported. +.IP "SSL" +HTTPS and FTPS are supported. +.IP "libz" +Automatic decompression of compressed files over HTTP is supported. +.IP "NTLM" +NTLM authenticaion is supported. +.IP "GSS-Negotiate" +Negotiate authenticaion is supported. +.IP "Debug" +This curl uses a libcurl built with Debug. This enables more error-tracking +and memory debugging etc. For curl-developers only! +.IP "AsynchDNS" +This curl uses asynchronous name resolves. +.IP "SPNEGO" +SPNEGO Negotiate authenticaion is supported. +.IP "Largefile" +This curl supports transfers of large files, files larger than 2GB. +.IP "IDN" +This curl supports IDN - international domain names. +.RE +.IP "-w/--write-out " +Defines what to display after a completed and successful operation. The format +is a string that may contain plain text mixed with any number of variables. The +string can be specified as "string", to get read from a particular file you +specify it "@filename" and to tell curl to read the format from stdin you +write "@-". + +The variables present in the output format will be substituted by the value or +text that curl thinks fit, as described below. All variables are specified +like %{variable_name} and to output a normal % you just write them like +%%. You can output a newline by using \\n, a carriage return with \\r and a tab +space with \\t. + +.B NOTE: +The %-letter is a special letter in the win32-environment, where all +occurrences of % must be doubled when using this option. + +Available variables are at this point: +.RS +.TP 15 +.B url_effective +The URL that was fetched last. This is mostly meaningful if you've told curl +to follow location: headers. +.TP +.B http_code +The numerical code that was found in the last retrieved HTTP(S) page. +.TP +.B time_total +The total time, in seconds, that the full operation lasted. The time will be +displayed with millisecond resolution. +.TP +.B time_namelookup +The time, in seconds, it took from the start until the name resolving was +completed. +.TP +.B time_connect +The time, in seconds, it took from the start until the connect to the remote +host (or proxy) was completed. +.TP +.B time_pretransfer +The time, in seconds, it took from the start until the file transfer is just +about to begin. This includes all pre-transfer commands and negotiations that +are specific to the particular protocol(s) involved. +.TP +.B time_starttransfer +The time, in seconds, it took from the start until the first byte is just about +to be transfered. This includes time_pretransfer and also the time the +server needs to calculate the result. +.TP +.B size_download +The total amount of bytes that were downloaded. +.TP +.B size_upload +The total amount of bytes that were uploaded. +.TP +.B size_header +The total amount of bytes of the downloaded headers. +.TP +.B size_request +The total amount of bytes that were sent in the HTTP request. +.TP +.B speed_download +The average download speed that curl measured for the complete download. +.TP +.B speed_upload +The average upload speed that curl measured for the complete upload. +.TP +.B content_type +The Content-Type of the requested document, if there was any. (Added in 7.9.5) +.RE + +If this option is used several times, the last one will be used. +.IP "-x/--proxy " +Use specified HTTP proxy. If the port number is not specified, it is assumed +at port 1080. + +This option overrides existing environment variables that sets proxy to +use. If there's an environment variable setting a proxy, you can set proxy to +\&"" to override it. + +\fBNote\fP that all operations that are performed over a HTTP proxy will +transparantly be converted to HTTP. It means that certain protocol specific +operations might not be available. This is not the case if you can tunnel +through the proxy, as done with the \fI-p/--proxytunnel\fP option. + +If this option is used several times, the last one will be used. +.IP "-X/--request " +(HTTP) +Specifies a custom request to use when communicating with the HTTP server. +The specified request will be used instead of the standard GET. Read the +HTTP 1.1 specification for details and explanations. + +(FTP) +Specifies a custom FTP command to use instead of LIST when doing file lists +with ftp. + +If this option is used several times, the last one will be used. +.IP "-y/--speed-time